script-astra/Android/Sdk/sources/android-35/android/hardware/camera2/CameraExtensionCharacteristics.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

1556 lines
70 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2020 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;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.impl.CameraExtensionUtils;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.ExtensionKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Binder;
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
import android.util.Size;
import com.android.internal.camera.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* <p>Allows clients to query availability and supported resolutions of camera extensions.</p>
*
* <p>Camera extensions give camera clients access to device-specific algorithms and sequences that
* can improve the overall image quality of snapshots in various cases such as low light, selfies,
* portraits, and scenes that can benefit from enhanced dynamic range. Often such sophisticated
* processing sequences will rely on multiple camera frames as input and will produce a single
* output.</p>
*
* <p>Camera extensions are not guaranteed to be present on all devices so camera clients must
* query for their availability via {@link CameraExtensionCharacteristics#getSupportedExtensions()}.
* </p>
*
* <p>In order to use any available camera extension, camera clients must create a corresponding
* {@link CameraExtensionSession} via
* {@link CameraDevice#createExtensionSession(ExtensionSessionConfiguration)}</p>
*
* <p>Camera clients must be aware that device-specific camera extensions may support only a
* subset of the available camera resolutions and must first query
* {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)} for supported
* single high-quality request output sizes and
* {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, Class)} for supported
* repeating request output sizes.</p>
*
* <p>The extension characteristics for a given device are expected to remain static under
* normal operating conditions.</p>
*
* @see CameraManager#getCameraExtensionCharacteristics(String)
*/
public final class CameraExtensionCharacteristics {
private static final String TAG = "CameraExtensionCharacteristics";
/**
* Device-specific extension implementation for automatic selection of particular extension
* such as HDR or NIGHT depending on the current lighting and environment conditions.
*/
public static final int EXTENSION_AUTOMATIC = 0;
/**
* Device-specific extension implementation which tends to smooth the skin and apply other
* cosmetic effects to people's faces.
*/
public static final int EXTENSION_FACE_RETOUCH = 1;
/**
* Device-specific extension implementation which tends to smooth the skin and apply other
* cosmetic effects to people's faces.
*
* @deprecated Use {@link #EXTENSION_FACE_RETOUCH} instead.
*/
public @Deprecated static final int EXTENSION_BEAUTY = EXTENSION_FACE_RETOUCH;
/**
* Device-specific extension implementation which can blur certain regions of the final image
* thereby "enhancing" focus for all remaining non-blurred parts.
*/
public static final int EXTENSION_BOKEH = 2;
/**
* Device-specific extension implementation for enhancing the dynamic range of the
* final image.
*/
public static final int EXTENSION_HDR = 3;
/**
* Device-specific extension implementation that aims to suppress noise and improve the
* overall image quality under low light conditions.
*/
public static final int EXTENSION_NIGHT = 4;
/**
* An extension that aims to lock and stabilize a given region or object of interest.
*/
@FlaggedApi(Flags.FLAG_CONCERT_MODE_API)
public static final int EXTENSION_EYES_FREE_VIDEOGRAPHY = 5;
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {EXTENSION_AUTOMATIC,
EXTENSION_FACE_RETOUCH,
EXTENSION_BOKEH,
EXTENSION_HDR,
EXTENSION_NIGHT,
EXTENSION_EYES_FREE_VIDEOGRAPHY})
public @interface Extension {
}
/**
* Default camera output in case additional processing from CameraX extensions is not needed
*
* @hide
*/
public static final int NON_PROCESSING_INPUT_FORMAT = ImageFormat.PRIVATE;
/**
* CameraX extensions require YUV_420_888 as default input for processing at the moment
*
* @hide
*/
public static final int PROCESSING_INPUT_FORMAT = ImageFormat.YUV_420_888;
private static final @Extension
int[] EXTENSION_LIST = new int[]{
EXTENSION_AUTOMATIC,
EXTENSION_FACE_RETOUCH,
EXTENSION_BOKEH,
EXTENSION_HDR,
EXTENSION_NIGHT};
/**
* List of synthetic CameraCharacteristics keys that are supported in the extensions.
*/
private static final List<CameraCharacteristics.Key>
SUPPORTED_SYNTHETIC_CAMERA_CHARACTERISTICS =
Arrays.asList(
CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES,
CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES
);
private final Context mContext;
private final String mCameraId;
private final Map<String, CameraCharacteristics> mCharacteristicsMap;
private final Map<String, CameraMetadataNative> mCharacteristicsMapNative;
/**
* @hide
*/
public CameraExtensionCharacteristics(Context context, String cameraId,
Map<String, CameraCharacteristics> characteristicsMap) {
mContext = context;
mCameraId = cameraId;
mCharacteristicsMap = characteristicsMap;
mCharacteristicsMapNative =
CameraExtensionUtils.getCharacteristicsMapNative(characteristicsMap);
}
private static ArrayList<Size> getSupportedSizes(List<SizeList> sizesList,
Integer format) {
ArrayList<Size> ret = new ArrayList<>();
if ((sizesList != null) && (!sizesList.isEmpty())) {
for (SizeList entry : sizesList) {
if ((entry.format == format) && !entry.sizes.isEmpty()) {
for (android.hardware.camera2.extension.Size sz : entry.sizes) {
ret.add(new Size(sz.width, sz.height));
}
return ret;
}
}
}
return ret;
}
private static List<Size> generateSupportedSizes(List<SizeList> sizesList,
Integer format,
StreamConfigurationMap streamMap) {
ArrayList<Size> ret = getSupportedSizes(sizesList, format);
if (format == ImageFormat.JPEG || format == ImageFormat.YUV_420_888 ||
format == ImageFormat.PRIVATE) {
// Per API contract it is assumed that the extension is able to support all
// camera advertised sizes for JPEG, YUV_420_888 and PRIVATE in case it doesn't return
// a valid non-empty size list.
Size[] supportedSizes = streamMap.getOutputSizes(format);
if ((ret.isEmpty()) && (supportedSizes != null)) {
ret.addAll(Arrays.asList(supportedSizes));
}
}
return ret;
}
private static List<Size> generateJpegSupportedSizes(List<SizeList> sizesList,
StreamConfigurationMap streamMap) {
ArrayList<Size> extensionSizes = getSupportedSizes(sizesList, ImageFormat.YUV_420_888);
HashSet<Size> supportedSizes = extensionSizes.isEmpty() ? new HashSet<>(Arrays.asList(
streamMap.getOutputSizes(ImageFormat.YUV_420_888))) : new HashSet<>(extensionSizes);
HashSet<Size> supportedJpegSizes = new HashSet<>(Arrays.asList(streamMap.getOutputSizes(
ImageFormat.JPEG)));
supportedSizes.retainAll(supportedJpegSizes);
return new ArrayList<>(supportedSizes);
}
/**
* A per-process global camera extension manager instance, to track and
* initialize/release extensions depending on client activity.
*/
private static final class CameraExtensionManagerGlobal {
private static final String TAG = "CameraExtensionManagerGlobal";
private static final String PROXY_PACKAGE_NAME = "com.android.cameraextensions";
private static final String PROXY_SERVICE_NAME =
"com.android.cameraextensions.CameraExtensionsProxyService";
@FlaggedApi(Flags.FLAG_CONCERT_MODE)
private static final int FALLBACK_PACKAGE_NAME =
com.android.internal.R.string.config_extensionFallbackPackageName;
@FlaggedApi(Flags.FLAG_CONCERT_MODE)
private static final int FALLBACK_SERVICE_NAME =
com.android.internal.R.string.config_extensionFallbackServiceName;
// Singleton instance
private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
new CameraExtensionManagerGlobal();
private final Object mLock = new Object();
private final int PROXY_SERVICE_DELAY_MS = 2000;
private ExtensionConnectionManager mConnectionManager = new ExtensionConnectionManager();
// Singleton, don't allow construction
private CameraExtensionManagerGlobal() {}
public static CameraExtensionManagerGlobal get() {
return GLOBAL_CAMERA_MANAGER;
}
private void releaseProxyConnectionLocked(Context ctx, int extension) {
if (mConnectionManager.getConnection(extension) != null) {
ctx.unbindService(mConnectionManager.getConnection(extension));
mConnectionManager.setConnection(extension, null);
mConnectionManager.setProxy(extension, null);
mConnectionManager.resetConnectionCount(extension);
}
}
private void connectToProxyLocked(Context ctx, int extension, boolean useFallback) {
if (mConnectionManager.getConnection(extension) == null) {
Intent intent = new Intent();
intent.setClassName(PROXY_PACKAGE_NAME, PROXY_SERVICE_NAME);
String vendorProxyPackage = SystemProperties.get(
"ro.vendor.camera.extensions.package");
String vendorProxyService = SystemProperties.get(
"ro.vendor.camera.extensions.service");
if (!vendorProxyPackage.isEmpty() && !vendorProxyService.isEmpty()) {
Log.v(TAG,
"Choosing the vendor camera extensions proxy package: "
+ vendorProxyPackage);
Log.v(TAG,
"Choosing the vendor camera extensions proxy service: "
+ vendorProxyService);
intent.setClassName(vendorProxyPackage, vendorProxyService);
}
if (Flags.concertMode() && useFallback) {
String packageName = ctx.getResources().getString(FALLBACK_PACKAGE_NAME);
String serviceName = ctx.getResources().getString(FALLBACK_SERVICE_NAME);
if (!packageName.isEmpty() && !serviceName.isEmpty()) {
Log.v(TAG,
"Choosing the fallback software implementation package: "
+ packageName);
Log.v(TAG,
"Choosing the fallback software implementation service: "
+ serviceName);
intent.setClassName(packageName, serviceName);
}
}
InitializerFuture initFuture = new InitializerFuture();
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName component) {
mConnectionManager.setConnection(extension, null);
mConnectionManager.setProxy(extension, null);
}
@Override
public void onServiceConnected(ComponentName component, IBinder binder) {
ICameraExtensionsProxyService proxy =
ICameraExtensionsProxyService.Stub.asInterface(binder);
mConnectionManager.setProxy(extension, proxy);
if (mConnectionManager.getProxy(extension) == null) {
throw new IllegalStateException("Camera Proxy service is null");
}
try {
mConnectionManager.setAdvancedExtensionsSupported(extension,
mConnectionManager.getProxy(extension)
.advancedExtensionsSupported());
} catch (RemoteException e) {
Log.e(TAG, "Remote IPC failed!");
}
initFuture.setStatus(true);
}
};
ctx.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT |
Context.BIND_ABOVE_CLIENT | Context.BIND_NOT_VISIBLE,
android.os.AsyncTask.THREAD_POOL_EXECUTOR, connection);
mConnectionManager.setConnection(extension, connection);
try {
initFuture.get(PROXY_SERVICE_DELAY_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.e(TAG, "Timed out while initializing proxy service!");
}
}
}
private static class InitializerFuture implements Future<Boolean> {
private volatile Boolean mStatus;
ConditionVariable mCondVar = new ConditionVariable(/*opened*/false);
public void setStatus(boolean status) {
mStatus = status;
mCondVar.open();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false; // don't allow canceling this task
}
@Override
public boolean isCancelled() {
return false; // can never cancel this task
}
@Override
public boolean isDone() {
return mStatus != null;
}
@Override
public Boolean get() {
mCondVar.block();
return mStatus;
}
@Override
public Boolean get(long timeout, TimeUnit unit) throws TimeoutException {
long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS);
if (!mCondVar.block(timeoutMs)) {
throw new TimeoutException(
"Failed to receive status after " + timeout + " " + unit);
}
if (mStatus == null) {
throw new AssertionError();
}
return mStatus;
}
}
public boolean registerClientHelper(Context ctx, IBinder token, int extension,
boolean useFallback) {
synchronized (mLock) {
boolean ret = false;
connectToProxyLocked(ctx, extension, useFallback);
if (mConnectionManager.getProxy(extension) == null) {
return false;
}
mConnectionManager.incrementConnectionCount(extension);
try {
ret = mConnectionManager.getProxy(extension).registerClient(token);
} catch (RemoteException e) {
Log.e(TAG, "Failed to initialize extension! Extension service does "
+ " not respond!");
}
if (!ret) {
mConnectionManager.decrementConnectionCount(extension);
}
if (mConnectionManager.getConnectionCount(extension) <= 0) {
releaseProxyConnectionLocked(ctx, extension);
}
return ret;
}
}
@SuppressLint("NonUserGetterCalled")
public boolean registerClient(Context ctx, IBinder token, int extension,
String cameraId, Map<String, CameraMetadataNative> characteristicsMapNative) {
boolean ret = registerClientHelper(ctx, token, extension, false /*useFallback*/);
if (Flags.concertMode()) {
// Check if user enabled fallback impl
ContentResolver resolver = ctx.getContentResolver();
int userEnabled = Settings.Secure.getInt(resolver,
Settings.Secure.CAMERA_EXTENSIONS_FALLBACK, 1);
boolean vendorImpl = true;
if (ret && (mConnectionManager.getProxy(extension) != null) && (userEnabled == 1)) {
// At this point, we are connected to either CameraExtensionsProxyService or
// the vendor extension proxy service. If the vendor does not support the
// extension, unregisterClient and re-register client with the proxy service
// containing the fallback impl
vendorImpl = isExtensionSupported(cameraId, extension,
characteristicsMapNative);
}
if (!vendorImpl) {
unregisterClient(ctx, token, extension);
ret = registerClientHelper(ctx, token, extension, true /*useFallback*/);
}
}
return ret;
}
public void unregisterClient(Context ctx, IBinder token, int extension) {
synchronized (mLock) {
if (mConnectionManager.getProxy(extension) != null) {
try {
mConnectionManager.getProxy(extension).unregisterClient(token);
} catch (RemoteException e) {
Log.e(TAG, "Failed to de-initialize extension! Extension service does"
+ " not respond!");
} finally {
mConnectionManager.decrementConnectionCount(extension);
if (mConnectionManager.getConnectionCount(extension) <= 0) {
releaseProxyConnectionLocked(ctx, extension);
}
}
}
}
}
public void initializeSession(IInitializeSessionCallback cb, int extension)
throws RemoteException {
synchronized (mLock) {
if (mConnectionManager.getProxy(extension) != null
&& !mConnectionManager.isSessionInitialized()) {
mConnectionManager.getProxy(extension).initializeSession(cb);
mConnectionManager.setSessionInitialized(true);
} else {
cb.onFailure();
}
}
}
public void releaseSession(int extension) {
synchronized (mLock) {
if (mConnectionManager.getProxy(extension) != null) {
try {
mConnectionManager.getProxy(extension).releaseSession();
mConnectionManager.setSessionInitialized(false);
} catch (RemoteException e) {
Log.e(TAG, "Failed to release session! Extension service does"
+ " not respond!");
}
}
}
}
public boolean areAdvancedExtensionsSupported(int extension) {
return mConnectionManager.areAdvancedExtensionsSupported(extension);
}
public IPreviewExtenderImpl initializePreviewExtension(int extension)
throws RemoteException {
synchronized (mLock) {
if (mConnectionManager.getProxy(extension) != null) {
return mConnectionManager.getProxy(extension)
.initializePreviewExtension(extension);
} else {
return null;
}
}
}
public IImageCaptureExtenderImpl initializeImageExtension(int extension)
throws RemoteException {
synchronized (mLock) {
if (mConnectionManager.getProxy(extension) != null) {
return mConnectionManager.getProxy(extension)
.initializeImageExtension(extension);
} else {
return null;
}
}
}
public IAdvancedExtenderImpl initializeAdvancedExtension(int extension)
throws RemoteException {
synchronized (mLock) {
if (mConnectionManager.getProxy(extension) != null) {
return mConnectionManager.getProxy(extension)
.initializeAdvancedExtension(extension);
} else {
return null;
}
}
}
private class ExtensionConnectionManager {
// Maps extension to ExtensionConnection
private Map<Integer, ExtensionConnection> mConnections = new HashMap<>();
private boolean mSessionInitialized = false;
public ExtensionConnectionManager() {
IntArray extensionList = new IntArray(EXTENSION_LIST.length);
extensionList.addAll(EXTENSION_LIST);
if (Flags.concertModeApi()) {
extensionList.add(EXTENSION_EYES_FREE_VIDEOGRAPHY);
}
for (int extensionType : extensionList.toArray()) {
mConnections.put(extensionType, new ExtensionConnection());
}
}
public ICameraExtensionsProxyService getProxy(@Extension int extension) {
return mConnections.get(extension).mProxy;
}
public ServiceConnection getConnection(@Extension int extension) {
return mConnections.get(extension).mConnection;
}
public int getConnectionCount(@Extension int extension) {
return mConnections.get(extension).mConnectionCount;
}
public boolean areAdvancedExtensionsSupported(@Extension int extension) {
return mConnections.get(extension).mSupportsAdvancedExtensions;
}
public boolean isSessionInitialized() {
return mSessionInitialized;
}
public void setProxy(@Extension int extension, ICameraExtensionsProxyService proxy) {
mConnections.get(extension).mProxy = proxy;
}
public void setConnection(@Extension int extension, ServiceConnection connection) {
mConnections.get(extension).mConnection = connection;
}
public void incrementConnectionCount(@Extension int extension) {
mConnections.get(extension).mConnectionCount++;
}
public void decrementConnectionCount(@Extension int extension) {
mConnections.get(extension).mConnectionCount--;
}
public void resetConnectionCount(@Extension int extension) {
mConnections.get(extension).mConnectionCount = 0;
}
public void setAdvancedExtensionsSupported(@Extension int extension,
boolean advancedExtSupported) {
mConnections.get(extension).mSupportsAdvancedExtensions = advancedExtSupported;
}
public void setSessionInitialized(boolean initialized) {
mSessionInitialized = initialized;
}
private class ExtensionConnection {
public ICameraExtensionsProxyService mProxy = null;
public ServiceConnection mConnection = null;
public int mConnectionCount = 0;
public boolean mSupportsAdvancedExtensions = false;
}
}
}
/**
* @hide
*/
public static boolean registerClient(Context ctx, IBinder token, int extension,
String cameraId, Map<String, CameraMetadataNative> characteristicsMapNative) {
return CameraExtensionManagerGlobal.get().registerClient(ctx, token, extension, cameraId,
characteristicsMapNative);
}
/**
* @hide
*/
public static void unregisterClient(Context ctx, IBinder token, int extension) {
CameraExtensionManagerGlobal.get().unregisterClient(ctx, token, extension);
}
/**
* @hide
*/
public static void initializeSession(IInitializeSessionCallback cb, int extension)
throws RemoteException {
CameraExtensionManagerGlobal.get().initializeSession(cb, extension);
}
/**
* @hide
*/
public static void releaseSession(int extension) {
CameraExtensionManagerGlobal.get().releaseSession(extension);
}
/**
* @hide
*/
public static boolean areAdvancedExtensionsSupported(int extension) {
return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported(extension);
}
/**
* @hide
*/
public static boolean isExtensionSupported(String cameraId, int extensionType,
Map<String, CameraMetadataNative> characteristicsMap) {
if (areAdvancedExtensionsSupported(extensionType)) {
try {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extensionType);
return extender.isExtensionAvailable(cameraId, characteristicsMap);
} catch (RemoteException e) {
Log.e(TAG, "Failed to query extension availability! Extension service does not"
+ " respond!");
return false;
}
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
try {
extenders = initializeExtension(extensionType);
} catch (IllegalArgumentException e) {
return false;
}
try {
return extenders.first.isExtensionAvailable(cameraId,
characteristicsMap.get(cameraId))
&& extenders.second.isExtensionAvailable(cameraId,
characteristicsMap.get(cameraId));
} catch (RemoteException e) {
Log.e(TAG, "Failed to query extension availability! Extension service does not"
+ " respond!");
return false;
}
}
}
/**
* @hide
*/
public static IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) {
IAdvancedExtenderImpl extender;
try {
extender = CameraExtensionManagerGlobal.get().initializeAdvancedExtension(
extensionType);
} catch (RemoteException e) {
throw new IllegalStateException("Failed to initialize extension: " + extensionType);
}
if (extender == null) {
throw new IllegalArgumentException("Unknown extension: " + extensionType);
}
return extender;
}
/**
* @hide
*/
public static Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> initializeExtension(
@Extension int extensionType) {
IPreviewExtenderImpl previewExtender;
IImageCaptureExtenderImpl imageExtender;
try {
previewExtender =
CameraExtensionManagerGlobal.get().initializePreviewExtension(extensionType);
imageExtender =
CameraExtensionManagerGlobal.get().initializeImageExtension(extensionType);
} catch (RemoteException e) {
throw new IllegalStateException("Failed to initialize extension: " + extensionType);
}
if ((imageExtender == null) || (previewExtender == null)) {
throw new IllegalArgumentException("Unknown extension: " + extensionType);
}
return new Pair<>(previewExtender, imageExtender);
}
private static <T> boolean isOutputSupportedFor(Class<T> klass) {
Objects.requireNonNull(klass, "klass must not be null");
if ((klass == android.graphics.SurfaceTexture.class) ||
(klass == android.view.SurfaceView.class)) {
return true;
}
return false;
}
/**
* Return a list of supported device-specific extensions for a given camera device.
*
* @return non-modifiable list of available extensions
*/
public @NonNull List<Integer> getSupportedExtensions() {
ArrayList<Integer> ret = new ArrayList<>();
final IBinder token = new Binder(TAG + "#getSupportedExtensions:" + mCameraId);
IntArray extensionList = new IntArray(EXTENSION_LIST.length);
extensionList.addAll(EXTENSION_LIST);
if (Flags.concertModeApi()) {
extensionList.add(EXTENSION_EYES_FREE_VIDEOGRAPHY);
}
for (int extensionType : extensionList.toArray()) {
try {
boolean success = registerClient(mContext, token, extensionType, mCameraId,
mCharacteristicsMapNative);
if (success && isExtensionSupported(mCameraId, extensionType,
mCharacteristicsMapNative)) {
ret.add(extensionType);
}
} finally {
unregisterClient(mContext, token, extensionType);
}
}
return Collections.unmodifiableList(ret);
}
/**
* Gets an extension specific camera characteristics field value.
*
* <p>An extension can have a reduced set of camera capabilities (such as limited zoom ratio
* range, available video stabilization modes, etc). This API enables applications to query for
* an extensions specific camera characteristics. Applications are recommended to prioritize
* obtaining camera characteristics using this API when using an extension. A {@code null}
* result indicates that the extension specific characteristic is not defined or available.
*
* @param extension The extension type.
* @param key The characteristics field to read.
* @return The value of that key, or {@code null} if the field is not set.
*
* @throws IllegalArgumentException if the key is not valid or extension type is not a supported
* device-specific extension.
*/
@FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET)
public <T> @Nullable T get(@Extension int extension,
@NonNull CameraCharacteristics.Key<T> key) {
final IBinder token = new Binder(TAG + "#get:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
if (areAdvancedExtensionsSupported(extension) && getKeys(extension).contains(key)) {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
CameraMetadataNative metadata =
extender.getAvailableCharacteristicsKeyValues(mCameraId);
if (metadata == null) {
return null;
}
CameraCharacteristics characteristics = new CameraCharacteristics(metadata);
return characteristics.get(key);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension for the specified key! Extension "
+ "service does not respond!");
} finally {
unregisterClient(mContext, token, extension);
}
return null;
}
/**
* Returns the {@link CameraCharacteristics} keys that have extension-specific values.
*
* <p>An application can query the value from the key using
* {@link #get(int, CameraCharacteristics.Key)} API.
*
* @param extension The extension type.
* @return An unmodifiable set of keys that are extension specific.
*
* @throws IllegalArgumentException in case the extension type is not a
* supported device-specific extension
*/
@FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET)
public @NonNull Set<CameraCharacteristics.Key> getKeys(@Extension int extension) {
final IBinder token =
new Binder(TAG + "#getKeys:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
HashSet<CameraCharacteristics.Key> ret = new HashSet<>();
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
if (areAdvancedExtensionsSupported(extension)) {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
CameraMetadataNative metadata =
extender.getAvailableCharacteristicsKeyValues(mCameraId);
if (metadata == null) {
return Collections.emptySet();
}
int[] keys = metadata.get(
CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
if (keys == null) {
throw new AssertionError(
"android.request.availableCharacteristicsKeys must be non-null"
+ " in the characteristics");
}
CameraCharacteristics chars = new CameraCharacteristics(metadata);
Object key = CameraCharacteristics.Key.class;
Class<CameraCharacteristics.Key<?>> keyTyped =
(Class<CameraCharacteristics.Key<?>>) key;
ret.addAll(chars.getAvailableKeyList(CameraCharacteristics.class, keyTyped, keys,
/*includeSynthetic*/ false));
// Add synthetic keys to the available key list if they are part of the supported
// synthetic camera characteristic key list
for (CameraCharacteristics.Key charKey :
SUPPORTED_SYNTHETIC_CAMERA_CHARACTERISTICS) {
if (chars.get(charKey) != null) {
ret.add(charKey);
}
}
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension for all available keys! Extension "
+ "service does not respond!");
} finally {
unregisterClient(mContext, token, extension);
}
return Collections.unmodifiableSet(ret);
}
/**
* Checks for postview support of still capture.
*
* <p>A postview is a preview version of the still capture that is available before the final
* image. For example, it can be used as a temporary placeholder for the requested capture
* while the final image is being processed. The supported sizes for a still capture's postview
* can be retrieved using
* {@link CameraExtensionCharacteristics#getPostviewSupportedSizes(int, Size, int)}.</p>
*
* <p>Starting with Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
* the formats of the still capture and postview are not required to be equivalent upon capture
* request.</p>
*
* @param extension the extension type
* @return {@code true} in case postview is supported, {@code false} otherwise
*
* @throws IllegalArgumentException in case the extension type is not a
* supported device-specific extension
*/
public boolean isPostviewAvailable(@Extension int extension) {
final IBinder token = new Binder(TAG + "#isPostviewAvailable:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
if (areAdvancedExtensionsSupported(extension)) {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
return extender.isPostviewAvailable();
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
return extenders.second.isPostviewAvailable();
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension for postview availability! Extension "
+ "service does not respond!");
} finally {
unregisterClient(mContext, token, extension);
}
return false;
}
/**
* Get a list of the postview sizes supported for a still capture, using its
* capture size {@code captureSize}, to use as an output for the postview request.
*
* <p>Available postview sizes will always be either equal to or less than the still
* capture size. When choosing the most applicable postview size for a usecase, it should
* be noted that lower resolution postviews will generally be available more quickly
* than larger resolution postviews. For example, when choosing a size for an optimized
* postview that will be displayed as a placeholder while the final image is processed,
* the resolution closest to the preview size may be most suitable.</p>
*
* <p>Note that device-specific extensions are allowed to support only a subset
* of the camera resolutions advertised by
* {@link StreamConfigurationMap#getOutputSizes}.</p>
*
* @param extension the extension type
* @param captureSize size of the still capture for which the postview is requested
* @param format device-specific extension output format of the postview
* @return non-modifiable list of available sizes or an empty list if the format and
* size is not supported.
* @throws IllegalArgumentException in case of unsupported extension or if postview
* feature is not supported by extension.
*/
@NonNull
public List<Size> getPostviewSupportedSizes(@Extension int extension,
@NonNull Size captureSize, int format) {
final IBinder token = new Binder(TAG + "#getPostviewSupportedSizes:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
android.hardware.camera2.extension.Size sz =
new android.hardware.camera2.extension.Size();
sz.width = captureSize.getWidth();
sz.height = captureSize.getHeight();
StreamConfigurationMap streamMap = mCharacteristicsMap.get(mCameraId).get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (areAdvancedExtensionsSupported(extension)) {
switch(format) {
case ImageFormat.YUV_420_888:
case ImageFormat.JPEG:
case ImageFormat.JPEG_R:
case ImageFormat.YCBCR_P010:
break;
default:
throw new IllegalArgumentException("Unsupported format: " + format);
}
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
return getSupportedSizes(extender.getSupportedPostviewResolutions(sz),
format);
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
if ((extenders.second.getCaptureProcessor() == null) ||
!isPostviewAvailable(extension)) {
// Extensions that don't implement any capture processor
// and have processing occur in the HAL don't currently support the
// postview feature
throw new IllegalArgumentException("Extension does not support "
+ "postview feature");
}
if (format == ImageFormat.YUV_420_888) {
return getSupportedSizes(
extenders.second.getSupportedPostviewResolutions(sz), format);
} else if (format == ImageFormat.JPEG) {
// The framework will perform the additional encoding pass on the
// processed YUV_420 buffers.
return getSupportedSizes(
extenders.second.getSupportedPostviewResolutions(sz), format);
} else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) {
// Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the basic
// extension case
return new ArrayList<>();
} else {
throw new IllegalArgumentException("Unsupported format: " + format);
}
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension postview supported sizes! Extension "
+ "service does not respond!");
return Collections.emptyList();
} finally {
unregisterClient(mContext, token, extension);
}
}
/**
* Get a list of sizes compatible with {@code klass} to use as an output for the
* repeating request
* {@link CameraExtensionSession#setRepeatingRequest}.
*
* <p>Note that device-specific extensions are allowed to support only a subset
* of the camera output surfaces and resolutions.
* The {@link android.graphics.SurfaceTexture} class is guaranteed at least one size for
* backward compatible cameras whereas other output classes are not guaranteed to be supported.
* </p>
*
* <p>Starting with Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}
* {@link android.view.SurfaceView} classes are also guaranteed to be supported and include
* the same resolutions as {@link android.graphics.SurfaceTexture}.
* Clients must set the desired SurfaceView resolution by calling
* {@link android.view.SurfaceHolder#setFixedSize}.</p>
*
* @param extension the extension type
* @param klass a non-{@code null} {@link Class} object reference
* @return non-modifiable list of available sizes or an empty list if the Surface output is not
* supported
* @throws NullPointerException if {@code klass} was {@code null}
* @throws IllegalArgumentException in case of unsupported extension.
*/
@NonNull
public <T> List<Size> getExtensionSupportedSizes(@Extension int extension,
@NonNull Class<T> klass) {
if (!isOutputSupportedFor(klass)) {
return new ArrayList<>();
}
// TODO: Revisit this code once the Extension preview processor output format
// ambiguity is resolved in b/169799538.
final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
StreamConfigurationMap streamMap = mCharacteristicsMap.get(mCameraId).get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (areAdvancedExtensionsSupported(extension)) {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
return generateSupportedSizes(
extender.getSupportedPreviewOutputResolutions(mCameraId),
ImageFormat.PRIVATE, streamMap);
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.first.init(mCameraId,
mCharacteristicsMapNative.get(mCameraId));
return generateSupportedSizes(extenders.first.getSupportedResolutions(),
ImageFormat.PRIVATE, streamMap);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
+ " not respond!");
return new ArrayList<>();
} finally {
unregisterClient(mContext, token, extension);
}
}
/**
* Check whether a given extension is available and return the
* supported output surface resolutions that can be used for high-quality capture
* requests via {@link CameraExtensionSession#capture}.
*
* <p>Note that device-specific extensions are allowed to support only a subset
* of the camera resolutions advertised by
* {@link StreamConfigurationMap#getOutputSizes}.</p>
*
* <p>Device-specific extensions currently support at most three
* multi-frame capture surface formats. ImageFormat.JPEG will be supported by all
* extensions while ImageFormat.YUV_420_888, ImageFormat.JPEG_R, or ImageFormat.YCBCR_P010
* may or may not be supported.</p>
*
* @param extension the extension type
* @param format device-specific extension output format
* @return non-modifiable list of available sizes or an empty list if the format is not
* supported.
* @throws IllegalArgumentException in case of format different from ImageFormat.JPEG,
* ImageFormat.YUV_420_888, ImageFormat.JPEG_R,
* ImageFormat.YCBCR_P010; or unsupported extension.
*/
public @NonNull
List<Size> getExtensionSupportedSizes(@Extension int extension, int format) {
try {
final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
StreamConfigurationMap streamMap = mCharacteristicsMap.get(mCameraId).get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (areAdvancedExtensionsSupported(extension)) {
switch(format) {
case ImageFormat.YUV_420_888:
case ImageFormat.JPEG:
case ImageFormat.JPEG_R:
case ImageFormat.YCBCR_P010:
break;
default:
throw new IllegalArgumentException("Unsupported format: " + format);
}
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
return generateSupportedSizes(extender.getSupportedCaptureOutputResolutions(
mCameraId), format, streamMap);
} else {
if (format == ImageFormat.YUV_420_888) {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
if (extenders.second.getCaptureProcessor() == null) {
// Extensions that don't implement any capture processor are limited to
// JPEG only!
return new ArrayList<>();
}
return generateSupportedSizes(extenders.second.getSupportedResolutions(),
format, streamMap);
} else if (format == ImageFormat.JPEG) {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
if (extenders.second.getCaptureProcessor() != null) {
// The framework will perform the additional encoding pass on the
// processed YUV_420 buffers.
return generateJpegSupportedSizes(
extenders.second.getSupportedResolutions(), streamMap);
} else {
return generateSupportedSizes(null, format, streamMap);
}
} else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) {
// Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the
// basic extension case
return new ArrayList<>();
} else {
throw new IllegalArgumentException("Unsupported format: " + format);
}
}
} finally {
unregisterClient(mContext, token, extension);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
+ " not respond!");
return new ArrayList<>();
}
}
/**
* Returns the estimated capture latency range in milliseconds for the
* target capture resolution during the calls to {@link CameraExtensionSession#capture}. This
* includes the time spent processing the multi-frame capture request along with any additional
* time for encoding of the processed buffer if necessary.
*
* @param extension the extension type
* @param captureOutputSize size of the capture output surface. If it is not in the supported
* output sizes, maximum capture output size is used for the estimation
* @param format device-specific extension output format
* @return the range of estimated minimal and maximal capture latency in milliseconds
* or null if no capture latency info can be provided
* @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG},
* {@link ImageFormat#YUV_420_888}, {@link ImageFormat#JPEG_R}
* {@link ImageFormat#YCBCR_P010};
* or unsupported extension.
*/
public @Nullable Range<Long> getEstimatedCaptureLatencyRangeMillis(@Extension int extension,
@NonNull Size captureOutputSize, @ImageFormat.Format int format) {
switch (format) {
case ImageFormat.YUV_420_888:
case ImageFormat.JPEG:
case ImageFormat.JPEG_R:
case ImageFormat.YCBCR_P010:
//No op
break;
default:
throw new IllegalArgumentException("Unsupported format: " + format);
}
final IBinder token = new Binder(TAG + "#getEstimatedCaptureLatencyRangeMillis:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
android.hardware.camera2.extension.Size sz =
new android.hardware.camera2.extension.Size();
sz.width = captureOutputSize.getWidth();
sz.height = captureOutputSize.getHeight();
if (areAdvancedExtensionsSupported(extension)) {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId,
sz, format);
if (latencyRange != null) {
return new Range(latencyRange.min, latencyRange.max);
}
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
if ((format == ImageFormat.YUV_420_888) &&
(extenders.second.getCaptureProcessor() == null) ){
// Extensions that don't implement any capture processor are limited to
// JPEG only!
return null;
}
if ((format == ImageFormat.JPEG) &&
(extenders.second.getCaptureProcessor() != null)) {
// The framework will perform the additional encoding pass on the
// processed YUV_420 buffers. Latency in this case is very device
// specific and cannot be estimated accurately enough.
return null;
}
if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) {
// JpegR/UltraHDR + YCBCR_P010 is not supported for basic extensions
return null;
}
LatencyRange latencyRange = extenders.second.getEstimatedCaptureLatencyRange(sz);
if (latencyRange != null) {
return new Range(latencyRange.min, latencyRange.max);
}
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ " not respond!");
} finally {
unregisterClient(mContext, token, extension);
}
return null;
}
/**
* Retrieve support for capture progress callbacks via
* {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureProcessProgressed}.
*
* @param extension the extension type
* @return {@code true} in case progress callbacks are supported, {@code false} otherwise
*
* @throws IllegalArgumentException in case of an unsupported extension.
*/
public boolean isCaptureProcessProgressAvailable(@Extension int extension) {
final IBinder token = new Binder(TAG + "#isCaptureProcessProgressAvailable:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
if (areAdvancedExtensionsSupported(extension)) {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
return extender.isCaptureProcessProgressAvailable();
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
return extenders.second.isCaptureProcessProgressAvailable();
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension progress callbacks! Extension service does"
+ " not respond!");
} finally {
unregisterClient(mContext, token, extension);
}
return false;
}
/**
* Returns the set of keys supported by a {@link CaptureRequest} submitted in a
* {@link CameraExtensionSession} with a given extension type.
*
* <p>The set returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
* <p>Devices launching on Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
* or newer versions are required to support {@link CaptureRequest#CONTROL_AF_MODE},
* {@link CaptureRequest#CONTROL_AF_REGIONS}, {@link CaptureRequest#CONTROL_AF_TRIGGER},
* {@link CaptureRequest#CONTROL_ZOOM_RATIO} for
* {@link CameraExtensionCharacteristics#EXTENSION_NIGHT}.</p>
*
* @param extension the extension type
*
* @return non-modifiable set of capture keys supported by camera extension session initialized
* with the given extension type.
* @throws IllegalArgumentException in case of unsupported extension.
*/
@NonNull
public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) {
final IBinder token = new Binder(TAG + "#getAvailableCaptureRequestKeys:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
HashSet<CaptureRequest.Key> ret = new HashSet<>();
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
CameraMetadataNative captureRequestMeta = null;
if (areAdvancedExtensionsSupported(extension)) {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
captureRequestMeta = extender.getAvailableCaptureRequestKeys(mCameraId);
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.onInit(token, mCameraId,
mCharacteristicsMapNative.get(mCameraId));
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys();
extenders.second.onDeInit(token);
}
if (captureRequestMeta != null) {
int[] requestKeys = captureRequestMeta.get(
CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS);
if (requestKeys == null) {
throw new AssertionError(
"android.request.availableRequestKeys must be non-null"
+ " in the characteristics");
}
CameraCharacteristics requestChars = new CameraCharacteristics(
captureRequestMeta);
Object crKey = CaptureRequest.Key.class;
Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>) crKey;
ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped,
requestKeys, /*includeSynthetic*/ true));
}
// Jpeg quality and orientation must always be supported
if (!ret.contains(CaptureRequest.JPEG_QUALITY)) {
ret.add(CaptureRequest.JPEG_QUALITY);
}
if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) {
ret.add(CaptureRequest.JPEG_ORIENTATION);
}
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture request keys!");
} finally {
unregisterClient(mContext, token, extension);
}
return Collections.unmodifiableSet(ret);
}
/**
* Returns the set of keys supported by a {@link CaptureResult} passed as an argument to
* {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}.
*
* <p>The set returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
* <p>In case the set is empty, then the extension is not able to support any capture results
* and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}
* callback will not be fired.</p>
*
* <p>Devices launching on Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
* or newer versions are required to support {@link CaptureResult#CONTROL_AF_MODE},
* {@link CaptureResult#CONTROL_AF_REGIONS}, {@link CaptureResult#CONTROL_AF_TRIGGER},
* {@link CaptureResult#CONTROL_AF_STATE}, {@link CaptureResult#CONTROL_ZOOM_RATIO} for
* {@link CameraExtensionCharacteristics#EXTENSION_NIGHT}.</p>
*
* @param extension the extension type
*
* @return non-modifiable set of capture result keys supported by camera extension session
* initialized with the given extension type.
* @throws IllegalArgumentException in case of unsupported extension.
*/
@NonNull
public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) {
final IBinder token = new Binder(TAG + "#getAvailableCaptureResultKeys:" + mCameraId);
boolean success = registerClient(mContext, token, extension, mCameraId,
mCharacteristicsMapNative);
if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
HashSet<CaptureResult.Key> ret = new HashSet<>();
try {
if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
throw new IllegalArgumentException("Unsupported extension");
}
CameraMetadataNative captureResultMeta = null;
if (areAdvancedExtensionsSupported(extension)) {
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
extender.init(mCameraId, mCharacteristicsMapNative);
captureResultMeta = extender.getAvailableCaptureResultKeys(mCameraId);
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.onInit(token, mCameraId,
mCharacteristicsMapNative.get(mCameraId));
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
captureResultMeta = extenders.second.getAvailableCaptureResultKeys();
extenders.second.onDeInit(token);
}
if (captureResultMeta != null) {
int[] resultKeys = captureResultMeta.get(
CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS);
if (resultKeys == null) {
throw new AssertionError("android.request.availableResultKeys must be non-null "
+ "in the characteristics");
}
CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta);
Object crKey = CaptureResult.Key.class;
Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>) crKey;
ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped,
resultKeys, /*includeSynthetic*/ true));
// Jpeg quality, orientation and sensor timestamp must always be supported
if (!ret.contains(CaptureResult.JPEG_QUALITY)) {
ret.add(CaptureResult.JPEG_QUALITY);
}
if (!ret.contains(CaptureResult.JPEG_ORIENTATION)) {
ret.add(CaptureResult.JPEG_ORIENTATION);
}
if (!ret.contains(CaptureResult.SENSOR_TIMESTAMP)) {
ret.add(CaptureResult.SENSOR_TIMESTAMP);
}
}
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture result keys!");
} finally {
unregisterClient(mContext, token, extension);
}
return Collections.unmodifiableSet(ret);
}
/**
* <p>Minimum and maximum padding zoom factors supported by this camera device for
* {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for
* the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
* extension.</p>
* <p>The minimum and maximum padding zoom factors supported by the device for
* {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the
* {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
* extension feature. This extension specific camera characteristic can be queried using
* {@link android.hardware.camera2.CameraExtensionCharacteristics#get}.</p>
* <p><b>Units</b>: A pair of padding zoom factors in floating-points:
* (minPaddingZoomFactor, maxPaddingZoomFactor)</p>
* <p><b>Range of valid values:</b><br></p>
* <p>1.0 &lt; minPaddingZoomFactor &lt;= maxPaddingZoomFactor</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
*/
@PublicKey
@NonNull
@ExtensionKey
@FlaggedApi(Flags.FLAG_CONCERT_MODE_API)
public static final Key<android.util.Range<Float>> EFV_PADDING_ZOOM_FACTOR_RANGE =
CameraCharacteristics.EFV_PADDING_ZOOM_FACTOR_RANGE;
}