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