public class

WaitForRepeatingRequestStart

extends java.lang.Object

 java.lang.Object

↳androidx.camera.camera2.internal.compat.workaround.WaitForRepeatingRequestStart

Overview

The workaround is used to wait for the other CameraCaptureSessions to complete their in-flight capture sequences before opening the current session.

If it tries to open the CameraCaptureSession before the others to complete their in-flight capture sequences, the current session may fail to be configured.

Summary

Constructors
publicWaitForRepeatingRequestStart(Quirks cameraQuirks)

Constructor of the WaitForRepeatingRequestStart workaround

Methods
public <any>getStartStreamFuture()

Returns a ListenableFuture to indicate whether the start repeating request is done.

public voidonSessionEnd()

This should be called when a SynchronizedCaptureSession is stopped or closed.

public <any>openCaptureSession(CameraDevice cameraDevice, SessionConfigurationCompat sessionConfigurationCompat, java.util.List<DeferrableSurface> deferrableSurfaces, java.util.List<SynchronizedCaptureSession> closingSessions, WaitForRepeatingRequestStart.OpenCaptureSession openCaptureSession)

For b/146773463: It needs to check all the releasing capture sessions are ready for opening next capture session.

public intsetSingleRepeatingRequest(CaptureRequest request, CameraCaptureSession.CaptureCallback listener, WaitForRepeatingRequestStart.SingleRepeatingRequest singleRepeatingRequest)

Hook the setSingleRepeatingRequest() to know if it has started a repeating request.

public booleanshouldWaitRepeatingSubmit()

Return true if the opening of the session should wait for the other CameraCaptureSessions to complete their in-flight capture sequences before opening the current session.

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

Constructors

public WaitForRepeatingRequestStart(Quirks cameraQuirks)

Constructor of the WaitForRepeatingRequestStart workaround

Methods

public boolean shouldWaitRepeatingSubmit()

Return true if the opening of the session should wait for the other CameraCaptureSessions to complete their in-flight capture sequences before opening the current session.

public <any> getStartStreamFuture()

Returns a ListenableFuture to indicate whether the start repeating request is done.

public <any> openCaptureSession(CameraDevice cameraDevice, SessionConfigurationCompat sessionConfigurationCompat, java.util.List<DeferrableSurface> deferrableSurfaces, java.util.List<SynchronizedCaptureSession> closingSessions, WaitForRepeatingRequestStart.OpenCaptureSession openCaptureSession)

For b/146773463: It needs to check all the releasing capture sessions are ready for opening next capture session.

public int setSingleRepeatingRequest(CaptureRequest request, CameraCaptureSession.CaptureCallback listener, WaitForRepeatingRequestStart.SingleRepeatingRequest singleRepeatingRequest)

Hook the setSingleRepeatingRequest() to know if it has started a repeating request.

public void onSessionEnd()

This should be called when a SynchronizedCaptureSession is stopped or closed.

Source

