public final class

Camera2CameraControl

extends java.lang.Object

 java.lang.Object

↳androidx.camera.camera2.interop.Camera2CameraControl

Gradle dependencies

compile group: 'androidx.camera', name: 'camera-camera2', version: '1.5.0-alpha01'

  • groupId: androidx.camera
  • artifactId: camera-camera2
  • version: 1.5.0-alpha01

Artifact androidx.camera:camera-camera2:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

An class that provides ability to interoperate with the android.hardware.camera2 APIs.

Camera2 specific controls, like capture request options, can be applied through this class. A Camera2CameraControl can be created from a general CameraControl which is associated to a camera. Then the controls will affect all use cases that are using that camera.

If any option applied by Camera2CameraControl conflicts with the options required by CameraX internally. The options from Camera2CameraControl will override, which may result in unexpected behavior depends on the options being applied.

Summary

Constructors
publicCamera2CameraControl(Camera2CameraControlImpl camera2CameraControlImpl, java.util.concurrent.Executor executor)

Creates a new camera control with Camera2 implementation.

Methods
public <any>addCaptureRequestOptions(CaptureRequestOptions bundle)

Adds a CaptureRequestOptions updates the session with the options it contains.

public voidapplyOptionsToBuilder(Camera2ImplConfig.Builder builder)

Applies the existing capture request options to a .

public <any>clearCaptureRequestOptions()

Clears all existing capture request options.

public static Camera2CameraControlfrom(CameraControl cameraControl)

Gets the Camera2CameraControl from a CameraControl.

public Camera2ImplConfiggetCamera2ImplConfig()

Gets the Camera2ImplConfig that contains the existing capture request options.

public CaptureRequestOptionsgetCaptureRequestOptions()

Gets all existing capture request options.

public voidsetActive(boolean isActive)

Sets current active state.

public <any>setCaptureRequestOptions(CaptureRequestOptions bundle)

Sets a CaptureRequestOptions and updates the session with the options it contains.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public Camera2CameraControl(Camera2CameraControlImpl camera2CameraControlImpl, java.util.concurrent.Executor executor)

Creates a new camera control with Camera2 implementation.

Parameters:

camera2CameraControlImpl: the camera control this Camera2CameraControl belongs.
executor: the camera executor used to run camera task.

Methods

public static Camera2CameraControl from(CameraControl cameraControl)

Gets the Camera2CameraControl from a CameraControl.

The CameraControl is still usable after a Camera2CameraControl is obtained from it. Note that the Camera2CameraControl has higher priority than the CameraControl. For example, if is set through the Camera2CameraControl. All CameraControl features that required internally like torch may not work properly.

Parameters:

cameraControl: The CameraControl to get from.

Returns:

The camera control with Camera2 implementation.

public <any> setCaptureRequestOptions(CaptureRequestOptions bundle)

Sets a CaptureRequestOptions and updates the session with the options it contains.

This will first clear all options that have already been set, then apply the new options.

Any values which are in conflict with values already set by CameraX, such as by CameraControl, will overwrite the existing values. The values will be submitted with every repeating and single capture requests issued by CameraX, which may result in unexpected behavior depending on the values being applied.

Parameters:

bundle: The CaptureRequestOptions which will be set.

Returns:

a which completes when the repeating shows the options have be submitted completely. The future fails with if newer options are set or camera is closed before the current request completes. Cancelling the ListenableFuture is a no-op.

public <any> addCaptureRequestOptions(CaptureRequestOptions bundle)

Adds a CaptureRequestOptions updates the session with the options it contains.

The options will be merged with the existing options. If one option is set with a different value, it will overwrite the existing value.

Any values which are in conflict with values already set by CameraX, such as by CameraControl, will overwrite the existing values. The values will be submitted with every repeating and single capture requests issued by CameraX, which may result in unexpected behavior depends on the values being applied.

Parameters:

bundle: The CaptureRequestOptions which will be set.

Returns:

