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