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.2.0-alpha01'

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

Artifact androidx.camera:camera-camera2:1.2.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

Fields
public static final java.lang.StringTAG_KEY

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 <any>clearCaptureRequestOptions()

Clears all capture request options that is currently applied by the Camera2CameraControl.

public static Camera2CameraControlfrom(CameraControl cameraControl)

Gets the Camera2CameraControl from a CameraControl.

public Camera2ImplConfiggetCamera2ImplConfig()

Gets the Camera2ImplConfig that is currently applied by the Camera2CameraControl.

public Camera2CameraControlImpl.CaptureResultListenergetCaptureRequestListener()

public CaptureRequestOptionsgetCaptureRequestOptions()

Gets all the capture request options that is currently applied by the Camera2CameraControl.

public voidsetActive(boolean isActive)

Set 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

Fields

public static final java.lang.String TAG_KEY

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 Camera2CameraControlImpl.CaptureResultListener getCaptureRequestListener()

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 the capture request options that is currently applied by the Camera2CameraControl.

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 capture request options that is currently applied by the Camera2CameraControl.

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 is currently applied by the Camera2CameraControl.

public void setActive(boolean isActive)

Set 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.RequiresApi;
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.Config;
import androidx.camera.core.impl.TagBundle;
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
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public final class Camera2CameraControl {

    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    public static final String TAG_KEY = "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;
    private final Camera2CameraControlImpl.CaptureResultListener mCaptureResultListener =
            (captureResult) -> {
                CallbackToFutureAdapter.Completer<Void> completerToSet = null;
                if (mCompleter != null) {
                    Object tag = captureResult.getRequest().getTag();
                    if (tag instanceof TagBundle) {
                        TagBundle tagBundle = (TagBundle) tag;
                        Integer tagInteger = (Integer) tagBundle.getTag(TAG_KEY);
                        if (tagInteger != null && tagInteger.equals(mCompleter.hashCode())) {
                            completerToSet = mCompleter;
                            mCompleter = null;
                        }
                    }
                }
                if (completerToSet != null) {
                    completerToSet.set(null);
                }
                // Return false to keep getting captureResult.
                return false;
            };

    /**
     * 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.
     * @hide
     */
    @RestrictTo(Scope.LIBRARY)
    public Camera2CameraControl(@NonNull Camera2CameraControlImpl camera2CameraControlImpl,
            @NonNull @CameraExecutor Executor executor) {
        mCamera2CameraControlImpl = camera2CameraControlImpl;
        mExecutor = executor;
    }

    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    @NonNull
    public Camera2CameraControlImpl.CaptureResultListener getCaptureRequestListener() {
        return mCaptureResultListener;
    }

    /**
     * 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) {
        Preconditions.checkArgument(cameraControl instanceof Camera2CameraControlImpl,
                "CameraControl doesn't contain Camera2 implementation.");
        return ((Camera2CameraControlImpl) cameraControl).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 the capture request options that is currently applied by the
     * {@link Camera2CameraControl}.
     *
     * <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 capture request options that is currently applied by the
     * {@link Camera2CameraControl}.
     *
     * @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 is currently applied by the
     * {@link Camera2CameraControl}.
     *
     * @hide
     */
    @RestrictTo(Scope.LIBRARY)
    @NonNull
    public Camera2ImplConfig getCamera2ImplConfig() {
        synchronized (mLock) {
            if (mCompleter != null) {
                mBuilder.getMutableConfig().insertOption(
                        Camera2ImplConfig.CAPTURE_REQUEST_TAG_OPTION,
                        mCompleter.hashCode());
            }
            return mBuilder.build();
        }
    }

    private void addCaptureRequestOptionsInternal(@NonNull CaptureRequestOptions bundle) {
        synchronized (mLock) {
            for (Config.Option<?> option : bundle.listOptions()) {
                @SuppressWarnings("unchecked")
                Config.Option<Object> objectOpt = (Config.Option<Object>) option;
                mBuilder.getMutableConfig().insertOption(objectOpt,
                        bundle.retrieveOption(objectOpt));
            }
        }
    }

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

    @ExecutedBy("mExecutor")
    private void updateConfig(CallbackToFutureAdapter.Completer<Void> completer) {
        mPendingUpdate = true;
        // Complete the future if CaptureResult shows it's submitted successfully.
        CallbackToFutureAdapter.Completer<Void> completerToCancel = null;
        if (mCompleter != null) {
            completerToCancel = mCompleter;
        }
        mCompleter = completer;
        if (mIsActive) {
            updateSession();
        }
        if (completerToCancel != null) {
            completerToCancel.setException(new CameraControl.OperationCanceledException(
                    "Camera2CameraControl was updated with new options."));
        }
    }

    @ExecutedBy("mExecutor")
    private void updateSession() {
        mCamera2CameraControlImpl.updateSessionConfig();
        mPendingUpdate = false;
    }

    /**
     * Set 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.
     *
     * @hide
     */
    @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 {
            if (mCompleter != null) {
                mCompleter.setException(new CameraControl.OperationCanceledException(
                        "The camera control has became inactive."));
                mCompleter = null;
            }
        }
    }
}