a which completes when the repeating shows the options have be submitted completely. The future fails with if newer options are set or camera is closed before the current request completes.

public CaptureRequestOptions getCaptureRequestOptions()

Gets all existing capture request options.

It doesn't include the capture request options applied by the android.hardware.camera2.CameraDevice templates or by CameraX.

Returns:

The CaptureRequestOptions.

public <any> clearCaptureRequestOptions()

Clears all existing capture request options.

Returns:

a which completes when the repeating shows the options have be submitted completely. The future fails with if newer options are set or camera is closed before the current request completes.

public Camera2ImplConfig getCamera2ImplConfig()

Gets the Camera2ImplConfig that contains the existing capture request options.

public void applyOptionsToBuilder(Camera2ImplConfig.Builder builder)

Applies the existing capture request options to a .

The options is set with Config.OptionPriority.ALWAYS_OVERRIDE to ensure the parameters set by ExperimentalCamera2Interop features always override as intended.

Parameters:

builder: the builder to apply the existing capture request options.

public void setActive(boolean isActive)

Sets current active state.

When the state changes from active to inactive, the Camera2 options will be cleared. When the state changes from inactive to active, a session update will be issued if there's Camera2 options set while inactive.

Source

/*
 * Copyright 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.camera2.interop;

import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.camera2.impl.Camera2ImplConfig;
import androidx.camera.camera2.internal.Camera2CameraControlImpl;
import androidx.camera.camera2.internal.annotation.CameraExecutor;
import androidx.camera.core.CameraControl;
import androidx.camera.core.impl.CameraControlInternal;
import androidx.camera.core.impl.Config;
import androidx.camera.core.impl.annotation.ExecutedBy;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.core.util.Preconditions;

import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.Executor;

/**
 * An class that provides ability to interoperate with the {@link android.hardware.camera2} APIs.
 *
 * <p>Camera2 specific controls, like capture request options, can be applied through this class.
 * A Camera2CameraControl can be created from a general {@link CameraControl} which is associated
 * to a camera. Then the controls will affect all use cases that are using that camera.
 *
 * <p>If any option applied by Camera2CameraControl conflicts with the options required by
 * CameraX internally. The options from Camera2CameraControl will override, which may result in
 * unexpected behavior depends on the options being applied.
 */
@ExperimentalCamera2Interop
public final class Camera2CameraControl {

    private boolean mIsActive = false;
    private boolean mPendingUpdate = false;
    private final Camera2CameraControlImpl mCamera2CameraControlImpl;
    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
    @CameraExecutor
    final Executor mExecutor;
    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
    final Object mLock = new Object();
    @GuardedBy("mLock")
    private Camera2ImplConfig.Builder mBuilder = new Camera2ImplConfig.Builder();
    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
    CallbackToFutureAdapter.Completer<Void> mCompleter;

    /**
     * Creates a new camera control with Camera2 implementation.
     *
     * @param camera2CameraControlImpl the camera control this Camera2CameraControl belongs.
     * @param executor                 the camera executor used to run camera task.
     */
    @RestrictTo(Scope.LIBRARY)
    public Camera2CameraControl(@NonNull Camera2CameraControlImpl camera2CameraControlImpl,
            @NonNull @CameraExecutor Executor executor) {
        mCamera2CameraControlImpl = camera2CameraControlImpl;
        mExecutor = executor;
    }

