/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.camera2.impl; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.ImageFormat; import android.graphics.PixelFormat; import android.hardware.HardwareBuffer; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.SurfaceUtils; import android.media.Image; import android.media.ImageWriter; import android.os.Handler; import android.util.IntArray; import android.util.Log; import android.util.Size; import android.view.Surface; import com.android.internal.camera.flags.Flags; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; public final class CameraExtensionUtils { private static final String TAG = "CameraExtensionUtils"; public final static int JPEG_DEFAULT_QUALITY = 100; public final static int JPEG_DEFAULT_ROTATION = 0; public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = { CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, ImageFormat.JPEG, ImageFormat.JPEG_R }; public static class SurfaceInfo { public int mWidth = 0; public int mHeight = 0; public int mFormat = PixelFormat.RGBA_8888; public long mUsage = 0; } public static final class HandlerExecutor implements Executor { private final Handler mHandler; public HandlerExecutor(Handler handler) { mHandler = handler; } @Override public void execute(Runnable runCmd) { try { mHandler.post(runCmd); } catch (RejectedExecutionException e) { Log.w(TAG, "Handler thread unavailable, skipping message!"); } } } public static @NonNull SurfaceInfo querySurface(@NonNull Surface s) { ImageWriter writer = null; Image img = null; SurfaceInfo surfaceInfo = new SurfaceInfo(); int nativeFormat = SurfaceUtils.detectSurfaceFormat(s); int dataspace = SurfaceUtils.getSurfaceDataspace(s); Size surfaceSize = SurfaceUtils.getSurfaceSize(s); surfaceInfo.mFormat = nativeFormat; surfaceInfo.mWidth = surfaceSize.getWidth(); surfaceInfo.mHeight = surfaceSize.getHeight(); surfaceInfo.mUsage = SurfaceUtils.getSurfaceUsage(s); // Jpeg surfaces cannot be queried for their usage and other parameters // in the usual way below. A buffer can only be de-queued after the // producer overrides the surface dimensions to (width*height) x 1. if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) && (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) { surfaceInfo.mFormat = ImageFormat.JPEG; return surfaceInfo; } else if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) && (dataspace == StreamConfigurationMap.HAL_DATASPACE_JPEG_R)) { surfaceInfo.mFormat = ImageFormat.JPEG_R; return surfaceInfo; } return surfaceInfo; } public static @Nullable Surface getPostviewSurface( @Nullable OutputConfiguration outputConfig, @NonNull HashMap> supportedPostviewSizes, @NonNull int captureFormat) { if (outputConfig == null) return null; SurfaceInfo surfaceInfo = querySurface(outputConfig.getSurface()); if (Flags.extension10Bit()) { Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight); if (supportedPostviewSizes.get(surfaceInfo.mFormat) .contains(postviewSize)) { return outputConfig.getSurface(); } else { throw new IllegalArgumentException("Postview size not supported!"); } } else { if (surfaceInfo.mFormat == captureFormat) { if (supportedPostviewSizes.containsKey(captureFormat)) { Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight); if (supportedPostviewSizes.get(surfaceInfo.mFormat) .contains(postviewSize)) { return outputConfig.getSurface(); } else { throw new IllegalArgumentException("Postview size not supported!"); } } } else { throw new IllegalArgumentException("Postview format should be equivalent to " + " the capture format!"); } } return null; } public static Surface getBurstCaptureSurface( @NonNull List outputConfigs, @NonNull HashMap> supportedCaptureSizes) { IntArray supportedCaptureOutputFormats = new IntArray(CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.length); supportedCaptureOutputFormats.addAll( CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS); if (Flags.extension10Bit()) { supportedCaptureOutputFormats.add(ImageFormat.YCBCR_P010); } for (OutputConfiguration config : outputConfigs) { SurfaceInfo surfaceInfo = querySurface(config.getSurface()); for (int supportedFormat : supportedCaptureOutputFormats.toArray()) { if (surfaceInfo.mFormat == supportedFormat) { Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight); if (supportedCaptureSizes.containsKey(supportedFormat)) { if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) { return config.getSurface(); } else { throw new IllegalArgumentException("Capture size not supported!"); } } return config.getSurface(); } } } return null; } public static @Nullable Surface getRepeatingRequestSurface( @NonNull List outputConfigs, @Nullable List supportedPreviewSizes) { for (OutputConfiguration config : outputConfigs) { SurfaceInfo surfaceInfo = querySurface(config.getSurface()); if ((surfaceInfo.mFormat == CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) || ((surfaceInfo.mUsage & HardwareBuffer.USAGE_COMPOSER_OVERLAY) != 0) || // The default RGBA_8888 is also implicitly supported because camera will // internally override it to // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT' (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) { Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight); if ((supportedPreviewSizes == null) || (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) { throw new IllegalArgumentException("Repeating request surface size " + repeatingRequestSurfaceSize + " not supported!"); } return config.getSurface(); } } return null; } public static Map getCharacteristicsMapNative( Map charsMap) { HashMap ret = new HashMap<>(); for (Map.Entry entry : charsMap.entrySet()) { ret.put(entry.getKey(), entry.getValue().getNativeMetadata()); } return ret; } }