• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.util.SparseIntArray;
36 import android.view.Surface;
37 
38 import com.android.internal.camera.flags.Flags;
39 
40 import java.util.Arrays;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.concurrent.Executor;
46 import java.util.concurrent.RejectedExecutionException;
47 
48 public final class CameraExtensionUtils {
49     private static final String TAG = "CameraExtensionUtils";
50 
51     public final static int JPEG_DEFAULT_QUALITY = 100;
52     public final static int JPEG_DEFAULT_ROTATION = 0;
53 
54     public static HashSet<Integer> SUPPORTED_CAPTURE_OUTPUT_FORMATS = new HashSet<>();
55 
56     static {
Arrays.asList(CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, ImageFormat.JPEG, ImageFormat.YCBCR_P010, ImageFormat.JPEG_R )57         SUPPORTED_CAPTURE_OUTPUT_FORMATS.addAll(Arrays.asList(
58                 CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, ImageFormat.JPEG,
59                 ImageFormat.YCBCR_P010, ImageFormat.JPEG_R ));
60         if (Flags.depthJpegExtensions()) {
61             SUPPORTED_CAPTURE_OUTPUT_FORMATS.add(ImageFormat.DEPTH_JPEG);
62         }
63     }
64 
65     public static class SurfaceInfo {
66         public int mWidth = 0;
67         public int mHeight = 0;
68         public int mFormat = PixelFormat.RGBA_8888;
69         public long mUsage = 0;
70     }
71 
72     public static final class HandlerExecutor implements Executor {
73         private final Handler mHandler;
74 
HandlerExecutor(Handler handler)75         public HandlerExecutor(Handler handler) {
76             mHandler = handler;
77         }
78 
79         @Override
execute(Runnable runCmd)80         public void execute(Runnable runCmd) {
81             try {
82                 mHandler.post(runCmd);
83             } catch (RejectedExecutionException e) {
84                 Log.w(TAG, "Handler thread unavailable, skipping message!");
85             }
86         }
87     }
88 
querySurface(@onNull Surface s)89     public static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
90         ImageWriter writer = null;
91         Image img = null;
92         SurfaceInfo surfaceInfo = new SurfaceInfo();
93         int nativeFormat = SurfaceUtils.detectSurfaceFormat(s);
94         int dataspace = SurfaceUtils.getSurfaceDataspace(s);
95         Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
96         surfaceInfo.mFormat = nativeFormat;
97         surfaceInfo.mWidth = surfaceSize.getWidth();
98         surfaceInfo.mHeight = surfaceSize.getHeight();
99         surfaceInfo.mUsage = SurfaceUtils.getSurfaceUsage(s);
100         // Jpeg surfaces cannot be queried for their usage and other parameters
101         // in the usual way below. A buffer can only be de-queued after the
102         // producer overrides the surface dimensions to (width*height) x 1.
103         if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
104                 (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
105             surfaceInfo.mFormat = ImageFormat.JPEG;
106             return surfaceInfo;
107         } else if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB)
108                 && (dataspace == StreamConfigurationMap.HAL_DATASPACE_JPEG_R)) {
109             surfaceInfo.mFormat = ImageFormat.JPEG_R;
110             return surfaceInfo;
111         }
112         if (Flags.depthJpegExtensions()) {
113             if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB)
114                     && (dataspace == StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH)) {
115                 surfaceInfo.mFormat = ImageFormat.DEPTH_JPEG;
116                 return surfaceInfo;
117             }
118         }
119 
120         return surfaceInfo;
121     }
122 
getPostviewSurface( @ullable OutputConfiguration outputConfig, @NonNull HashMap<Integer, List<Size>> supportedPostviewSizes, @NonNull int captureFormat)123     public static @Nullable Surface getPostviewSurface(
124             @Nullable OutputConfiguration outputConfig,
125             @NonNull HashMap<Integer, List<Size>> supportedPostviewSizes,
126             @NonNull int captureFormat) {
127         if (outputConfig == null) return null;
128 
129         SurfaceInfo surfaceInfo = querySurface(outputConfig.getSurface());
130 
131         Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
132         if (supportedPostviewSizes.get(surfaceInfo.mFormat)
133                 .contains(postviewSize)) {
134             return outputConfig.getSurface();
135         } else {
136             throw new IllegalArgumentException("Postview size not supported!");
137         }
138     }
139 
getBurstCaptureSurface( @onNull List<OutputConfiguration> outputConfigs, @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes)140     public static Surface getBurstCaptureSurface(
141             @NonNull List<OutputConfiguration> outputConfigs,
142             @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
143         Integer[] supportedCaptureOutputFormats =
144                 new Integer[CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.size()];
145         supportedCaptureOutputFormats =
146                 CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.toArray(
147                         supportedCaptureOutputFormats);
148         for (OutputConfiguration config : outputConfigs) {
149             SurfaceInfo surfaceInfo = querySurface(config.getSurface());
150             for (int supportedFormat : supportedCaptureOutputFormats) {
151                 if (surfaceInfo.mFormat == supportedFormat) {
152                     Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
153                     if (supportedCaptureSizes.containsKey(supportedFormat)) {
154                         if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
155                             return config.getSurface();
156                         } else {
157                             throw new IllegalArgumentException("Capture size not supported!");
158                         }
159                     }
160                     return config.getSurface();
161                 }
162             }
163         }
164 
165         return null;
166     }
167 
getRepeatingRequestSurface( @onNull List<OutputConfiguration> outputConfigs, @Nullable List<Size> supportedPreviewSizes)168     public static @Nullable Surface getRepeatingRequestSurface(
169             @NonNull List<OutputConfiguration> outputConfigs,
170             @Nullable List<Size> supportedPreviewSizes) {
171         for (OutputConfiguration config : outputConfigs) {
172             SurfaceInfo surfaceInfo = querySurface(config.getSurface());
173             if ((surfaceInfo.mFormat ==
174                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
175                     ((surfaceInfo.mUsage & HardwareBuffer.USAGE_COMPOSER_OVERLAY) != 0) ||
176                     // The default RGBA_8888 is also implicitly supported because camera will
177                     // internally override it to
178                     // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
179                     (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
180                 Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
181                         surfaceInfo.mHeight);
182                 if ((supportedPreviewSizes == null) ||
183                         (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
184                     throw new IllegalArgumentException("Repeating request surface size " +
185                             repeatingRequestSurfaceSize + " not supported!");
186                 }
187 
188                 return config.getSurface();
189             }
190         }
191 
192         return null;
193     }
194 
getCharacteristicsMapNative( Map<String, CameraCharacteristics> charsMap)195     public static Map<String, CameraMetadataNative> getCharacteristicsMapNative(
196             Map<String, CameraCharacteristics> charsMap) {
197         HashMap<String, CameraMetadataNative> ret = new HashMap<>();
198         for (Map.Entry<String, CameraCharacteristics> entry : charsMap.entrySet()) {
199             ret.put(entry.getKey(), entry.getValue().getNativeMetadata());
200         }
201         return ret;
202     }
203 }
204