    /**
     * Gets the {@link Camera2CameraControl} from a {@link CameraControl}.
     *
     * <p>The {@link CameraControl} is still usable after a {@link Camera2CameraControl} is
     * obtained from it. Note that the {@link Camera2CameraControl} has higher priority than the
     * {@link CameraControl}. For example, if
     * {@link android.hardware.camera2.CaptureRequest#FLASH_MODE} is set through the
     * {@link Camera2CameraControl}. All {@link CameraControl} features that required
     * {@link android.hardware.camera2.CaptureRequest#FLASH_MODE} internally like torch may not
     * work properly.
     *
     * @param cameraControl The {@link CameraControl} to get from.
     * @return The camera control with Camera2 implementation.
     * @throws IllegalArgumentException if the camera control does not contain the camera2
     *                                  information (e.g., if CameraX was not initialized with a
     *                                  {@link androidx.camera.camera2.Camera2Config}).
     */
    @NonNull
    public static Camera2CameraControl from(@NonNull CameraControl cameraControl) {
        CameraControlInternal cameraControlImpl =
                ((CameraControlInternal) cameraControl).getImplementation();
        Preconditions.checkArgument(cameraControlImpl instanceof Camera2CameraControlImpl,
                "CameraControl doesn't contain Camera2 implementation.");
        return ((Camera2CameraControlImpl) cameraControlImpl).getCamera2CameraControl();
    }

