java.lang.Object
↳androidx.camera.extensions.ExtensionsManager
Gradle dependencies
compile group: 'androidx.camera', name: 'camera-extensions', version: '1.5.0-alpha01'
- groupId: androidx.camera
- artifactId: camera-extensions
- version: 1.5.0-alpha01
Artifact androidx.camera:camera-extensions:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)
Overview
Provides interfaces for third party app developers to get capabilities info of extension
functions.
Many Android devices contain powerful cameras, with manufacturers devoting a lot of effort
to build many cutting-edge features, or special effects, into these camera devices.
CameraX Extensions
allows third party apps to enable the available extension
modes on the supported devices. The extension modes which might be supported via CameraX
Extensions
are ExtensionMode.BOKEH, ExtensionMode.HDR,
ExtensionMode.NIGHT, ExtensionMode.FACE_RETOUCH and ExtensionMode.AUTO
. The known supported devices are listed in the
Supported devices
page.
CameraX Extensions
are built on the top of CameraX Core
libraries
. To enable an extension mode, an ExtensionsManager instance needs to be retrieved
first with ExtensionsManager.getInstanceAsync(Context, CameraProvider). Only a single
ExtensionsManager instance can exist within a process. After retrieving the
ExtensionsManager instance, the availability of a specific extension mode can be
checked by ExtensionsManager.isExtensionAvailable(CameraSelector, int). For an available extension
mode, an extension enabled CameraSelector can be obtained by calling
ExtensionsManager.getExtensionEnabledCameraSelector(CameraSelector, int). After binding use cases by
the extension enabled CameraSelector, the extension mode will be applied to the bound
Preview and ImageCapture. The following sample code describes how to enable an
extension mode for use cases.
void bindUseCasesWithBokehMode() {
// Create a camera provider
ProcessCameraProvider cameraProvider = ... // Get the provider instance
// Call the getInstance function to retrieve a ListenableFuture object
ListenableFuture future = ExtensionsManager.getInstance(context, cameraProvider);
// Obtain the ExtensionsManager instance from the returned ListenableFuture object
future.addListener(() -> {
try {
ExtensionsManager extensionsManager = future.get()
// Query if extension is available.
if (mExtensionsManager.isExtensionAvailable(DEFAULT_BACK_CAMERA,
ExtensionMode.BOKEH)) {
// Needs to unbind all use cases before enabling different extension mode.
cameraProvider.unbindAll();
// Retrieve extension enabled camera selector
CameraSelector extensionCameraSelector;
extensionCameraSelector = extensionsManager.getExtensionEnabledCameraSelector(
DEFAULT_BACK_CAMERA, ExtensionMode.BOKEH);
// Bind image capture and preview use cases with the extension enabled camera
// selector.
ImageCapture imageCapture = new ImageCapture.Builder().build();
Preview preview = new Preview.Builder().build();
cameraProvider.bindToLifecycle(lifecycleOwner, extensionCameraSelector,
imageCapture, preview);
}
} catch (ExecutionException | InterruptedException e) {
// This should not happen unless the future is cancelled or the thread is
// interrupted by applications.
}
}, ContextCompact.getMainExecutor(context));
}
Without enabling CameraX Extensions
, any device should be able to support the
use cases combination of ImageCapture, Preview and ImageAnalysis. To
support the CameraX Extensions
functionality, the ImageCapture or
Preview might need to occupy a different format of stream. This might restrict the app
to not be able to bind ImageCapture, Preview and ImageAnalysis at the
same time if the device's hardware level is not
or above. If enabling an extension
mode is more important and the ImageAnalysis could be optional to the app design, the
extension mode can be enabled successfully when only binding ImageCapture,
Preview even if the device's hardware level is
.
While CameraX Extensions
dose not directly support
VideoCapture,
VideoCapture can still be used when any extension mode is
enabled. When the app binds VideoCapture and enables any
extension mode, VideoCapture can obtain the shared stream of
Preview and record it as a video.
For some devices, the vendor library implementation might only support a subset of the all
supported sizes retrieved by . CameraX
will select the supported sizes for the use cases according to the use cases'
configuration and combination.
Summary
Methods |
---|
public CameraExtensionsControl | getCameraExtensionsControl(CameraControl cameraControl)
Retrieves a CameraExtensionsControl object that allows customization of capture
request settings for supported camera extensions. |
public CameraExtensionsInfo | getCameraExtensionsInfo(CameraInfo cameraInfo)
Retrieves a CameraExtensionsInfo object that allows to observe or monitor capture
request settings and results for supported camera extensions. |
public <any> | getEstimatedCaptureLatencyRange(CameraSelector cameraSelector, int mode)
Returns the estimated capture latency range in milliseconds for the target camera and
extension mode. |
public CameraSelector | getExtensionEnabledCameraSelector(CameraSelector baseCameraSelector, int mode)
Returns a modified CameraSelector that will enable the specified extension mode. |
public static <any> | getInstanceAsync(Context context, CameraProvider cameraProvider)
Retrieves the ExtensionsManager associated with the current process. |
public static <any> | getInstanceAsync(Context context, CameraProvider cameraProvider, java.lang.String clientVersionStr)
Retrieves the ExtensionsManager associated with the current process and
initializes with the given client extensions-interface version. |
public boolean | isExtensionAvailable(CameraSelector baseCameraSelector, int mode)
Returns true if the particular extension mode is available for the specified
CameraSelector. |
public boolean | isImageAnalysisSupported(CameraSelector cameraSelector, int mode)
Returns whether the given extension mode supports the ImageAnalysis use case on
the camera specified by the given CameraSelector. |
public <any> | shutdown()
Shutdown the extensions. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Methods
public static <any>
getInstanceAsync(Context context,
CameraProvider cameraProvider)
Retrieves the ExtensionsManager associated with the current process.
An application must wait until the completes to get an
ExtensionsManager instance. The ExtensionsManager instance can be used to
access the extensions related functions.
Parameters:
context: The context to initialize the extensions library.
cameraProvider: A CameraProvider will be used to query the information
of cameras on the device. The CameraProvider can be the
ProcessCameraProvider
which is obtained by
ProcessCameraProvider.getInstance(Context).
public static <any>
getInstanceAsync(Context context,
CameraProvider cameraProvider, java.lang.String clientVersionStr)
Retrieves the ExtensionsManager associated with the current process and
initializes with the given client extensions-interface version.
This is for testing purpose. Since CameraX uses the latest extensions-interface
version, we need a way to emulate the earlier version to see if OEM implementation can be
compatible. For example, CameraX uses 1.3.0 and OEM implements 1.3.0 as well. We can use
this API to emulate the situation that CameraX uses 1.2.0 and invokes the older version of
API.
Parameters:
context: The context to initialize the extensions library.
cameraProvider: A CameraProvider will be used to query the information
of cameras on the device. The CameraProvider can be the
ProcessCameraProvider
which is obtained by*
ProcessCameraProvider.getInstance(Context).
clientVersionStr: the extensions-interface version used to initialize the extensions.
Shutdown the extensions.
For the moment only used for testing to shutdown the extensions. Calling this function
can deinitialize the extensions vendor library and release the created
ExtensionsManager instance. Tests should wait until the returned future is
complete. Then, tests can call the
ExtensionsManager.getInstanceAsync(Context, CameraProvider) function again to
initialize a new ExtensionsManager instance.
Returns a modified CameraSelector that will enable the specified extension mode.
The returned extension CameraSelector can be used to bind use cases to a
desired LifecycleOwner and then the specified extension mode will be enabled on
the camera.
Parameters:
baseCameraSelector: The base CameraSelector on top of which the extension
config is applied.
ExtensionsManager.isExtensionAvailable(CameraSelector, int) can be used
to check whether any camera can support the specified extension
mode for the base camera selector.
mode: The target extension mode.
Returns:
a CameraSelector for the specified Extensions mode.
public boolean
isExtensionAvailable(
CameraSelector baseCameraSelector, int mode)
Returns true if the particular extension mode is available for the specified
CameraSelector.
Note that Extensions are not supported for use with 10-bit capture output (e.g.
setting a dynamic range other than DynamicRange.SDR).
Parameters:
baseCameraSelector: The base CameraSelector to find a camera to use.
mode: The target extension mode to support.
public <any>
getEstimatedCaptureLatencyRange(
CameraSelector cameraSelector, int mode)
Returns the estimated capture latency range in milliseconds for the target camera and
extension mode.
This includes the time spent processing the multi-frame capture request along with any
additional time for encoding of the processed buffer in the framework if necessary.
Parameters:
cameraSelector: The CameraSelector to find a camera which supports the
specified extension mode.
mode: The extension mode to check.
Returns:
the range of estimated minimal and maximal capture latency in milliseconds.
Returns null if no capture latency info can be provided or if the device doesn't support
the extension mode on this camera.
public boolean
isImageAnalysisSupported(
CameraSelector cameraSelector, int mode)
Returns whether the given extension mode supports the ImageAnalysis use case on
the camera specified by the given CameraSelector. If it returns false, invoking
ProcessCameraProvider.bindToLifecycle with an ImageAnalysis use case will
throw an java.lang.IllegalArgumentException
.
Parameters:
cameraSelector: The CameraSelector to find a camera which supports the
specified extension mode.
mode: The extension mode to check.
Returns:
true if ImageAnalysis can be bound when the specified extension mode is
enabled on the camera specified by the given CameraSelector. Returns false
otherwise. If the device doesn't support this extension mode on this camera, it will also
return false.
Retrieves a CameraExtensionsControl object that allows customization of capture
request settings for supported camera extensions.
Parameters:
cameraControl: the camera control for a camera with a specific extension mode turned
on.
Returns:
a CameraExtensionsControl object to manage extension-related settings. Or
returns null if the provided CameraControl doesn't represent a camera with
enabled extensions.
Retrieves a CameraExtensionsInfo object that allows to observe or monitor capture
request settings and results for supported camera extensions.
If the provided CameraInfo doesn't represent a camera with enabled extensions, a
placeholder CameraExtensionsInfo object will be returned, indicating no extension
type and strength support.
Parameters:
cameraInfo: the camera info for a camera with a specific extension mode turned on.
Returns:
a CameraExtensionsInfo object for observing extension-specific capture
request settings and results.
Source
/*
* Copyright (C) 2019 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 androidx.camera.extensions;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.content.Context;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.util.Range;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.CameraControl;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraProvider;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.DynamicRange;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.Logger;
import androidx.camera.core.Preview;
import androidx.camera.core.impl.ExtendedCameraConfigProviderStore;
import androidx.camera.core.impl.utils.ContextUtil;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.camera.extensions.impl.InitializerImpl;
import androidx.camera.extensions.internal.ClientVersion;
import androidx.camera.extensions.internal.ExtensionVersion;
import androidx.camera.extensions.internal.Version;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.lifecycle.LifecycleOwner;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.ExecutionException;
/**
* Provides interfaces for third party app developers to get capabilities info of extension
* functions.
*
* <p>Many Android devices contain powerful cameras, with manufacturers devoting a lot of effort
* to build many cutting-edge features, or special effects, into these camera devices.
* <code>CameraX Extensions</code> allows third party apps to enable the available extension
* modes on the supported devices. The extension modes which might be supported via <code>CameraX
* Extensions</code> are {@link ExtensionMode#BOKEH}, {@link ExtensionMode#HDR},
* {@link ExtensionMode#NIGHT}, {@link ExtensionMode#FACE_RETOUCH} and {@link ExtensionMode#AUTO}
* . The known supported devices are listed in the
* <a href="https://developer.android.com/training/camera/supported-devices">Supported devices</a>
* page.
*
* <p><code>CameraX Extensions</code> are built on the top of <code>CameraX Core</code> libraries
* . To enable an extension mode, an {@link ExtensionsManager} instance needs to be retrieved
* first with {@link #getInstanceAsync(Context, CameraProvider)}. Only a single
* {@link ExtensionsManager} instance can exist within a process. After retrieving the
* {@link ExtensionsManager} instance, the availability of a specific extension mode can be
* checked by {@link #isExtensionAvailable(CameraSelector, int)}. For an available extension
* mode, an extension enabled {@link CameraSelector} can be obtained by calling
* {@link #getExtensionEnabledCameraSelector(CameraSelector, int)}. After binding use cases by
* the extension enabled {@link CameraSelector}, the extension mode will be applied to the bound
* {@link Preview} and {@link ImageCapture}. The following sample code describes how to enable an
* extension mode for use cases.
* </p>
* <pre>
* void bindUseCasesWithBokehMode() {
* // Create a camera provider
* ProcessCameraProvider cameraProvider = ... // Get the provider instance
* // Call the getInstance function to retrieve a ListenableFuture object
* ListenableFuture future = ExtensionsManager.getInstance(context, cameraProvider);
*
* // Obtain the ExtensionsManager instance from the returned ListenableFuture object
* future.addListener(() -> {
* try {
* ExtensionsManager extensionsManager = future.get()
*
* // Query if extension is available.
* if (mExtensionsManager.isExtensionAvailable(DEFAULT_BACK_CAMERA,
* ExtensionMode.BOKEH)) {
* // Needs to unbind all use cases before enabling different extension mode.
* cameraProvider.unbindAll();
*
* // Retrieve extension enabled camera selector
* CameraSelector extensionCameraSelector;
* extensionCameraSelector = extensionsManager.getExtensionEnabledCameraSelector(
* DEFAULT_BACK_CAMERA, ExtensionMode.BOKEH);
*
* // Bind image capture and preview use cases with the extension enabled camera
* // selector.
* ImageCapture imageCapture = new ImageCapture.Builder().build();
* Preview preview = new Preview.Builder().build();
* cameraProvider.bindToLifecycle(lifecycleOwner, extensionCameraSelector,
* imageCapture, preview);
* }
* } catch (ExecutionException | InterruptedException e) {
* // This should not happen unless the future is cancelled or the thread is
* // interrupted by applications.
* }
* }, ContextCompact.getMainExecutor(context));
* }
* </pre>
*
* <p>Without enabling <code>CameraX Extensions</code>, any device should be able to support the
* use cases combination of {@link ImageCapture}, {@link Preview} and {@link ImageAnalysis}. To
* support the <code>CameraX Extensions</code> functionality, the {@link ImageCapture} or
* {@link Preview} might need to occupy a different format of stream. This might restrict the app
* to not be able to bind {@link ImageCapture}, {@link Preview} and {@link ImageAnalysis} at the
* same time if the device's hardware level is not
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL} or above. If enabling an extension
* mode is more important and the {@link ImageAnalysis} could be optional to the app design, the
* extension mode can be enabled successfully when only binding {@link ImageCapture},
* {@link Preview} even if the device's hardware level is
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED}.
*
* <p>While <code>CameraX Extensions</code> dose not directly support
* {@linkplain androidx.camera.video.VideoCapture},
* {@linkplain androidx.camera.video.VideoCapture} can still be used when any extension mode is
* enabled. When the app binds {@linkplain androidx.camera.video.VideoCapture} and enables any
* extension mode, {@linkplain androidx.camera.video.VideoCapture} can obtain the shared stream of
* {@link Preview} and record it as a video.
*
* <p>For some devices, the vendor library implementation might only support a subset of the all
* supported sizes retrieved by {@link StreamConfigurationMap#getOutputSizes(int)}. <code>CameraX
* </code> will select the supported sizes for the use cases according to the use cases'
* configuration and combination.
*/
public final class ExtensionsManager {
private static final String TAG = "ExtensionsManager";
enum ExtensionsAvailability {
/**
* The device extensions library exists and has been correctly loaded.
*/
LIBRARY_AVAILABLE,
/**
* The device extensions library exists. However, there was some error loading the library.
*/
LIBRARY_UNAVAILABLE_ERROR_LOADING,
/**
* The device extensions library exists. However, the library is missing implementations.
*/
LIBRARY_UNAVAILABLE_MISSING_IMPLEMENTATION,
/**
* There are no extensions available on this device.
*/
NONE
}
// Singleton instance of the Extensions object
private static final Object EXTENSIONS_LOCK = new Object();
@GuardedBy("EXTENSIONS_LOCK")
private static ListenableFuture<ExtensionsManager> sInitializeFuture;
@GuardedBy("EXTENSIONS_LOCK")
private static ListenableFuture<Void> sDeinitializeFuture;
@GuardedBy("EXTENSIONS_LOCK")
private static ExtensionsManager sExtensionsManager;
private final ExtensionsAvailability mExtensionsAvailability;
private final ExtensionsInfo mExtensionsInfo;
/**
* Retrieves the {@link ExtensionsManager} associated with the current process.
*
* <p>An application must wait until the {@link ListenableFuture} completes to get an
* {@link ExtensionsManager} instance. The {@link ExtensionsManager} instance can be used to
* access the extensions related functions.
*
* @param context The context to initialize the extensions library.
* @param cameraProvider A {@link CameraProvider} will be used to query the information
* of cameras on the device. The {@link CameraProvider} can be the
* {@link androidx.camera.lifecycle.ProcessCameraProvider}
* which is obtained by
* {@link androidx.camera.lifecycle.ProcessCameraProvider#getInstance(Context)}.
*/
@NonNull
public static ListenableFuture<ExtensionsManager> getInstanceAsync(@NonNull Context context,
@NonNull CameraProvider cameraProvider) {
return getInstanceAsync(context, cameraProvider, ClientVersion.getCurrentVersion());
}
/**
* Retrieves the {@link ExtensionsManager} associated with the current process and
* initializes with the given client extensions-interface version.
*
* <p>This is for testing purpose. Since CameraX uses the latest extensions-interface
* version, we need a way to emulate the earlier version to see if OEM implementation can be
* compatible. For example, CameraX uses 1.3.0 and OEM implements 1.3.0 as well. We can use
* this API to emulate the situation that CameraX uses 1.2.0 and invokes the older version of
* API.
*
* @param context The context to initialize the extensions library.
* @param cameraProvider A {@link CameraProvider} will be used to query the information
* of cameras on the device. The {@link CameraProvider} can be the
* {@link androidx.camera.lifecycle.ProcessCameraProvider}
* which is obtained by*
* {@link androidx.camera.lifecycle.ProcessCameraProvider#getInstance(Context)}.
* @param clientVersionStr the extensions-interface version used to initialize the extensions.
*/
@RestrictTo(LIBRARY_GROUP)
@VisibleForTesting
@NonNull
public static ListenableFuture<ExtensionsManager> getInstanceAsync(@NonNull Context context,
@NonNull CameraProvider cameraProvider, @NonNull String clientVersionStr) {
ClientVersion clientVersion = new ClientVersion(clientVersionStr);
ClientVersion.setCurrentVersion(clientVersion);
return getInstanceAsync(context, cameraProvider, clientVersion);
}
@NonNull
static ListenableFuture<ExtensionsManager> getInstanceAsync(@NonNull Context context,
@NonNull CameraProvider cameraProvider, @NonNull ClientVersion clientVersion) {
synchronized (EXTENSIONS_LOCK) {
if (sDeinitializeFuture != null && !sDeinitializeFuture.isDone()) {
throw new IllegalStateException("Not yet done deinitializing extensions");
}
sDeinitializeFuture = null;
// Will be initialized, with an empty implementation which will report all extensions
// as unavailable
if (ExtensionVersion.getRuntimeVersion() == null) {
return Futures.immediateFuture(
getOrCreateExtensionsManager(ExtensionsAvailability.NONE, cameraProvider));
}
// Prior to 1.1 no additional initialization logic required
if (ClientVersion.isMaximumCompatibleVersion(Version.VERSION_1_0)
|| ExtensionVersion.isMaximumCompatibleVersion(Version.VERSION_1_0)) {
return Futures.immediateFuture(
getOrCreateExtensionsManager(ExtensionsAvailability.LIBRARY_AVAILABLE,
cameraProvider));
}
if (sInitializeFuture == null) {
sInitializeFuture = CallbackToFutureAdapter.getFuture(completer -> {
try {
InitializerImpl.init(clientVersion.toVersionString(),
ContextUtil.getApplicationContext(context),
new InitializerImpl.OnExtensionsInitializedCallback() {
@Override
public void onSuccess() {
Logger.d(TAG, "Successfully initialized extensions");
completer.set(getOrCreateExtensionsManager(
ExtensionsAvailability.LIBRARY_AVAILABLE,
cameraProvider));
}
@Override
public void onFailure(int error) {
Logger.e(TAG, "Failed to initialize extensions");
completer.set(getOrCreateExtensionsManager(
ExtensionsAvailability
.LIBRARY_UNAVAILABLE_ERROR_LOADING,
cameraProvider));
}
},
CameraXExecutors.directExecutor());
} catch (NoSuchMethodError | NoClassDefFoundError | AbstractMethodError e) {
Logger.e(TAG, "Failed to initialize extensions. Some classes or methods "
+ "are missed in the vendor library. " + e);
completer.set(getOrCreateExtensionsManager(
ExtensionsAvailability.LIBRARY_UNAVAILABLE_MISSING_IMPLEMENTATION,
cameraProvider));
} catch (RuntimeException e) {
// Catches all unexpected runtime exceptions and still returns an
// ExtensionsManager instance which performs default behavior.
Logger.e(TAG,
"Failed to initialize extensions. Something wents wrong when "
+ "initializing the vendor library. "
+ e);
completer.set(getOrCreateExtensionsManager(
ExtensionsAvailability.LIBRARY_UNAVAILABLE_ERROR_LOADING,
cameraProvider));
}
return "Initialize extensions";
});
}
return sInitializeFuture;
}
}
/**
* Shutdown the extensions.
*
* <p> For the moment only used for testing to shutdown the extensions. Calling this function
* can deinitialize the extensions vendor library and release the created
* {@link ExtensionsManager} instance. Tests should wait until the returned future is
* complete. Then, tests can call the
* {@link ExtensionsManager#getInstanceAsync(Context, CameraProvider)} function again to
* initialize a new {@link ExtensionsManager} instance.
*/
// TODO: Will need to be rewritten to be threadsafe with use in conjunction with
// ExtensionsManager.init(...) if this is to be released for use outside of testing.
@VisibleForTesting
@RestrictTo(LIBRARY_GROUP)
@NonNull
public ListenableFuture<Void> shutdown() {
synchronized (EXTENSIONS_LOCK) {
if (ExtensionVersion.getRuntimeVersion() == null) {
sInitializeFuture = null;
sExtensionsManager = null;
ExtensionVersion.injectInstance(null);
return Futures.immediateFuture(null);
}
// Reset the ExtensionsVersion.
ExtensionVersion.injectInstance(null);
// If initialization not yet attempted then deinit should succeed immediately.
if (sInitializeFuture == null) {
return Futures.immediateFuture(null);
}
// If already in progress of deinit then return the future
if (sDeinitializeFuture != null) {
return sDeinitializeFuture;
}
ExtensionsAvailability availability;
// Wait for the extension to be initialized before deinitializing. Block since
// this is only used for testing.
try {
sInitializeFuture.get();
sInitializeFuture = null;
availability = sExtensionsManager.mExtensionsAvailability;
sExtensionsManager = null;
} catch (ExecutionException | InterruptedException e) {
sDeinitializeFuture = Futures.immediateFailedFuture(e);
return sDeinitializeFuture;
}
// Once extension has been initialized start the deinit call
if (availability == ExtensionsAvailability.LIBRARY_AVAILABLE) {
ExtendedCameraConfigProviderStore.clear();
sDeinitializeFuture = CallbackToFutureAdapter.getFuture(completer -> {
try {
InitializerImpl.deinit(
new InitializerImpl.OnExtensionsDeinitializedCallback() {
@Override
public void onSuccess() {
completer.set(null);
}
@Override
public void onFailure(int error) {
completer.setException(new Exception("Failed to "
+ "deinitialize extensions."));
}
},
CameraXExecutors.directExecutor());
} catch (NoSuchMethodError | NoClassDefFoundError e) {
completer.setException(e);
}
return null;
});
} else {
sDeinitializeFuture = Futures.immediateFuture(null);
}
return sDeinitializeFuture;
}
}
static ExtensionsManager getOrCreateExtensionsManager(
@NonNull ExtensionsAvailability extensionsAvailability,
@NonNull CameraProvider cameraProvider) {
synchronized (EXTENSIONS_LOCK) {
if (sExtensionsManager != null) {
return sExtensionsManager;
}
sExtensionsManager = new ExtensionsManager(extensionsAvailability, cameraProvider);
return sExtensionsManager;
}
}
/**
* Returns a modified {@link CameraSelector} that will enable the specified extension mode.
*
* <p>The returned extension {@link CameraSelector} can be used to bind use cases to a
* desired {@link LifecycleOwner} and then the specified extension mode will be enabled on
* the camera.
*
* @param baseCameraSelector The base {@link CameraSelector} on top of which the extension
* config is applied.
* {@link #isExtensionAvailable(CameraSelector, int)} can be used
* to check whether any camera can support the specified extension
* mode for the base camera selector.
* @param mode The target extension mode.
* @return a {@link CameraSelector} for the specified Extensions mode.
* @throws IllegalArgumentException If this device doesn't support extensions function, no
* camera can be found to support the specified extension
* mode, or the base {@link CameraSelector} has contained
* extension related configuration in it.
*/
@NonNull
public CameraSelector getExtensionEnabledCameraSelector(
@NonNull CameraSelector baseCameraSelector, @ExtensionMode.Mode int mode) {
// Directly return the input baseCameraSelector if the target extension mode is NONE.
if (mode == ExtensionMode.NONE) {
return baseCameraSelector;
}
if (mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
throw new IllegalArgumentException("This device doesn't support extensions function! "
+ "isExtensionAvailable should be checked first before calling "
+ "getExtensionEnabledCameraSelector.");
}
return mExtensionsInfo.getExtensionCameraSelectorAndInjectCameraConfig(baseCameraSelector,
mode);
}
/**
* Returns true if the particular extension mode is available for the specified
* {@link CameraSelector}.
*
* <p> Note that Extensions are not supported for use with 10-bit capture output (e.g.
* setting a dynamic range other than {@link DynamicRange#SDR}).
*
* @param baseCameraSelector The base {@link CameraSelector} to find a camera to use.
* @param mode The target extension mode to support.
*/
public boolean isExtensionAvailable(@NonNull CameraSelector baseCameraSelector,
@ExtensionMode.Mode int mode) {
if (mode == ExtensionMode.NONE) {
return true;
}
if (mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
// Returns false if extensions are not available.
return false;
}
return mExtensionsInfo.isExtensionAvailable(baseCameraSelector, mode);
}
/**
* Returns the estimated capture latency range in milliseconds for the target camera and
* extension mode.
*
* <p>This includes the time spent processing the multi-frame capture request along with any
* additional time for encoding of the processed buffer in the framework if necessary.
*
* @param cameraSelector The {@link CameraSelector} to find a camera which supports the
* specified extension mode.
* @param mode The extension mode to check.
* @return the range of estimated minimal and maximal capture latency in milliseconds.
* Returns null if no capture latency info can be provided or if the device doesn't support
* the extension mode on this camera.
*/
@Nullable
public Range<Long> getEstimatedCaptureLatencyRange(@NonNull CameraSelector cameraSelector,
@ExtensionMode.Mode int mode) {
if (mode == ExtensionMode.NONE
|| mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
// Returns null for non-Extensions mode or if Extensions are not supported on this
// device.
return null;
}
return mExtensionsInfo.getEstimatedCaptureLatencyRange(cameraSelector, mode, null);
}
/**
* Returns whether the given extension mode supports the {@link ImageAnalysis} use case on
* the camera specified by the given {@link CameraSelector}. If it returns false, invoking
* {@code ProcessCameraProvider.bindToLifecycle} with an {@link ImageAnalysis} use case will
* throw an {@link IllegalArgumentException}.
*
* @param cameraSelector The {@link CameraSelector} to find a camera which supports the
* specified extension mode.
* @param mode The extension mode to check.
* @return true if {@link ImageAnalysis} can be bound when the specified extension mode is
* enabled on the camera specified by the given {@link CameraSelector}. Returns false
* otherwise. If the device doesn't support this extension mode on this camera, it will also
* return false.
*/
public boolean isImageAnalysisSupported(@NonNull CameraSelector cameraSelector,
@ExtensionMode.Mode int mode) {
if (mode == ExtensionMode.NONE) {
return true;
}
// Returns false if Extensions are not supported on this device.
if (mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
return false;
}
return mExtensionsInfo.isImageAnalysisSupported(cameraSelector, mode);
}
/**
* Retrieves a {@link CameraExtensionsControl} object that allows customization of capture
* request settings for supported camera extensions.
*
* @param cameraControl the camera control for a camera with a specific extension mode turned
* on.
* @return a {@link CameraExtensionsControl} object to manage extension-related settings. Or
* returns {@code null} if the provided {@link CameraControl} doesn't represent a camera with
* enabled extensions.
*/
@Nullable
public CameraExtensionsControl getCameraExtensionsControl(
@NonNull CameraControl cameraControl) {
return CameraExtensionsControls.from(cameraControl);
}
/**
* Retrieves a {@link CameraExtensionsInfo} object that allows to observe or monitor capture
* request settings and results for supported camera extensions.
*
* <p>If the provided {@link CameraInfo} doesn't represent a camera with enabled extensions, a
* placeholder {@link CameraExtensionsInfo} object will be returned, indicating no extension
* type and strength support.
*
* @param cameraInfo the camera info for a camera with a specific extension mode turned on.
* @return a {@link CameraExtensionsInfo} object for observing extension-specific capture
* request settings and results.
*/
@NonNull
public CameraExtensionsInfo getCameraExtensionsInfo(@NonNull CameraInfo cameraInfo) {
return CameraExtensionsInfos.from(cameraInfo);
}
@VisibleForTesting
@NonNull
ExtensionsAvailability getExtensionsAvailability() {
return mExtensionsAvailability;
}
@VisibleForTesting
void setVendorExtenderFactory(VendorExtenderFactory vendorExtenderFactory) {
mExtensionsInfo.setVendorExtenderFactory(vendorExtenderFactory);
}
private ExtensionsManager(@NonNull ExtensionsAvailability extensionsAvailability,
@NonNull CameraProvider cameraProvider) {
mExtensionsAvailability = extensionsAvailability;
mExtensionsInfo = new ExtensionsInfo(cameraProvider);
}
}