Search code examples
javaandroidandroid-cameraandroid-camera2

Auto Focus and Manual focus Focus Locking Error In Camera 2 api


I have one camera app in which i am providing user with 2 option manual focus and auto focus and then capturing 6 image back to back but my issue is when i call auto focus and then capture image it is getting stuck , but if i start capture without calling auto focus it is working fine

state: STATE_WAITING_LOCK
17:55:14.621 com....extureView  V  Current afState: 4
17:55:14.626 com....extureView  V  state: STATE_WAITING_LOCK
17:55:14.626 com....extureView  V  Current afState: 4
17:55:14.651 com....extureView  V  state: STATE_WAITING_LOCK
17:55:14.651 com....extureView  V  Current afState: 4
17:55:14.674 com....extureView  V  state: STATE_WAITING_LOCK
17:55:14.674 com....extureView  V  Current afState: 4
17:55:14.688 com....extureView  V  state: STATE_WAITING_LOCK
17:55:14.688 com....extureView  V  Current afState: 4

Auto Focus

 protected CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @SuppressWarnings("ConstantConditions")
        private void process(CaptureResult result) {
            switch (mState) {
                case STATE_STREAMING: {
                    break;
                }
                case STATE_WAITING_LOCK: {
                    Log.v(TAG, "state: STATE_WAITING_LOCK");
                    final int afState = result.get(CaptureResult.CONTROL_AF_STATE);
                    Log.v(TAG, "Current afState: " + afState); // Add this line
                    if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
                            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
                        // Check if focusLockLatch is null before calling countDown()
                        if (focusLockLatch == null) {
                            focusLockLatch = new CountDownLatch(1);
                        }
                        focusLockLatch.countDown();
                    }
                    break;
                }
                case STATE_WAITING_PRECAPTURE: {
                    Log.v(TAG, "state: STATE_WAITING_PRECAPTURE");
                    mState = STATE_WAITING_NON_PRECAPTURE;
                    break;
                }
            }
        }

        @Override
        public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
            super.onCaptureStarted(session, request, timestamp, frameNumber);
            //Log.v( TAG, "onCaptureStarted( session, request, timestamp, frameNumber )" );
        }

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
            super.onCaptureProgressed(session, request, partialResult);
            process(partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
            process(result);
        }

        @Override
        public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
            super.onCaptureFailed(session, request, failure);
            Log.v(TAG, "onCaptureFailed( session, request, failure )");
        }
    };


public void autoFocus() {
        try {
            focusSeekBar.setCurrentValue(0);
            setFocusMode(Mode.AUTO);
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO);
            // Trigger an autofocus scan
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
            Log.v(TAG, "autoFocus()");
            mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, null);

            // set seekbar  to focus distance
        } catch (CameraAccessException e) {
            OnCameraErrorListener.handle(mCameraErrorCallback, e);
        }
    }


 protected void lockFocus() {
        Log.v(TAG, "lockFocus()");

        if (mCaptureSession == null) {
            return;
        }
        try {

            if (focusMode == Mode.AUTO) {
                // This is how to tell the camera to lock focus.
                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
                mState = STATE_WAITING_LOCK;
                mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);

                // check fo focus state
                // if focus is locked call start capture
                // if focus is not locked call unlock focus

                if (focusLockLatch.await(500, TimeUnit.MILLISECONDS)) {
                    startCapture();
                } else {
                    unlockFocus();
                }
            } else {
                startCapture();
            }
        } catch (CameraAccessException e) {
            OnCameraErrorListener.handle(mCameraErrorCallback, e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    protected void unlockFocus() {
        Log.v(TAG, "unlockFocus()");
        try {
            // Reset the auto-focus trigger
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            mCaptureSession.capture(mPreviewRequestBuilder.build(), null, null);
            // After this, the camera will go back to the normal state of preview.
            mState = STATE_STREAMING;
            //resume Zoom effect after taking a picture
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            if (mZoom != null) {
                mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, mZoom);
            }
            mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            OnCameraErrorListener.handle(mCameraErrorCallback, e);
        }
    }


    protected void unlockFocus() {
        Log.v(TAG, "unlockFocus()");
        try {
            // Reset the auto-focus trigger
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            mCaptureSession.capture(mPreviewRequestBuilder.build(), null, null);
            // After this, the camera will go back to the normal state of preview.
            mState = STATE_STREAMING;
            //resume Zoom effect after taking a picture
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            if (mZoom != null) {
                mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, mZoom);
            }
            mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            OnCameraErrorListener.handle(mCameraErrorCallback, e);
        }
    }