    /**
     * Sets a {@link CaptureRequestOptions} and updates the session with the options it
     * contains.
     *
     * <p>This will first clear all options that have already been set, then apply the new options.
     *
     * <p>Any values which are in conflict with values already set by CameraX, such as by
     * {@link androidx.camera.core.CameraControl}, will overwrite the existing values. The
     * values will be submitted with every repeating and single capture requests issued by
     * CameraX, which may result in unexpected behavior depending on the values being applied.
     *
     * @param bundle The {@link CaptureRequestOptions} which will be set.
     * @return a {@link ListenableFuture} which completes when the repeating
     * {@link android.hardware.camera2.CaptureResult} shows the options have be submitted
     * completely. The future fails with {@link CameraControl.OperationCanceledException} if newer
     * options are set or camera is closed before the current request completes.
     * Cancelling the ListenableFuture is a no-op.
     */
    @SuppressWarnings("AsyncSuffixFuture")
    @NonNull
    public ListenableFuture<Void> setCaptureRequestOptions(
            @NonNull CaptureRequestOptions bundle) {
        clearCaptureRequestOptionsInternal();
        addCaptureRequestOptionsInternal(bundle);

        return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(completer -> {
            mExecutor.execute(() -> updateConfig(completer));
            return "setCaptureRequestOptions";
        }));
    }

    /**
     * Adds a {@link CaptureRequestOptions} updates the session with the options it
     * contains.
     *
     * <p>The options will be merged with the existing options. If one option is set with a
     * different value, it will overwrite the existing value.
     *
     * <p>Any values which are in conflict with values already set by CameraX, such as by
     * {@link androidx.camera.core.CameraControl}, will overwrite the existing values. The
     * values will be submitted with every repeating and single capture requests issued by
     * CameraX, which may result in unexpected behavior depends on the values being applied.
     *
     * @param bundle The {@link CaptureRequestOptions} which will be set.
     * @return a {@link ListenableFuture} which completes when the repeating
     * {@link android.hardware.camera2.CaptureResult} shows the options have be submitted
     * completely. The future fails with {@link CameraControl.OperationCanceledException} if newer
     * options are set or camera is closed before the current request completes.
     */
    @SuppressWarnings("AsyncSuffixFuture")
    @NonNull
    public ListenableFuture<Void> addCaptureRequestOptions(
            @NonNull CaptureRequestOptions bundle) {
        addCaptureRequestOptionsInternal(bundle);

        return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(completer -> {
            mExecutor.execute(() -> updateConfig(completer));
            return "addCaptureRequestOptions";
        }));
    }

    /**
     * Gets all existing capture request options.
     *
     * <p>It doesn't include the capture request options applied by
     * the {@link android.hardware.camera2.CameraDevice} templates or by CameraX.
     *
     * @return The {@link CaptureRequestOptions}.
     */
    @NonNull
    public CaptureRequestOptions getCaptureRequestOptions() {
        synchronized (mLock) {
            return CaptureRequestOptions.Builder.from(mBuilder.build()).build();
        }
    }

    /**
     * Clears all existing capture request options.
     *
     * @return a {@link ListenableFuture} which completes when the repeating
     * {@link android.hardware.camera2.CaptureResult} shows the options have be submitted
     * completely. The future fails with {@link CameraControl.OperationCanceledException} if newer
     * options are set or camera is closed before the current request completes.
     */
    @SuppressWarnings("AsyncSuffixFuture")
    @NonNull
    public ListenableFuture<Void> clearCaptureRequestOptions() {
        clearCaptureRequestOptionsInternal();

        return Futures.nonCancellationPropagating(CallbackToFutureAdapter.getFuture(completer -> {
            mExecutor.execute(() -> updateConfig(completer));
            return "clearCaptureRequestOptions";
        }));
    }

    /**
     * Gets the {@link Camera2ImplConfig} that contains the existing capture request options.
     */
    @RestrictTo(Scope.LIBRARY)
    @NonNull
    public Camera2ImplConfig getCamera2ImplConfig() {
        synchronized (mLock) {
            return mBuilder.build();
        }
    }

    /**
     * Applies the existing capture request options to a {@link Camera2ImplConfig.Builder}.
     *
     * <p>The options is set with
     * {@link androidx.camera.core.impl.Config.OptionPriority#ALWAYS_OVERRIDE} to ensure the
     * parameters set by {@link ExperimentalCamera2Interop} features always override as intended.
     *
     * @param builder the builder to apply the existing capture request options.
     */
    @RestrictTo(Scope.LIBRARY)
    public void applyOptionsToBuilder(@NonNull Camera2ImplConfig.Builder builder) {
        synchronized (mLock) {
            builder.insertAllOptions(mBuilder.getMutableConfig(),
                    Config.OptionPriority.ALWAYS_OVERRIDE);
        }
    }

    private void addCaptureRequestOptionsInternal(@NonNull CaptureRequestOptions bundle) {
        synchronized (mLock) {
            mBuilder.insertAllOptions(bundle);
        }
    }

    private void clearCaptureRequestOptionsInternal() {
        synchronized (mLock) {
            mBuilder = new Camera2ImplConfig.Builder();
        }
    }

    @ExecutedBy("mExecutor")
    private void updateConfig(@NonNull CallbackToFutureAdapter.Completer<Void> completer) {
        mPendingUpdate = true;
        failInFlightUpdate(new CameraControl.OperationCanceledException(
                "Camera2CameraControl was updated with new options."));
        mCompleter = completer;
        if (mIsActive) {
            updateSession();
        }
    }

    @ExecutedBy("mExecutor")
    private void updateSession() {
        mCamera2CameraControlImpl.updateSessionConfigAsync().addListener(
                this::completeInFlightUpdate, mExecutor);
        mPendingUpdate = false;
    }

    /**
     * Sets current active state.
     *
     * <p>When the state changes from active to inactive, the Camera2 options will be cleared.
     * When the state changes from inactive to active, a session update will be issued if there's
     * Camera2 options set while inactive.
     *
     */
    @RestrictTo(Scope.LIBRARY)
    public void setActive(boolean isActive) {
        mExecutor.execute(() -> setActiveInternal(isActive));
    }

    @ExecutedBy("mExecutor")
    private void setActiveInternal(boolean isActive) {
        if (mIsActive == isActive) {
            return;
        }

        mIsActive = isActive;

        if (mIsActive) {
            if (mPendingUpdate) {
                updateSession();
            }
        } else {
            failInFlightUpdate(new CameraControl.OperationCanceledException(
                    "The camera control has became inactive."));
        }
    }

    @ExecutedBy("mExecutor")
    private void completeInFlightUpdate() {
        if (mCompleter != null) {
            mCompleter.set(null);
            mCompleter = null;
        }
    }

    @ExecutedBy("mExecutor")
    private void failInFlightUpdate(@Nullable Exception exception) {
        if (mCompleter != null) {
            mCompleter.setException(exception != null ? exception : new Exception(
                    "Camera2CameraControl failed with unknown error."));
            mCompleter = null;
        }
    }
}