/*
 * Copyright 2022 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.internal.compat.workaround;

import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.camera.camera2.internal.Camera2CaptureCallbacks;
import androidx.camera.camera2.internal.SynchronizedCaptureSession;
import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat;
import androidx.camera.camera2.internal.compat.quirk.CaptureSessionStuckQuirk;
import androidx.camera.core.impl.DeferrableSurface;
import androidx.camera.core.impl.Quirks;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.FutureChain;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.concurrent.futures.CallbackToFutureAdapter;

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

import java.util.ArrayList;
import java.util.List;

/**
 * The workaround is used to wait for the other CameraCaptureSessions to complete their in-flight
 * capture sequences before opening the current session.
 * <p>If it tries to open the CameraCaptureSession before the others to complete their in-flight
 * capture sequences, the current session may fail to be configured.
 *
 * @see CaptureSessionStuckQuirk
 */
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class WaitForRepeatingRequestStart {
    private final boolean mHasCaptureSessionStuckQuirk;
    private final Object mLock = new Object();

    @NonNull
    private final ListenableFuture<Void> mStartStreamingFuture;
    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
    CallbackToFutureAdapter.Completer<Void> mStartStreamingCompleter;
    /** Whether the capture session has submitted the repeating request. */
    private boolean mHasSubmittedRepeating;

    /** Constructor of the WaitForRepeatingRequestStart workaround */
    public WaitForRepeatingRequestStart(@NonNull Quirks cameraQuirks) {
        mHasCaptureSessionStuckQuirk = cameraQuirks.contains(CaptureSessionStuckQuirk.class);

        if (shouldWaitRepeatingSubmit()) {
            mStartStreamingFuture = CallbackToFutureAdapter.getFuture(completer -> {
                mStartStreamingCompleter = completer;
                return "WaitForRepeatingRequestStart[" + this + "]";
            });
        } else {
            mStartStreamingFuture = Futures.immediateFuture(null);
        }
    }

    /**
     * Return true if the opening of the session should wait for the other CameraCaptureSessions
     * to complete their in-flight capture sequences before opening the current session.
     */
    public boolean shouldWaitRepeatingSubmit() {
        return mHasCaptureSessionStuckQuirk;
    }

    /** Returns a ListenableFuture to indicate whether the start repeating request is done. */
    @NonNull
    public ListenableFuture<Void> getStartStreamFuture() {
        return Futures.nonCancellationPropagating(mStartStreamingFuture);
    }

    /**
     * For b/146773463: It needs to check all the releasing capture sessions are ready for
     * opening next capture session.
     */
    @NonNull
    public ListenableFuture<Void> openCaptureSession(
            @NonNull CameraDevice cameraDevice,
            @NonNull SessionConfigurationCompat sessionConfigurationCompat,
            @NonNull List<DeferrableSurface> deferrableSurfaces,
            @NonNull List<SynchronizedCaptureSession> closingSessions,
            @NonNull OpenCaptureSession openCaptureSession) {
        List<ListenableFuture<Void>> futureList = new ArrayList<>();
        for (SynchronizedCaptureSession session : closingSessions) {
            futureList.add(session.getOpeningBlocker());
        }

        return FutureChain.from(Futures.successfulAsList(futureList)).transformAsync(
                v -> openCaptureSession.run(cameraDevice, sessionConfigurationCompat,
                        deferrableSurfaces), CameraXExecutors.directExecutor());
    }

    /** Hook the setSingleRepeatingRequest() to know if it has started a repeating request. */
    public int setSingleRepeatingRequest(
            @NonNull CaptureRequest request,
            @NonNull CameraCaptureSession.CaptureCallback listener,
            @NonNull SingleRepeatingRequest singleRepeatingRequest)
            throws CameraAccessException {
        synchronized (mLock) {
            if (shouldWaitRepeatingSubmit()) {
                listener = Camera2CaptureCallbacks.createComboCallback(mCaptureCallback, listener);
                mHasSubmittedRepeating = true;
            }
            return singleRepeatingRequest.run(request, listener);
        }
    }

    /** This should be called when a SynchronizedCaptureSession is stopped or closed. */
    public void onSessionEnd() {
        synchronized (mLock) {
            if (shouldWaitRepeatingSubmit() && !mHasSubmittedRepeating) {
                // If the session is closed before any repeating requests have been issued,
                // then the startStreamingFuture should be cancelled.
                mStartStreamingFuture.cancel(true);
            }
        }
    }

    private final CameraCaptureSession.CaptureCallback mCaptureCallback =
            new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureStarted(@NonNull CameraCaptureSession session,
                        @NonNull CaptureRequest request, long timestamp, long frameNumber) {
                    if (mStartStreamingCompleter != null) {
                        mStartStreamingCompleter.set(null);
                        mStartStreamingCompleter = null;
                    }
                }

                @Override
                public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session,
                        int sequenceId) {
                    if (mStartStreamingCompleter != null) {
                        mStartStreamingCompleter.setCancelled();
                        mStartStreamingCompleter = null;
                    }
                }
            };

    /** Interface to forward call of the setSingleRepeatingRequest() method. */
    @FunctionalInterface
    public interface SingleRepeatingRequest {
        /** Run the setSingleRepeatingRequest() method. */
        int run(@NonNull CaptureRequest request,
                @NonNull CameraCaptureSession.CaptureCallback listener)
                throws CameraAccessException;
    }

    /** Interface to forward call of the openCaptureSession() method. */
    @FunctionalInterface
    public interface OpenCaptureSession {
        /** Run the openCaptureSession() method. */
        @NonNull
        ListenableFuture<Void> run(@NonNull CameraDevice cameraDevice,
                @NonNull SessionConfigurationCompat sessionConfigurationCompat,
                @NonNull List<DeferrableSurface> deferrableSurfaces);
    }
}