Auto Focus

 protected CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @SuppressWarnings("ConstantConditions")
        private void process(CaptureResult result) {
            switch (mState) {
                case STATE_STREAMING: {
                    break;
                }
                case STATE_WAITING_LOCK: {
                    Log.v(TAG, "state: STATE_WAITING_LOCK");
                    final int afState = result.get(CaptureResult.CONTROL_AF_STATE);
                    Log.v(TAG, "Current afState: " + afState); // Add this line
                    if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
                            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
                        // Check if focusLockLatch is null before calling countDown()
                        if (focusLockLatch == null) {
                            focusLockLatch = new CountDownLatch(1);
                        }
                        focusLockLatch.countDown();
                    }
                    break;
                }
                case STATE_WAITING_PRECAPTURE: {
                    Log.v(TAG, "state: STATE_WAITING_PRECAPTURE");
                    mState = STATE_WAITING_NON_PRECAPTURE;
                    break;
                }
            }
        }

        @Override
        public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
            super.onCaptureStarted(session, request, timestamp, frameNumber);
            //Log.v( TAG, "onCaptureStarted( session, request, timestamp, frameNumber )" );
        }

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
            super.onCaptureProgressed(session, request, partialResult);
            process(partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
            process(result);
        }

        @Override
        public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
            super.onCaptureFailed(session, request, failure);
            Log.v(TAG, "onCaptureFailed( session, request, failure )");
        }
    };


public void autoFocus() {
        try {
            focusSeekBar.setCurrentValue(0);
            setFocusMode(Mode.AUTO);
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_AUTO);
            // Trigger an autofocus scan
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
            Log.v(TAG, "autoFocus()");
            mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, null);

            // set seekbar  to focus distance
        } catch (CameraAccessException e) {
            OnCameraErrorListener.handle(mCameraErrorCallback, e);
        }
    }


 protected void lockFocus() {
        Log.v(TAG, "lockFocus()");

        if (mCaptureSession == null) {
            return;
        }
        try {

            if (focusMode == Mode.AUTO) {
                // This is how to tell the camera to lock focus.
                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
                mState = STATE_WAITING_LOCK;
                mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);

                // check fo focus state
                // if focus is locked call start capture
                // if focus is not locked call unlock focus

                if (focusLockLatch.await(500, TimeUnit.MILLISECONDS)) {
                    startCapture();
                } else {
                    unlockFocus();
                }
            } else {
                startCapture();
            }
        } catch (CameraAccessException e) {
            OnCameraErrorListener.handle(mCameraErrorCallback, e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    protected void unlockFocus() {
        Log.v(TAG, "unlockFocus()");
        try {
            // Reset the auto-focus trigger
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            mCaptureSession.capture(mPreviewRequestBuilder.build(), null, null);
            // After this, the camera will go back to the normal state of preview.
            mState = STATE_STREAMING;
            //resume Zoom effect after taking a picture
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            if (mZoom != null) {
                mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, mZoom);
            }
            mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            OnCameraErrorListener.handle(mCameraErrorCallback, e);
        }
    }


    protected void unlockFocus() {
        Log.v(TAG, "unlockFocus()");
        try {
            // Reset the auto-focus trigger
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            mCaptureSession.capture(mPreviewRequestBuilder.build(), null, null);
            // After this, the camera will go back to the normal state of preview.
            mState = STATE_STREAMING;
            //resume Zoom effect after taking a picture
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            if (mZoom != null) {
                mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, mZoom);
            }
            mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
        } catch (CameraAccessException e) {
            OnCameraErrorListener.handle(mCameraErrorCallback, e);
        }
    }



Solution

  • You are using the same thread for focusLockLatch await() and countdown() invocations. So if await is invoked first, the thread is already and waiting until countdown is called, but countdown can no longer be invoked to release the thread.

    The reason same thread is being for callback is mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, null);

    Since you are setting null to the handler i.e. 3rd parameter here, current thread's looper is being used and thus the callback is executed on the current thread. I see that you are already using a mBackgroundHandler in other places, simply use that here as well i.e. mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);.

    Also, since your mBackgroundHandler initialization is not here, maybe double-check that the mBackgroundHandler does indeed execute on a background thread, not the same thread (add some logs to check it if needed).