From fc121178a18d0860d020e9cb2253c5b00dbf19c3 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Tue, 24 Sep 2013 17:46:56 -0700 Subject: camera2: Add state machine for doing auto-focus easily from apps Bug: 10890749 Change-Id: I966768bdcdbb2876d44eefc2156e7aca64c4e601 --- .../ex/camera2/pos/AutoFocusStateMachine.java | 275 +++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java diff --git a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java new file mode 100644 index 0000000..ba32b12 --- /dev/null +++ b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java @@ -0,0 +1,275 @@ +/* + * Copyright 2013 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 com.android.ex.camera2.pos; + +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Log; + +/** + * Manage the auto focus state machine for CameraDevice. + * + *

Requests are created only when the AF needs to be manipulated from the user, + * but automatic camera-caused AF state changes are broadcasted from any new result.

+ */ +public class AutoFocusStateMachine { + + public interface AutoFocusStateListener { + /** + * The camera is currently focused (either active or passive). + * + * @param locked True if the lens has been locked from moving, false otherwise. + */ + void onAutoFocusSuccess(CaptureResult result, boolean locked); + + /** + * The camera is currently not focused (either active or passive). + * + * @param locked False if the AF is still scanning, true if needs a restart. + */ + void onAutoFocusFail(CaptureResult result, boolean locked); + + /** + * The camera is currently scanning (either active or passive) + * and has not yet converged. + * + *

This is not called for results where the AF either succeeds or fails.

+ */ + void onAutoFocusScan(CaptureResult result); + + /** + * The camera is currently not doing anything with the autofocus. + * + *

Autofocus could be off, or this could be an intermediate state transition as + * scanning restarts.

+ */ + void onAutoFocusInactive(CaptureResult result); + } + + private static final String TAG = "AutoFocusStateMachine"; + private static final boolean DEBUG_LOGGING = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE); + private static final int AF_UNINITIALIZED = -1; + + private final AutoFocusStateListener mListener; + private int mLastAfState = AF_UNINITIALIZED; + private int mLastAfMode = AF_UNINITIALIZED; + private int mCurrentAfMode = AF_UNINITIALIZED; + private int mCurrentAfTrigger = AF_UNINITIALIZED; + + public AutoFocusStateMachine(AutoFocusStateListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener should not be null"); + } + mListener = listener; + } + + /** + * Invoke every time we get a new CaptureResult via + * {@link CameraDevice.CaptureListener#onCaptureCompleted}. + * + *

This function is responsible for dispatching updates via the + * {@link AutoFocusStateListener} so without calling this on a regular basis, no + * AF changes will be observed.

+ * + * @param result CaptureResult + */ + public synchronized void onCaptureCompleted(CaptureResult result) { + + int afState = result.get(CaptureResult.CONTROL_AF_STATE); + + if (DEBUG_LOGGING) Log.d(TAG, "onCaptureCompleted - new AF state = " + afState); + + if (mLastAfState == afState) { + // Same AF state as last time, nothing else needs to be done. + return; + } + + if (VERBOSE_LOGGING) Log.v(TAG, "onCaptureCompleted - new AF state = " + afState); + + mLastAfState = afState; + mLastAfMode = result.get(CaptureResult.CONTROL_AF_MODE); + + switch (afState) { + case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED: + mListener.onAutoFocusSuccess(result, /*locked*/true); + break; + case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: + mListener.onAutoFocusFail(result, /*locked*/true); + break; + case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: + mListener.onAutoFocusSuccess(result, /*locked*/true); + break; + //case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED: // not yet pushed + // mListener.onAutoFocusFail(result, /*locked*/false); + // break; + case CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN: + mListener.onAutoFocusScan(result); + break; + case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN: + mListener.onAutoFocusScan(result); + break; + case CaptureResult.CONTROL_AF_STATE_INACTIVE: + mListener.onAutoFocusInactive(result); + break; + } + } + + /** + * Lock the lens from moving. Typically used before taking a picture. + * + *

After calling this function, submit the new requestBuilder as a separate capture. + * Do not submit it as a repeating request or the AF lock will be repeated every time.

+ * + *

Create a new repeating request from repeatingBuilder and set that as the updated + * repeating request.

+ * + *

If the lock succeeds, {@link AutoFocusStateListener#onAutoFocusSuccess} with + * {@code locked == true} will be invoked. If the lock fails, + * {@link AutoFocusStateListener#onAutoFocusFail} with {@code scanning == false} will be + * invoked.

+ * + * @param repeatingBuilder Builder for a repeating request. + * @param requestBuilder Builder for a non-repeating request. + * + */ + public synchronized void lockAutoFocus(CaptureRequest.Builder repeatingBuilder, + CaptureRequest.Builder requestBuilder) { + + if (VERBOSE_LOGGING) Log.v(TAG, "lockAutoFocus"); + + if (mCurrentAfMode == AF_UNINITIALIZED) { + throw new IllegalStateException("AF mode was not enabled"); + } + + mCurrentAfTrigger = CaptureRequest.CONTROL_AF_TRIGGER_START; + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, + CaptureRequest.CONTROL_AF_TRIGGER_IDLE); + requestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, + CaptureRequest.CONTROL_AF_TRIGGER_START); + } + + /** + * Unlock the lens, allowing it to move again. Typically used after taking a picture. + * + *

After calling this function, submit the new requestBuilder as a separate capture. + * Do not submit it as a repeating request or the AF lock will be repeated every time.

+ * + *

Create a new repeating request from repeatingBuilder and set that as the updated + * repeating request.

+ * + *

Once the unlock takes effect, {@link AutoFocusStateListener#onAutoFocusInactive} is + * invoked, and after that the effects depend on which mode you were in: + *

+ *

+ * + * @param repeatingBuilder Builder for a repeating request. + * @param requestBuilder Builder for a non-repeating request. + * + */ + public synchronized void unlockAutoFocus(CaptureRequest.Builder repeatingBuilder, + CaptureRequest.Builder requestBuilder) { + + if (VERBOSE_LOGGING) Log.v(TAG, "unlockAutoFocus"); + + if (mCurrentAfMode == AF_UNINITIALIZED) { + throw new IllegalStateException("AF mode was not enabled"); + } + + mCurrentAfTrigger = CaptureRequest.CONTROL_AF_TRIGGER_CANCEL; + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, + CaptureRequest.CONTROL_AF_TRIGGER_IDLE); + requestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, + CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); + } + + /** + * Enable active auto focus, immediately triggering a converging scan. + * + *

This is typically only used when locking the passive AF has failed.

+ * + *

Once active AF scanning starts, {@link AutoFocusStateListener#onAutoFocusScan} will be + * invoked.

+ * + *

If the active scan succeeds, {@link AutoFocusStateListener#onAutoFocusSuccess} with + * {@code locked == true} will be invoked. If the active scan fails, + * {@link AutoFocusStateListener#onAutoFocusFail} with {@code scanning == false} will be + * invoked.

+ * + *

After calling this function, submit the new requestBuilder as a separate capture. + * Do not submit it as a repeating request or the AF trigger will be repeated every time.

+ * + *

Create a new repeating request from repeatingBuilder and set that as the updated + * repeating request.

+ * + * @param repeatingBuilder Builder for a repeating request. + * @param requestBuilder Builder for a non-repeating request. + * + * @param repeatingBuilder Builder for a repeating request. + */ + public synchronized void setActiveAutoFocus(CaptureRequest.Builder repeatingBuilder, + CaptureRequest.Builder requestBuilder) { + if (VERBOSE_LOGGING) Log.v(TAG, "setActiveAutoFocus"); + + mCurrentAfMode = CaptureRequest.CONTROL_AF_MODE_AUTO; + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, + CaptureRequest.CONTROL_AF_TRIGGER_IDLE); + requestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, + CaptureRequest.CONTROL_AF_TRIGGER_START); + } + + /** + * Enable passive autofocus, immediately triggering a non-converging scan. + * + *

While passive autofocus is enabled, use {@link #lockAutoFocus} to lock + * the lens before taking a picture. Once a picture is taken, use {@link #unlockAutoFocus} + * to let the lens go back into passive scanning.

+ * + *

Once passive AF scanning starts, {@link AutoFocusStateListener#onAutoFocusScan} will be + * invoked.

+ * + * @param repeatingBuilder Builder for a repeating request. + * @param picture True for still capture AF, false for video AF. + */ + public synchronized void setPassiveAutoFocus(boolean picture, + CaptureRequest.Builder repeatingBuilder) { + if (VERBOSE_LOGGING) Log.v(TAG, "setPassiveAutoFocus - picture " + picture); + + if (picture) { + mCurrentAfMode = CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE; + } else { + mCurrentAfMode = CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO; + } + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + } +} -- cgit v1.2.3