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.camera2.CameraExtensionCharacteristics;
24 import android.hardware.camera2.params.OutputConfiguration;
25 import android.hardware.camera2.params.StreamConfigurationMap;
26 import android.hardware.camera2.utils.SurfaceUtils;
27 import android.media.Image;
28 import android.media.ImageWriter;
29 import android.os.Handler;
30 import android.util.Log;
31 import android.util.Size;
32 import android.view.Surface;
33 
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.concurrent.Executor;
37 import java.util.concurrent.RejectedExecutionException;
38 
39 public final class CameraExtensionUtils {
40     private static final String TAG = "CameraExtensionUtils";
41 
42     public final static int JPEG_DEFAULT_QUALITY = 100;
43     public final static int JPEG_DEFAULT_ROTATION = 0;
44 
45     public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = {
46             CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
47             ImageFormat.JPEG
48     };
49 
50     public static class SurfaceInfo {
51         public int mWidth = 0;
52         public int mHeight = 0;
53         public int mFormat = PixelFormat.RGBA_8888;
54         public long mUsage = 0;
55     }
56 
57     public static final class HandlerExecutor implements Executor {
58         private final Handler mHandler;
59 
HandlerExecutor(Handler handler)60         public HandlerExecutor(Handler handler) {
61             mHandler = handler;
62         }
63 
64         @Override
execute(Runnable runCmd)65         public void execute(Runnable runCmd) {
66             try {
67                 mHandler.post(runCmd);
68             } catch (RejectedExecutionException e) {
69                 Log.w(TAG, "Handler thread unavailable, skipping message!");
70             }
71         }
72     }
73 
querySurface(@onNull Surface s)74     public static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
75         ImageWriter writer = null;
76         Image img = null;
77         SurfaceInfo surfaceInfo = new SurfaceInfo();
78         int nativeFormat = SurfaceUtils.detectSurfaceFormat(s);
79         int dataspace = SurfaceUtils.getSurfaceDataspace(s);
80         Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
81         surfaceInfo.mFormat = nativeFormat;
82         surfaceInfo.mWidth = surfaceSize.getWidth();
83         surfaceInfo.mHeight = surfaceSize.getHeight();
84         surfaceInfo.mUsage = SurfaceUtils.getSurfaceUsage(s);
85         // Jpeg surfaces cannot be queried for their usage and other parameters
86         // in the usual way below. A buffer can only be de-queued after the
87         // producer overrides the surface dimensions to (width*height) x 1.
88         if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
89                 (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
90             surfaceInfo.mFormat = ImageFormat.JPEG;
91             return surfaceInfo;
92         }
93 
94         return surfaceInfo;
95     }
96 
getBurstCaptureSurface( @onNull List<OutputConfiguration> outputConfigs, @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes)97     public static Surface getBurstCaptureSurface(
98             @NonNull List<OutputConfiguration> outputConfigs,
99             @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
100         for (OutputConfiguration config : outputConfigs) {
101             SurfaceInfo surfaceInfo = querySurface(config.getSurface());
102             for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
103                 if (surfaceInfo.mFormat == supportedFormat) {
104                     Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
105                     if (supportedCaptureSizes.containsKey(supportedFormat)) {
106                         if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
107                             return config.getSurface();
108                         } else {
109                             throw new IllegalArgumentException("Capture size not supported!");
110                         }
111                     }
112                     return config.getSurface();
113                 }
114             }
115         }
116 
117         return null;
118     }
119 
getRepeatingRequestSurface( @onNull List<OutputConfiguration> outputConfigs, @Nullable List<Size> supportedPreviewSizes)120     public static @Nullable Surface getRepeatingRequestSurface(
121             @NonNull List<OutputConfiguration> outputConfigs,
122             @Nullable List<Size> supportedPreviewSizes) {
123         for (OutputConfiguration config : outputConfigs) {
124             SurfaceInfo surfaceInfo = querySurface(config.getSurface());
125             if ((surfaceInfo.mFormat ==
126                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
127                     // The default RGBA_8888 is also implicitly supported because camera will
128                     // internally override it to
129                     // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
130                     (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
131                 Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
132                         surfaceInfo.mHeight);
133                 if ((supportedPreviewSizes == null) ||
134                         (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
135                     throw new IllegalArgumentException("Repeating request surface size " +
136                             repeatingRequestSurfaceSize + " not supported!");
137                 }
138 
139                 return config.getSurface();
140             }
141         }
142 
143         return null;
144     }
145 }
146