
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 / setRepeatingRequestcapture / 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