/* * Copyright (C) 2014 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 android.hardware.camera2.legacy; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.utils.ParamsUtils; import android.util.Log; import java.util.Objects; import static android.hardware.camera2.CaptureRequest.*; import static com.android.internal.util.Preconditions.*; /** * Map capture request data into legacy focus state transitions. * *

This object will asynchronously process auto-focus changes, so no interaction * with it is necessary beyond reading the current state and updating with the latest trigger.

*/ @SuppressWarnings("deprecation") public class LegacyFocusStateMapper { private static String TAG = "LegacyFocusStateMapper"; private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private final Camera mCamera; private int mAfStatePrevious = CONTROL_AF_STATE_INACTIVE; private String mAfModePrevious = null; /** Guard mAfRun and mAfState */ private final Object mLock = new Object(); /** Guard access with mLock */ private int mAfRun = 0; /** Guard access with mLock */ private int mAfState = CONTROL_AF_STATE_INACTIVE; /** * Instantiate a new focus state mapper. * * @param camera a non-{@code null} camera1 device * * @throws NullPointerException if any of the args were {@code null} */ public LegacyFocusStateMapper(Camera camera) { mCamera = checkNotNull(camera, "camera must not be null"); } /** * Process the AF triggers from the request as a camera1 autofocus routine. * *

This method should be called after the parameters are {@link LegacyRequestMapper mapped} * with the request.

* *

Callbacks are processed in the background, and the next call to {@link #mapResultTriggers} * will have the latest AF state as reflected by the camera1 callbacks.

* *

None of the arguments will be mutated.

* * @param captureRequest a non-{@code null} request * @param parameters a non-{@code null} parameters corresponding to this request (read-only) */ public void processRequestTriggers(CaptureRequest captureRequest, Camera.Parameters parameters) { checkNotNull(captureRequest, "captureRequest must not be null"); /* * control.afTrigger */ int afTrigger = ParamsUtils.getOrDefault(captureRequest, CONTROL_AF_TRIGGER, CONTROL_AF_TRIGGER_IDLE); final String afMode = parameters.getFocusMode(); if (!Objects.equals(mAfModePrevious, afMode)) { if (VERBOSE) { Log.v(TAG, "processRequestTriggers - AF mode switched from " + mAfModePrevious + " to " + afMode); } // Switching modes always goes back to INACTIVE; ignore callbacks from previous modes synchronized (mLock) { ++mAfRun; mAfState = CONTROL_AF_STATE_INACTIVE; } mCamera.cancelAutoFocus(); } mAfModePrevious = afMode; // Passive AF Scanning { final int currentAfRun; synchronized (mLock) { currentAfRun = mAfRun; } Camera.AutoFocusMoveCallback afMoveCallback = new Camera.AutoFocusMoveCallback() { @Override public void onAutoFocusMoving(boolean start, Camera camera) { synchronized (mLock) { int latestAfRun = mAfRun; if (VERBOSE) { Log.v(TAG, "onAutoFocusMoving - start " + start + " latest AF run " + latestAfRun + ", last AF run " + currentAfRun ); } if (currentAfRun != latestAfRun) { Log.d(TAG, "onAutoFocusMoving - ignoring move callbacks from old af run" + currentAfRun ); return; } int newAfState = start ? CONTROL_AF_STATE_PASSIVE_SCAN : CONTROL_AF_STATE_PASSIVE_FOCUSED; // We never send CONTROL_AF_STATE_PASSIVE_UNFOCUSED switch (afMode) { case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE: case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO: break; // This callback should never be sent in any other AF mode default: Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode " + afMode); } mAfState = newAfState; } } }; // Only set move callback if we can call autofocus. switch (afMode) { case Parameters.FOCUS_MODE_AUTO: case Parameters.FOCUS_MODE_MACRO: case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE: case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO: mCamera.setAutoFocusMoveCallback(afMoveCallback); } } // AF Locking switch (afTrigger) { case CONTROL_AF_TRIGGER_START: int afStateAfterStart; switch (afMode) { case Parameters.FOCUS_MODE_AUTO: case Parameters.FOCUS_MODE_MACRO: afStateAfterStart = CONTROL_AF_STATE_ACTIVE_SCAN; break; case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE: case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO: afStateAfterStart = CONTROL_AF_STATE_PASSIVE_SCAN; break; default: // EDOF, INFINITY afStateAfterStart = CONTROL_AF_STATE_INACTIVE; } final int currentAfRun; synchronized (mLock) { currentAfRun = ++mAfRun; mAfState = afStateAfterStart; } if (VERBOSE) { Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_START, " + "new AF run is " + currentAfRun); } // Avoid calling autofocus unless we are in a state that supports calling this. if (afStateAfterStart == CONTROL_AF_STATE_INACTIVE) { break; } mCamera.autoFocus(new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { synchronized (mLock) { int latestAfRun = mAfRun; if (VERBOSE) { Log.v(TAG, "onAutoFocus - success " + success + " latest AF run " + latestAfRun + ", last AF run " + currentAfRun); } // Ignore old auto-focus results, since another trigger was requested if (latestAfRun != currentAfRun) { Log.d(TAG, String.format("onAutoFocus - ignoring AF callback " + "(old run %d, new run %d)", currentAfRun, latestAfRun)); return; } int newAfState = success ? CONTROL_AF_STATE_FOCUSED_LOCKED : CONTROL_AF_STATE_NOT_FOCUSED_LOCKED; switch (afMode) { case Parameters.FOCUS_MODE_AUTO: case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE: case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO: case Parameters.FOCUS_MODE_MACRO: break; // This callback should never be sent in any other AF mode default: Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode " + afMode); } mAfState = newAfState; } } }); break; case CONTROL_AF_TRIGGER_CANCEL: synchronized (mLock) { int updatedAfRun; synchronized (mLock) { updatedAfRun = ++mAfRun; mAfState = CONTROL_AF_STATE_INACTIVE; } mCamera.cancelAutoFocus(); if (VERBOSE) { Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_CANCEL, " + "new AF run is " + updatedAfRun); } } break; case CONTROL_AF_TRIGGER_IDLE: // No action necessary. The callbacks will handle transitions. break; default: Log.w(TAG, "processRequestTriggers - ignoring unknown control.afTrigger = " + afTrigger); } } /** * Update the {@code result} camera metadata map with the new value for the * {@code control.afState}. * *

AF callbacks are processed in the background, and each call to {@link #mapResultTriggers} * will have the latest AF state as reflected by the camera1 callbacks.

* * @param result a non-{@code null} result */ public void mapResultTriggers(CameraMetadataNative result) { checkNotNull(result, "result must not be null"); int newAfState; synchronized (mLock) { newAfState = mAfState; } if (VERBOSE && newAfState != mAfStatePrevious) { Log.v(TAG, String.format("mapResultTriggers - afState changed from %s to %s", afStateToString(mAfStatePrevious), afStateToString(newAfState))); } result.set(CaptureResult.CONTROL_AF_STATE, newAfState); mAfStatePrevious = newAfState; } private static String afStateToString(int afState) { switch (afState) { case CONTROL_AF_STATE_ACTIVE_SCAN: return "ACTIVE_SCAN"; case CONTROL_AF_STATE_FOCUSED_LOCKED: return "FOCUSED_LOCKED"; case CONTROL_AF_STATE_INACTIVE: return "INACTIVE"; case CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: return "NOT_FOCUSED_LOCKED"; case CONTROL_AF_STATE_PASSIVE_FOCUSED: return "PASSIVE_FOCUSED"; case CONTROL_AF_STATE_PASSIVE_SCAN: return "PASSIVE_SCAN"; case CONTROL_AF_STATE_PASSIVE_UNFOCUSED: return "PASSIVE_UNFOCUSED"; default : return "UNKNOWN(" + afState + ")"; } } }