am c5b6a0c8: am 44e5ce82: am 6dc35134: Merge changes I104c8243,I82811142 into lmp-dev
* commit 'c5b6a0c8ba9f1c89ec58670e374039fe7e88df2a': camera2: legacy: fix cached results bug and fix focus modes camera2: legacy: Add focus support
This commit is contained in:
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package android.hardware.camera2.legacy;
|
||||
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
import android.util.Log;
|
||||
import android.util.MutableLong;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -77,7 +78,7 @@ public class CaptureCollector {
|
||||
if (needsPreview && isPreviewCompleted()) {
|
||||
CaptureCollector.this.onPreviewCompleted();
|
||||
}
|
||||
CaptureCollector.this.onRequestCompleted(mRequest, mLegacy, mTimestamp);
|
||||
CaptureCollector.this.onRequestCompleted(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,13 +177,13 @@ public class CaptureCollector {
|
||||
private final ArrayDeque<CaptureHolder> mJpegProduceQueue;
|
||||
private final ArrayDeque<CaptureHolder> mPreviewCaptureQueue;
|
||||
private final ArrayDeque<CaptureHolder> mPreviewProduceQueue;
|
||||
private final ArrayList<CaptureHolder> mCompletedRequests = new ArrayList<>();
|
||||
|
||||
private final ReentrantLock mLock = new ReentrantLock();
|
||||
private final Condition mIsEmpty;
|
||||
private final Condition mPreviewsEmpty;
|
||||
private final Condition mNotFull;
|
||||
private final CameraDeviceState mDeviceState;
|
||||
private final LegacyResultMapper mMapper = new LegacyResultMapper();
|
||||
private int mInFlight = 0;
|
||||
private int mInFlightPreviews = 0;
|
||||
private final int mMaxInFlight;
|
||||
@@ -319,6 +320,54 @@ public class CaptureCollector {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the specified request to be completed (all buffers available).
|
||||
*
|
||||
* <p>May not wait for the same request more than once, since a successful wait
|
||||
* will erase the history of that request.</p>
|
||||
*
|
||||
* @param holder the {@link RequestHolder} for this request.
|
||||
* @param timeout a timeout to use for this call.
|
||||
* @param unit the units to use for the timeout.
|
||||
* @param timestamp the timestamp of the request will be written out to here, in ns
|
||||
*
|
||||
* @return {@code false} if this method timed out.
|
||||
*
|
||||
* @throws InterruptedException if this thread is interrupted.
|
||||
*/
|
||||
public boolean waitForRequestCompleted(RequestHolder holder, long timeout, TimeUnit unit,
|
||||
MutableLong timestamp)
|
||||
throws InterruptedException {
|
||||
long nanos = unit.toNanos(timeout);
|
||||
final ReentrantLock lock = this.mLock;
|
||||
lock.lock();
|
||||
try {
|
||||
while (!removeRequestIfCompleted(holder, /*out*/timestamp)) {
|
||||
if (nanos <= 0) {
|
||||
return false;
|
||||
}
|
||||
nanos = mNotFull.awaitNanos(nanos);
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean removeRequestIfCompleted(RequestHolder holder, MutableLong timestamp) {
|
||||
int i = 0;
|
||||
for (CaptureHolder h : mCompletedRequests) {
|
||||
if (h.mRequest.equals(holder)) {
|
||||
timestamp.value = h.mTimestamp;
|
||||
mCompletedRequests.remove(i);
|
||||
return true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to alert the {@link CaptureCollector} that the jpeg capture has begun.
|
||||
*
|
||||
@@ -431,8 +480,9 @@ public class CaptureCollector {
|
||||
}
|
||||
}
|
||||
|
||||
private void onRequestCompleted(RequestHolder request, LegacyRequest legacyHolder,
|
||||
long timestamp) {
|
||||
private void onRequestCompleted(CaptureHolder capture) {
|
||||
RequestHolder request = capture.mRequest;
|
||||
|
||||
mInFlight--;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Completed request " + request.getRequestId() +
|
||||
@@ -442,12 +492,12 @@ public class CaptureCollector {
|
||||
throw new IllegalStateException(
|
||||
"More captures completed than requests queued.");
|
||||
}
|
||||
|
||||
mCompletedRequests.add(capture);
|
||||
|
||||
mNotFull.signalAll();
|
||||
if (mInFlight == 0) {
|
||||
mIsEmpty.signalAll();
|
||||
}
|
||||
CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
|
||||
legacyHolder, timestamp);
|
||||
mDeviceState.setCaptureResult(request, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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 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.
|
||||
*
|
||||
* <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 (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;
|
||||
}
|
||||
|
||||
mCamera.setAutoFocusMoveCallback(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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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;
|
||||
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);
|
||||
}
|
||||
|
||||
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, "mapTriggers - 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 (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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ import static android.hardware.camera2.legacy.ParameterUtils.*;
|
||||
* Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
|
||||
* camera characteristics.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class LegacyMetadataMapper {
|
||||
private static final String TAG = "LegacyMetadataMapper";
|
||||
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
@@ -87,8 +88,8 @@ public class LegacyMetadataMapper {
|
||||
*/
|
||||
static final boolean LIE_ABOUT_AE_STATE = false;
|
||||
static final boolean LIE_ABOUT_AE_MAX_REGIONS = false;
|
||||
static final boolean LIE_ABOUT_AF = true;
|
||||
static final boolean LIE_ABOUT_AF_MAX_REGIONS = true;
|
||||
static final boolean LIE_ABOUT_AF = false;
|
||||
static final boolean LIE_ABOUT_AF_MAX_REGIONS = false;
|
||||
static final boolean LIE_ABOUT_AWB_STATE = false;
|
||||
static final boolean LIE_ABOUT_AWB = true;
|
||||
|
||||
@@ -161,6 +162,10 @@ public class LegacyMetadataMapper {
|
||||
* control.ae*
|
||||
*/
|
||||
mapControlAe(m, p);
|
||||
/*
|
||||
* control.af*
|
||||
*/
|
||||
mapControlAf(m, p);
|
||||
/*
|
||||
* control.awb*
|
||||
*/
|
||||
@@ -379,6 +384,54 @@ public class LegacyMetadataMapper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
private static void mapControlAf(CameraMetadataNative m, Camera.Parameters p) {
|
||||
/*
|
||||
* control.afAvailableModes
|
||||
*/
|
||||
{
|
||||
List<String> focusModes = p.getSupportedFocusModes();
|
||||
|
||||
String[] focusModeStrings = new String[] {
|
||||
Camera.Parameters.FOCUS_MODE_AUTO,
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
|
||||
Camera.Parameters.FOCUS_MODE_EDOF,
|
||||
Camera.Parameters.FOCUS_MODE_INFINITY,
|
||||
Camera.Parameters.FOCUS_MODE_MACRO,
|
||||
Camera.Parameters.FOCUS_MODE_FIXED,
|
||||
};
|
||||
|
||||
int[] focusModeInts = new int[] {
|
||||
CONTROL_AF_MODE_AUTO,
|
||||
CONTROL_AF_MODE_CONTINUOUS_PICTURE,
|
||||
CONTROL_AF_MODE_CONTINUOUS_VIDEO,
|
||||
CONTROL_AF_MODE_EDOF,
|
||||
CONTROL_AF_MODE_OFF,
|
||||
CONTROL_AF_MODE_MACRO,
|
||||
CONTROL_AF_MODE_OFF
|
||||
};
|
||||
|
||||
List<Integer> afAvail = ArrayUtils.convertStringListToIntList(
|
||||
focusModes, focusModeStrings, focusModeInts);
|
||||
|
||||
// No AF modes supported? That's unpossible!
|
||||
if (afAvail == null || afAvail.size() == 0) {
|
||||
Log.w(TAG, "No AF modes supported (HAL bug); defaulting to AF_MODE_OFF only");
|
||||
afAvail = new ArrayList<Integer>(/*capacity*/1);
|
||||
afAvail.add(CONTROL_AF_MODE_OFF);
|
||||
}
|
||||
|
||||
m.set(CONTROL_AF_AVAILABLE_MODES, ArrayUtils.toIntArray(afAvail));
|
||||
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "mapControlAf - control.afAvailableModes set to " +
|
||||
ListUtils.listToString(afAvail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void mapControlAwb(CameraMetadataNative m, Camera.Parameters p) {
|
||||
if (!LIE_ABOUT_AWB) {
|
||||
throw new AssertionError("Not implemented yet");
|
||||
@@ -794,4 +847,55 @@ public class LegacyMetadataMapper {
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the requested AF mode into its equivalent supported parameter.
|
||||
*
|
||||
* @param mode {@code CONTROL_AF_MODE}
|
||||
* @param supportedFocusModes list of camera1's supported focus modes
|
||||
* @return the stringified af mode, or {@code null} if its not supported
|
||||
*/
|
||||
static String convertAfModeToLegacy(int mode, List<String> supportedFocusModes) {
|
||||
if (supportedFocusModes == null || supportedFocusModes.isEmpty()) {
|
||||
Log.w(TAG, "No focus modes supported; API1 bug");
|
||||
return null;
|
||||
}
|
||||
|
||||
String param = null;
|
||||
switch (mode) {
|
||||
case CONTROL_AF_MODE_AUTO:
|
||||
param = Parameters.FOCUS_MODE_AUTO;
|
||||
break;
|
||||
case CONTROL_AF_MODE_CONTINUOUS_PICTURE:
|
||||
param = Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
|
||||
break;
|
||||
case CONTROL_AF_MODE_CONTINUOUS_VIDEO:
|
||||
param = Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
|
||||
break;
|
||||
case CONTROL_AF_MODE_EDOF:
|
||||
param = Parameters.FOCUS_MODE_EDOF;
|
||||
break;
|
||||
case CONTROL_AF_MODE_MACRO:
|
||||
param = Parameters.FOCUS_MODE_MACRO;
|
||||
break;
|
||||
case CONTROL_AF_MODE_OFF:
|
||||
if (supportedFocusModes.contains(Parameters.FOCUS_MODE_FIXED)) {
|
||||
param = Parameters.FOCUS_MODE_FIXED;
|
||||
} else {
|
||||
param = Parameters.FOCUS_MODE_INFINITY;
|
||||
}
|
||||
}
|
||||
|
||||
if (!supportedFocusModes.contains(param)) {
|
||||
// Weed out bad user input by setting to the first arbitrary focus mode
|
||||
String defaultMode = supportedFocusModes.get(0);
|
||||
Log.w(TAG,
|
||||
String.format(
|
||||
"convertAfModeToLegacy - ignoring unsupported mode %d, " +
|
||||
"defaulting to %s", mode, defaultMode));
|
||||
param = defaultMode;
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.params.MeteringRectangle;
|
||||
import android.hardware.camera2.utils.ListUtils;
|
||||
import android.hardware.camera2.utils.ParamsUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Range;
|
||||
import android.util.Size;
|
||||
@@ -38,6 +39,7 @@ import static android.hardware.camera2.CaptureRequest.*;
|
||||
/**
|
||||
* Provide legacy-specific implementations of camera2 CaptureRequest for legacy devices.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class LegacyRequestMapper {
|
||||
private static final String TAG = "LegacyRequestMapper";
|
||||
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
@@ -158,7 +160,7 @@ public class LegacyRequestMapper {
|
||||
{
|
||||
Range<Integer> compensationRange =
|
||||
characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
|
||||
int compensation = getOrDefault(request,
|
||||
int compensation = ParamsUtils.getOrDefault(request,
|
||||
CONTROL_AE_EXPOSURE_COMPENSATION,
|
||||
/*defaultValue*/0);
|
||||
|
||||
@@ -192,6 +194,23 @@ public class LegacyRequestMapper {
|
||||
// control.aeMode, flash.mode
|
||||
mapAeAndFlashMode(request, /*out*/params);
|
||||
|
||||
// control.afMode
|
||||
{
|
||||
int afMode = ParamsUtils.getOrDefault(request, CONTROL_AF_MODE,
|
||||
/*defaultValue*/CONTROL_AF_MODE_OFF);
|
||||
String focusMode = LegacyMetadataMapper.convertAfModeToLegacy(afMode,
|
||||
params.getSupportedFocusModes());
|
||||
|
||||
if (focusMode != null) {
|
||||
params.setFocusMode(focusMode);
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "convertRequestToMetadata - control.afMode "
|
||||
+ afMode + " mapped to " + focusMode);
|
||||
}
|
||||
}
|
||||
|
||||
// control.awbLock
|
||||
{
|
||||
Boolean awbLock = getIfSupported(request, CONTROL_AWB_LOCK, /*defaultValue*/false,
|
||||
@@ -204,6 +223,21 @@ public class LegacyRequestMapper {
|
||||
|
||||
// TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
|
||||
}
|
||||
|
||||
// lens.focusDistance
|
||||
{
|
||||
boolean infinityFocusSupported =
|
||||
ListUtils.listContains(params.getSupportedFocusModes(),
|
||||
Parameters.FOCUS_MODE_INFINITY);
|
||||
Float focusDistance = getIfSupported(request, LENS_FOCUS_DISTANCE,
|
||||
/*defaultValue*/0f, infinityFocusSupported, /*allowedValue*/0f);
|
||||
|
||||
if (focusDistance == null || focusDistance != 0f) {
|
||||
Log.w(TAG,
|
||||
"convertRequestToMetadata - Ignoring android.lens.focusDistance "
|
||||
+ infinityFocusSupported + ", only 0.0f is supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Camera.Area> convertMeteringRegionsToLegacy(
|
||||
@@ -253,8 +287,8 @@ public class LegacyRequestMapper {
|
||||
}
|
||||
|
||||
private static void mapAeAndFlashMode(CaptureRequest r, /*out*/Parameters p) {
|
||||
int flashMode = getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
|
||||
int aeMode = getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
|
||||
int flashMode = ParamsUtils.getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
|
||||
int aeMode = ParamsUtils.getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
|
||||
|
||||
List<String> supportedFlashModes = p.getSupportedFlashModes();
|
||||
|
||||
@@ -355,20 +389,6 @@ public class LegacyRequestMapper {
|
||||
return legacyFps;
|
||||
}
|
||||
|
||||
/** Return the value set by the key, or the {@code defaultValue} if no value was set. */
|
||||
private static <T> T getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue) {
|
||||
checkNotNull(r, "r must not be null");
|
||||
checkNotNull(key, "key must not be null");
|
||||
checkNotNull(defaultValue, "defaultValue must not be null");
|
||||
|
||||
T value = r.get(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code null} if the value is not supported, otherwise return the retrieved key's
|
||||
* value from the request (or the default value if it wasn't set).
|
||||
@@ -382,7 +402,7 @@ public class LegacyRequestMapper {
|
||||
private static <T> T getIfSupported(
|
||||
CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue, boolean isSupported,
|
||||
T allowedValue) {
|
||||
T val = getOrDefault(r, key, defaultValue);
|
||||
T val = ParamsUtils.getOrDefault(r, key, defaultValue);
|
||||
|
||||
if (!isSupported) {
|
||||
if (!Objects.equals(val, allowedValue)) {
|
||||
|
||||
@@ -34,11 +34,13 @@ import android.util.Size;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.android.internal.util.Preconditions.*;
|
||||
import static android.hardware.camera2.CaptureResult.*;
|
||||
|
||||
/**
|
||||
* Provide legacy-specific implementations of camera2 CaptureResult for legacy devices.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class LegacyResultMapper {
|
||||
private static final String TAG = "LegacyResultMapper";
|
||||
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
@@ -55,22 +57,53 @@ public class LegacyResultMapper {
|
||||
*
|
||||
* @param legacyRequest a non-{@code null} legacy request containing the latest parameters
|
||||
* @param timestamp the timestamp to use for this result in nanoseconds.
|
||||
* @param frameCounter a monotonically increasing frame counter for the result
|
||||
*
|
||||
* @return {@link CameraMetadataNative} object containing result metadata.
|
||||
*/
|
||||
public CameraMetadataNative cachedConvertResultMetadata(
|
||||
LegacyRequest legacyRequest, long timestamp) {
|
||||
if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) {
|
||||
CameraMetadataNative newResult = new CameraMetadataNative(mCachedResult);
|
||||
LegacyRequest legacyRequest, long timestamp, long frameCounter) {
|
||||
CameraMetadataNative result;
|
||||
boolean cached;
|
||||
|
||||
// sensor.timestamp
|
||||
newResult.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
|
||||
return newResult;
|
||||
/*
|
||||
* Attempt to look up the result from the cache if the parameters haven't changed
|
||||
*/
|
||||
if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) {
|
||||
result = new CameraMetadataNative(mCachedResult);
|
||||
cached = true;
|
||||
} else {
|
||||
result = convertResultMetadata(legacyRequest, timestamp);
|
||||
cached = false;
|
||||
|
||||
// Always cache a *copy* of the metadata result,
|
||||
// since api2's client side takes ownership of it after it receives a result
|
||||
mCachedRequest = legacyRequest;
|
||||
mCachedResult = new CameraMetadataNative(result);
|
||||
}
|
||||
|
||||
mCachedRequest = legacyRequest;
|
||||
mCachedResult = convertResultMetadata(mCachedRequest, timestamp);
|
||||
return mCachedResult;
|
||||
/*
|
||||
* Unconditionally set fields that change in every single frame
|
||||
*/
|
||||
{
|
||||
// request.frameCounter
|
||||
result.set(REQUEST_FRAME_COUNT, (int)frameCounter);
|
||||
// TODO: fix CaptureResult#getFrameNumber not to need this key
|
||||
|
||||
// sensor.timestamp
|
||||
result.set(SENSOR_TIMESTAMP, timestamp);
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "cachedConvertResultMetadata - cached? " + cached +
|
||||
" frameCounter = " + frameCounter + " timestamp = " + timestamp);
|
||||
|
||||
Log.v(TAG, "----- beginning of result dump ------");
|
||||
result.dumpToLog();
|
||||
Log.v(TAG, "----- end of result dump ------");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +114,7 @@ public class LegacyResultMapper {
|
||||
*
|
||||
* @return a {@link CameraMetadataNative} object containing result metadata.
|
||||
*/
|
||||
public static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest,
|
||||
private static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest,
|
||||
long timestamp) {
|
||||
CameraCharacteristics characteristics = legacyRequest.characteristics;
|
||||
CaptureRequest request = legacyRequest.captureRequest;
|
||||
@@ -98,17 +131,15 @@ public class LegacyResultMapper {
|
||||
/*
|
||||
* control
|
||||
*/
|
||||
// control.afState
|
||||
if (LegacyMetadataMapper.LIE_ABOUT_AF) {
|
||||
// TODO: Implement autofocus state machine
|
||||
result.set(CaptureResult.CONTROL_AF_MODE, request.get(CaptureRequest.CONTROL_AF_MODE));
|
||||
}
|
||||
|
||||
/*
|
||||
* control.ae*
|
||||
*/
|
||||
mapAe(result, characteristics, request, activeArraySize, zoomData, /*out*/params);
|
||||
|
||||
// control.afMode
|
||||
result.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(params.getFocusMode()));
|
||||
|
||||
// control.awbLock
|
||||
result.set(CaptureResult.CONTROL_AWB_LOCK, params.getAutoWhiteBalanceLock());
|
||||
|
||||
@@ -137,9 +168,23 @@ public class LegacyResultMapper {
|
||||
/*
|
||||
* lens
|
||||
*/
|
||||
// lens.focusDistance
|
||||
{
|
||||
if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) {
|
||||
result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// lens.focalLength
|
||||
result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
|
||||
|
||||
/*
|
||||
* request
|
||||
*/
|
||||
// request.pipelineDepth
|
||||
result.set(REQUEST_PIPELINE_DEPTH,
|
||||
characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
|
||||
|
||||
/*
|
||||
* scaler
|
||||
*/
|
||||
@@ -148,8 +193,6 @@ public class LegacyResultMapper {
|
||||
/*
|
||||
* sensor
|
||||
*/
|
||||
// sensor.timestamp
|
||||
result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
|
||||
|
||||
// TODO: Remaining result metadata tags conversions.
|
||||
return result;
|
||||
@@ -301,6 +344,33 @@ public class LegacyResultMapper {
|
||||
m.set(CONTROL_AE_MODE, aeMode);
|
||||
}
|
||||
|
||||
private static int convertLegacyAfMode(String mode) {
|
||||
if (mode == null) {
|
||||
Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF");
|
||||
return CONTROL_AF_MODE_OFF;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case Parameters.FOCUS_MODE_AUTO:
|
||||
return CONTROL_AF_MODE_AUTO;
|
||||
case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
|
||||
return CONTROL_AF_MODE_CONTINUOUS_PICTURE;
|
||||
case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
|
||||
return CONTROL_AF_MODE_CONTINUOUS_VIDEO;
|
||||
case Parameters.FOCUS_MODE_EDOF:
|
||||
return CONTROL_AF_MODE_EDOF;
|
||||
case Parameters.FOCUS_MODE_MACRO:
|
||||
return CONTROL_AF_MODE_MACRO;
|
||||
case Parameters.FOCUS_MODE_FIXED:
|
||||
return CONTROL_AF_MODE_OFF;
|
||||
case Parameters.FOCUS_MODE_INFINITY:
|
||||
return CONTROL_AF_MODE_OFF;
|
||||
default:
|
||||
Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring");
|
||||
return CONTROL_AF_MODE_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
/** Map results for scaler.* */
|
||||
private static void mapScaler(CameraMetadataNative m,
|
||||
ZoomData zoomData,
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.MutableLong;
|
||||
import android.util.Pair;
|
||||
import android.util.Size;
|
||||
import android.view.Surface;
|
||||
@@ -65,6 +66,7 @@ public class RequestThreadManager {
|
||||
|
||||
private final CameraDeviceState mDeviceState;
|
||||
private final CaptureCollector mCaptureCollector;
|
||||
private final LegacyFocusStateMapper mFocusStateMapper;
|
||||
|
||||
private static final int MSG_CONFIGURE_OUTPUTS = 1;
|
||||
private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
|
||||
@@ -74,6 +76,7 @@ public class RequestThreadManager {
|
||||
|
||||
private static final int PREVIEW_FRAME_TIMEOUT = 300; // ms
|
||||
private static final int JPEG_FRAME_TIMEOUT = 3000; // ms (same as CTS for API2)
|
||||
private static final int REQUEST_COMPLETE_TIMEOUT = 3000; // ms (same as JPEG timeout)
|
||||
|
||||
private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
|
||||
private boolean mPreviewRunning = false;
|
||||
@@ -510,7 +513,7 @@ public class RequestThreadManager {
|
||||
|
||||
private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
|
||||
private boolean mCleanup = false;
|
||||
private LegacyResultMapper mMapper = new LegacyResultMapper();
|
||||
private final LegacyResultMapper mMapper = new LegacyResultMapper();
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
@@ -586,6 +589,8 @@ public class RequestThreadManager {
|
||||
CaptureRequest request = holder.getRequest();
|
||||
|
||||
boolean paramsChanged = false;
|
||||
|
||||
// Lazily process the rest of the request
|
||||
if (mLastRequest == null || mLastRequest.captureRequest != request) {
|
||||
|
||||
// The intermediate buffer is sometimes null, but we always need
|
||||
@@ -608,6 +613,10 @@ public class RequestThreadManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Unconditionally process AF triggers, since they're non-idempotent
|
||||
// - must be done after setting the most-up-to-date AF mode
|
||||
mFocusStateMapper.processRequestTriggers(request, mParams);
|
||||
|
||||
try {
|
||||
boolean success = mCaptureCollector.queueRequest(holder,
|
||||
mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
@@ -649,6 +658,27 @@ public class RequestThreadManager {
|
||||
// Update parameters to the latest that we think the camera is using
|
||||
mLastRequest.setParameters(mParams);
|
||||
}
|
||||
|
||||
MutableLong timestampMutable = new MutableLong(/*value*/0L);
|
||||
try {
|
||||
boolean success = mCaptureCollector.waitForRequestCompleted(holder,
|
||||
REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
|
||||
/*out*/timestampMutable);
|
||||
|
||||
if (!success) {
|
||||
Log.e(TAG, "Timed out while waiting for request to complete.");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// TODO: report error to CameraDevice
|
||||
Log.e(TAG, "Interrupted during request completition.", e);
|
||||
}
|
||||
|
||||
CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
|
||||
mLastRequest, timestampMutable.value, holder.getFrameNumber());
|
||||
// Update AF state
|
||||
mFocusStateMapper.mapResultTriggers(result);
|
||||
|
||||
mDeviceState.setCaptureResult(holder, result);
|
||||
}
|
||||
if (DEBUG) {
|
||||
long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
@@ -700,6 +730,7 @@ public class RequestThreadManager {
|
||||
String name = String.format("RequestThread-%d", cameraId);
|
||||
TAG = name;
|
||||
mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
|
||||
mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
|
||||
mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
|
||||
mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
|
||||
}
|
||||
|
||||
@@ -67,6 +67,36 @@ public class ArrayUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Integer> convertedList = convertStringListToIntList(list, convertFrom, convertTo);
|
||||
|
||||
int[] returnArray = new int[convertedList.size()];
|
||||
for (int i = 0; i < returnArray.length; ++i) {
|
||||
returnArray[i] = convertedList.get(i);
|
||||
}
|
||||
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code List<Integer>} from the {@code List<>} by using {@code convertFrom} and
|
||||
* {@code convertTo} as a one-to-one map (via the index).
|
||||
*
|
||||
* <p>Strings not appearing in {@code convertFrom} are ignored (with a logged warning);
|
||||
* strings appearing in {@code convertFrom} but not {@code convertTo} are silently
|
||||
* dropped.</p>
|
||||
*
|
||||
* @param list Source list of strings
|
||||
* @param convertFrom Conversion list of strings
|
||||
* @param convertTo Conversion list of ints
|
||||
* @return A list of ints where the values correspond to the ones in {@code convertTo}
|
||||
* or {@code null} if {@code list} was {@code null}
|
||||
*/
|
||||
public static List<Integer> convertStringListToIntList(
|
||||
List<String> list, String[] convertFrom, int[] convertTo) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Integer> convertedList = new ArrayList<>(list.size());
|
||||
|
||||
for (String str : list) {
|
||||
@@ -84,12 +114,33 @@ public class ArrayUtils {
|
||||
}
|
||||
}
|
||||
|
||||
int[] returnArray = new int[convertedList.size()];
|
||||
for (int i = 0; i < returnArray.length; ++i) {
|
||||
returnArray[i] = convertedList.get(i);
|
||||
return convertedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the list of integers in {@code list} to an {@code int} array.
|
||||
*
|
||||
* <p>Every element in {@code list} must be non-{@code null}.</p>
|
||||
*
|
||||
* @param list a list of non-{@code null} integers
|
||||
*
|
||||
* @return a new int array containing all the elements from {@code list}
|
||||
*
|
||||
* @throws NullPointerException if any of the elements in {@code list} were {@code null}
|
||||
*/
|
||||
public static int[] toIntArray(List<Integer> list) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return returnArray;
|
||||
int[] arr = new int[list.size()];
|
||||
int i = 0;
|
||||
for (int elem : list) {
|
||||
arr[i] = elem;
|
||||
i++;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
private ArrayUtils() {
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.hardware.camera2.utils;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.util.Rational;
|
||||
import android.util.Size;
|
||||
|
||||
@@ -175,6 +176,24 @@ public class ParamsUtils {
|
||||
destination.top = source.top;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value set by the key, or the {@code defaultValue} if no value was set.
|
||||
*
|
||||
* @throws NullPointerException if any of the args were {@code null}
|
||||
*/
|
||||
public static <T> T getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue) {
|
||||
checkNotNull(r, "r must not be null");
|
||||
checkNotNull(key, "key must not be null");
|
||||
checkNotNull(defaultValue, "defaultValue must not be null");
|
||||
|
||||
T value = r.get(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private ParamsUtils() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user