blob: d33c09eac85d27f6e02451b5346cd7b459d9a7c5 [file] [log] [blame]
/*
* 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.
*
* <p>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.</p>
*/
@SuppressWarnings("deprecation")
public class LegacyFocusStateMapper {
private static String TAG = "LegacyFocusStateMapper";
private static final boolean DEBUG = false;
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.
*
* <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped}
* with the request.</p>
*
* <p>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.</p>
*
* <p>None of the arguments will be mutated.</p>
*
* @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 (DEBUG) {
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 (DEBUG) {
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 (DEBUG) {
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 (DEBUG) {
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 (DEBUG) {
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}.
*
* <p>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.</p>
*
* @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 (DEBUG && 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 + ")";
}
}
}