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