diff options
author | Igor Murashkin <iam@google.com> | 2013-09-24 17:46:56 -0700 |
---|---|---|
committer | Igor Murashkin <iam@google.com> | 2013-09-24 19:34:26 -0700 |
commit | fc121178a18d0860d020e9cb2253c5b00dbf19c3 (patch) | |
tree | 12081eaf212a94e406fcd1a0b2373aff36e566da | |
parent | 07f09b47112dc1094649da00f7e86024b67d5777 (diff) | |
download | android_frameworks_ex-fc121178a18d0860d020e9cb2253c5b00dbf19c3.tar.gz android_frameworks_ex-fc121178a18d0860d020e9cb2253c5b00dbf19c3.tar.bz2 android_frameworks_ex-fc121178a18d0860d020e9cb2253c5b00dbf19c3.zip |
camera2: Add state machine for doing auto-focus easily from apps
Bug: 10890749
Change-Id: I966768bdcdbb2876d44eefc2156e7aca64c4e601
-rw-r--r-- | camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java | 275 |
1 files changed, 275 insertions, 0 deletions
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. + * + * <p>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.</p> + */ +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. + * + * <p>This is not called for results where the AF either succeeds or fails.</p> + */ + void onAutoFocusScan(CaptureResult result); + + /** + * The camera is currently not doing anything with the autofocus. + * + * <p>Autofocus could be off, or this could be an intermediate state transition as + * scanning restarts.</p> + */ + 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}. + * + * <p>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.</p> + * + * @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. + * + * <p>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.</p> + * + * <p>Create a new repeating request from repeatingBuilder and set that as the updated + * repeating request.</p> + * + * <p>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.</p> + * + * @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. + * + * <p>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.</p> + * + * <p>Create a new repeating request from repeatingBuilder and set that as the updated + * repeating request.</p> + * + * <p>Once the unlock takes effect, {@link AutoFocusStateListener#onAutoFocusInactive} is + * invoked, and after that the effects depend on which mode you were in: + * <ul> + * <li>Passive - Scanning restarts with {@link AutoFocusStateListener#onAutoFocusScan}</li> + * <li>Active - The lens goes back to a default position (no callbacks)</li> + * </ul> + * </p> + * + * @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. + * + * <p>This is typically only used when locking the passive AF has failed.</p> + * + * <p>Once active AF scanning starts, {@link AutoFocusStateListener#onAutoFocusScan} will be + * invoked.</p> + * + * <p>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.</p> + * + * <p>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.</p> + * + * <p>Create a new repeating request from repeatingBuilder and set that as the updated + * repeating request.</p> + * + * @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. + * + * <p>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.</p> + * + * <p>Once passive AF scanning starts, {@link AutoFocusStateListener#onAutoFocusScan} will be + * invoked.</p> + * + * @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); + } +} |