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