Observable.combineLatest(previewObservable, mOnShutterClick, (captureSessionData, o) -> captureSessionData) .firstElement().toObservable() .flatMap(this::waitForAf) .flatMap(this::waitForAe) .flatMap(captureSessionData -> captureStillPicture(captureSessionData.session)) .subscribe(__ -> {}, this::onError)
waitForAe
and waitForAf
? In order for the autofocus / auto exposure processes to start, and upon their completion we would receive a notification of readiness for the image.Observable
, which emits an event when the camera reports that the convergence process has triggered (in order not to repeat the words “autofocus” and “auto exposure”, then I will use the word “convergence”). But how to start and control this process?capture
with the necessary flags and wait for the CaptureCallback
call onCaptureCompleted
.onCaptureCompleted
.setRepeatingRequest
to continue the preview and waits until the onCaptureCompleted comes with a certain set of flags in TotalCaptureResult
. The right answer could come in a few onCaptureCompleted
!setRepeatingRequest
to continue the preview;onCaptureCompleted
notification onCaptureCompleted
evidence that the convergence process has been completed.ConvergeWaiter
class with the following fields: private final CaptureRequest.Key<Integer> mRequestTriggerKey; private final int mRequestTriggerStartValue;
capture
.CaptureRequest.CONTROL_AF_TRIGGER
and CameraMetadata.CONTROL_AF_TRIGGER_START
respectively. For auto exposure - CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER
and CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START
respectively. private final CaptureResult.Key<Integer> mResultStateKey; private final List<Integer> mResultReadyStates;
onCaptureCompleted
result. When we see one of the expected key values, we can assume that the convergence process is complete.CaptureResult.CONTROL_AF_STATE
, a list of values: CaptureResult.CONTROL_AF_STATE_INACTIVE, CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED, CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED, CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
CaptureResult.CONTROL_AE_STATE
, a list of values: CaptureResult.CONTROL_AE_STATE_INACTIVE, CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED, CaptureResult.CONTROL_AE_STATE_CONVERGED, CaptureResult.CONTROL_AE_STATE_LOCKED.
ConvergeWaiter
instances for autofocus and exposure, for this we will make a factory: static class Factory { private static final List<Integer> afReadyStates = Collections.unmodifiableList( Arrays.asList( CaptureResult.CONTROL_AF_STATE_INACTIVE, CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED, CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED, CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED ) ); private static final List<Integer> aeReadyStates = Collections.unmodifiableList( Arrays.asList( CaptureResult.CONTROL_AE_STATE_INACTIVE, CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED, CaptureResult.CONTROL_AE_STATE_CONVERGED, CaptureResult.CONTROL_AE_STATE_LOCKED ) ); static ConvergeWaiter createAutoFocusConvergeWaiter() { return new ConvergeWaiter( CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START, CaptureResult.CONTROL_AF_STATE, afReadyStates ); } static ConvergeWaiter createAutoExposureConvergeWaiter() { return new ConvergeWaiter( CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START, CaptureResult.CONTROL_AE_STATE, aeReadyStates ); } }
capture
/ setRepeatingRequest
capture
/ setRepeatingRequest
we need:CameraCaptureSession
, which is available in CaptureSessionData
;CaptureRequest
we will create using CaptureRequest.Builder.
Single<CaptureSessionData> waitForConverge(@NonNull CaptureSessionData captureResultParams, @NonNull CaptureRequest.Builder builder)
builder
configured for the preview. Therefore, CaptureRequest
for previews can be created immediately by calling CaptureRequest previewRequest = builder.build();
CaptureRequest
to run the convergence procedure, add a flag to the builder
that will start the necessary convergence process: builder.set(mRequestTriggerKey, mRequestTriggerStartValue); CaptureRequest triggerRequest = builder.build();
Observable
from the methods of capture
/ setRepeatingRequest
: Observable<CaptureSessionData> triggerObservable = CameraRxWrapper.fromCapture(captureResultParams.session, triggerRequest); Observable<CaptureSessionData> previewObservable = CameraRxWrapper.fromSetRepeatingRequest(captureResultParams.session, previewRequest);
merge
operator. Observable<CaptureSessionData> convergeObservable = Observable .merge(previewObservable, triggerObservable)
convergeObservable
will emit events with the results of the onCaptureCompleted
calls.CaptureResult
passed to this method contains the expected value of the flag. To do this, create a function that takes CaptureResult
and returns true
if it has the expected flag value: private boolean isStateReady(@NonNull CaptureResult result) { Integer aeState = result.get(mResultStateKey); return aeState == null || mResultReadyStates.contains(aeState); }
null
needed for the crooked implementations of the Camera2 API, so as not to hang forever.filter
operator to wait for the event for which isStateReady
is executed: .filter(resultParams -> isStateReady(resultParams.result))
.first(captureResultParams);
Single<CaptureSessionData> convergeSingle = Observable .merge(previewObservable, triggerObservable) .filter(resultParams -> isStateReady(resultParams.result)) .first(captureResultParams);
private static final int TIMEOUT_SECONDS = 3; Single<CaptureSessionData> timeOutSingle = Single .just(captureResultParams) .delay(TIMEOUT_SECONDS, TimeUnit.SECONDS, AndroidSchedulers.mainThread());
delay
operator reissues events with a specified delay. By default, it does this in a stream belonging to the computation scheduler, so we drop it into Main Thread using the last parameter.convergeSingle
and timeOutSingle
, and whoever is the first to emit an event, he won: return Single .merge(convergeSingle, timeOutSingle) .firstElement() .toSingle();
@NonNull Single<CaptureSessionData> waitForConverge(@NonNull CaptureSessionData captureResultParams, @NonNull CaptureRequest.Builder builder) { CaptureRequest previewRequest = builder.build(); builder.set(mRequestTriggerKey, mRequestTriggerStartValue); CaptureRequest triggerRequest = builder.build(); Observable<CaptureSessionData> triggerObservable = CameraRxWrapper.fromCapture(captureResultParams.session, triggerRequest); Observable<CaptureSessionData> previewObservable = CameraRxWrapper.fromSetRepeatingRequest(captureResultParams.session, previewRequest); Single<CaptureSessionData> convergeSingle = Observable .merge(previewObservable, triggerObservable) .filter(resultParams -> isStateReady(resultParams.result)) .first(captureResultParams); Single<CaptureSessionData> timeOutSingle = Single .just(captureResultParams) .delay(TIMEOUT_SECONDS, TimeUnit.SECONDS, AndroidSchedulers.mainThread()); return Single .merge(convergeSingle, timeOutSingle) .firstElement() .toSingle(); }
waitForAf
/ waitForAe
private final ConvergeWaiter mAutoFocusConvergeWaiter = ConvergeWaiter.Factory.createAutoFocusConvergeWaiter(); private final ConvergeWaiter mAutoExposureConvergeWaiter = ConvergeWaiter.Factory.createAutoExposureConvergeWaiter();
private Observable<CaptureSessionData> waitForAf(@NonNull CaptureSessionData captureResultParams) { return Observable .fromCallable(() -> createPreviewBuilder(captureResultParams.session, mSurface)) .flatMap( previewBuilder -> mAutoFocusConvergeWaiter .waitForConverge(captureResultParams, previewBuilder) .toObservable() ); } @NonNull private Observable<CaptureSessionData> waitForAe(@NonNull CaptureSessionData captureResultParams) { return Observable .fromCallable(() -> createPreviewBuilder(captureResultParams.session, mSurface)) .flatMap( previewBuilder -> mAutoExposureConvergeWaiter .waitForConverge(captureResultParams, previewBuilder) .toObservable() ); }
fromCallable
operator. It may be tempting to use the operator just
. For example: just(createPreviewBuilder(captureResultParams.session, mSurface)).
createPreviewBuilder
function will be called right at the time of the waitForAf
call, and we want it to be called only when a subscription to our Observable
appears.Source: https://habr.com/ru/post/352318/
All Articles