1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2.impl;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.graphics.ImageFormat;
22 import android.graphics.PixelFormat;
23 import android.hardware.HardwareBuffer;
24 import android.hardware.camera2.CameraCharacteristics;
25 import android.hardware.camera2.CameraExtensionCharacteristics;
26 import android.hardware.camera2.params.OutputConfiguration;
27 import android.hardware.camera2.params.StreamConfigurationMap;
28 import android.hardware.camera2.utils.SurfaceUtils;
29 import android.media.Image;
30 import android.media.ImageWriter;
31 import android.os.Handler;
32 import android.util.IntArray;
33 import android.util.Log;
34 import android.util.Size;
35 import android.view.Surface;
36 
37 import com.android.internal.camera.flags.Flags;
38 
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.concurrent.Executor;
43 import java.util.concurrent.RejectedExecutionException;
44 
45 public final class CameraExtensionUtils {
46     private static final String TAG = "CameraExtensionUtils";
47 
48     public final static int JPEG_DEFAULT_QUALITY = 100;
49     public final static int JPEG_DEFAULT_ROTATION = 0;
50 
51     public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = {
52             CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
53             ImageFormat.JPEG,
54             ImageFormat.JPEG_R
55     };
56 
57     public static class SurfaceInfo {
58         public int mWidth = 0;
59         public int mHeight = 0;
60         public int mFormat = PixelFormat.RGBA_8888;
61         public long mUsage = 0;
62     }
63 
64     public static final class HandlerExecutor implements Executor {
65         private final Handler mHandler;
66 
HandlerExecutor(Handler handler)67         public HandlerExecutor(Handler handler) {
68             mHandler = handler;
69         }
70 
71         @Override
execute(Runnable runCmd)72         public void execute(Runnable runCmd) {
73             try {
74                 mHandler.post(runCmd);
75             } catch (RejectedExecutionException e) {
76                 Log.w(TAG, "Handler thread unavailable, skipping message!");
77             }
78         }
79     }
80 
querySurface(@onNull Surface s)81     public static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
82         ImageWriter writer = null;
83         Image img = null;
84         SurfaceInfo surfaceInfo = new SurfaceInfo();
85         int nativeFormat = SurfaceUtils.detectSurfaceFormat(s);
86         int dataspace = SurfaceUtils.getSurfaceDataspace(s);
87         Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
88         surfaceInfo.mFormat = nativeFormat;
89         surfaceInfo.mWidth = surfaceSize.getWidth();
90         surfaceInfo.mHeight = surfaceSize.getHeight();
91         surfaceInfo.mUsage = SurfaceUtils.getSurfaceUsage(s);
92         // Jpeg surfaces cannot be queried for their usage and other parameters
93         // in the usual way below. A buffer can only be de-queued after the
94         // producer overrides the surface dimensions to (width*height) x 1.
95         if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
96                 (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
97             surfaceInfo.mFormat = ImageFormat.JPEG;
98             return surfaceInfo;
99         } else if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB)
100                 && (dataspace == StreamConfigurationMap.HAL_DATASPACE_JPEG_R)) {
101             surfaceInfo.mFormat = ImageFormat.JPEG_R;
102             return surfaceInfo;
103         }
104 
105         return surfaceInfo;
106     }
107 
getPostviewSurface( @ullable OutputConfiguration outputConfig, @NonNull HashMap<Integer, List<Size>> supportedPostviewSizes, @NonNull int captureFormat)108     public static @Nullable Surface getPostviewSurface(
109             @Nullable OutputConfiguration outputConfig,
110             @NonNull HashMap<Integer, List<Size>> supportedPostviewSizes,
111             @NonNull int captureFormat) {
112         if (outputConfig == null) return null;
113 
114         SurfaceInfo surfaceInfo = querySurface(outputConfig.getSurface());
115 
116         if (Flags.extension10Bit()) {
117             Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
118             if (supportedPostviewSizes.get(surfaceInfo.mFormat)
119                     .contains(postviewSize)) {
120                 return outputConfig.getSurface();
121             } else {
122                 throw new IllegalArgumentException("Postview size not supported!");
123             }
124         } else {
125             if (surfaceInfo.mFormat == captureFormat) {
126                 if (supportedPostviewSizes.containsKey(captureFormat)) {
127                     Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
128                     if (supportedPostviewSizes.get(surfaceInfo.mFormat)
129                             .contains(postviewSize)) {
130                         return outputConfig.getSurface();
131                     } else {
132                         throw new IllegalArgumentException("Postview size not supported!");
133                     }
134                 }
135             } else {
136                 throw new IllegalArgumentException("Postview format should be equivalent to "
137                         + " the capture format!");
138             }
139         }
140 
141         return null;
142     }
143 
getBurstCaptureSurface( @onNull List<OutputConfiguration> outputConfigs, @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes)144     public static Surface getBurstCaptureSurface(
145             @NonNull List<OutputConfiguration> outputConfigs,
146             @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
147         IntArray supportedCaptureOutputFormats =
148                 new IntArray(CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.length);
149         supportedCaptureOutputFormats.addAll(
150                 CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS);
151         if (Flags.extension10Bit()) {
152             supportedCaptureOutputFormats.add(ImageFormat.YCBCR_P010);
153         }
154         for (OutputConfiguration config : outputConfigs) {
155             SurfaceInfo surfaceInfo = querySurface(config.getSurface());
156             for (int supportedFormat : supportedCaptureOutputFormats.toArray()) {
157                 if (surfaceInfo.mFormat == supportedFormat) {
158                     Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
159                     if (supportedCaptureSizes.containsKey(supportedFormat)) {
160                         if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
161                             return config.getSurface();
162                         } else {
163                             throw new IllegalArgumentException("Capture size not supported!");
164                         }
165                     }
166                     return config.getSurface();
167                 }
168             }
169         }
170 
171         return null;
172     }
173 
getRepeatingRequestSurface( @onNull List<OutputConfiguration> outputConfigs, @Nullable List<Size> supportedPreviewSizes)174     public static @Nullable Surface getRepeatingRequestSurface(
175             @NonNull List<OutputConfiguration> outputConfigs,
176             @Nullable List<Size> supportedPreviewSizes) {
177         for (OutputConfiguration config : outputConfigs) {
178             SurfaceInfo surfaceInfo = querySurface(config.getSurface());
179             if ((surfaceInfo.mFormat ==
180                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
181                     ((surfaceInfo.mUsage & HardwareBuffer.USAGE_COMPOSER_OVERLAY) != 0) ||
182                     // The default RGBA_8888 is also implicitly supported because camera will
183                     // internally override it to
184                     // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
185                     (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
186                 Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
187                         surfaceInfo.mHeight);
188                 if ((supportedPreviewSizes == null) ||
189                         (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
190                     throw new IllegalArgumentException("Repeating request surface size " +
191                             repeatingRequestSurfaceSize + " not supported!");
192                 }
193 
194                 return config.getSurface();
195             }
196         }
197 
198         return null;
199     }
200 
getCharacteristicsMapNative( Map<String, CameraCharacteristics> charsMap)201     public static Map<String, CameraMetadataNative> getCharacteristicsMapNative(
202             Map<String, CameraCharacteristics> charsMap) {
203         HashMap<String, CameraMetadataNative> ret = new HashMap<>();
204         for (Map.Entry<String, CameraCharacteristics> entry : charsMap.entrySet()) {
205             ret.put(entry.getKey(), entry.getValue().getNativeMetadata());
206         }
207         return ret;
208     }
209 }
210