am 6a80927a: Merge "camera2: refactor LEGACY mode error handling." into lmp-dev
* commit '6a80927aef62f74a4801180adf68fe8781181060': camera2: refactor LEGACY mode error handling.
This commit is contained in:
@@ -51,7 +51,6 @@ import java.util.TreeSet;
|
||||
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
|
||||
*/
|
||||
public class CameraDeviceImpl extends CameraDevice {
|
||||
|
||||
private final String TAG;
|
||||
private final boolean DEBUG;
|
||||
|
||||
@@ -1136,7 +1135,6 @@ public class CameraDeviceImpl extends CameraDevice {
|
||||
}
|
||||
|
||||
public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
|
||||
|
||||
//
|
||||
// Constants below need to be kept up-to-date with
|
||||
// frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
|
||||
@@ -1149,34 +1147,29 @@ public class CameraDeviceImpl extends CameraDevice {
|
||||
/**
|
||||
* Camera has been disconnected
|
||||
*/
|
||||
static final int ERROR_CAMERA_DISCONNECTED = 0;
|
||||
|
||||
public static final int ERROR_CAMERA_DISCONNECTED = 0;
|
||||
/**
|
||||
* Camera has encountered a device-level error
|
||||
* Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE
|
||||
*/
|
||||
static final int ERROR_CAMERA_DEVICE = 1;
|
||||
|
||||
public static final int ERROR_CAMERA_DEVICE = 1;
|
||||
/**
|
||||
* Camera has encountered a service-level error
|
||||
* Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE
|
||||
*/
|
||||
static final int ERROR_CAMERA_SERVICE = 2;
|
||||
|
||||
public static final int ERROR_CAMERA_SERVICE = 2;
|
||||
/**
|
||||
* Camera has encountered an error processing a single request.
|
||||
*/
|
||||
static final int ERROR_CAMERA_REQUEST = 3;
|
||||
|
||||
public static final int ERROR_CAMERA_REQUEST = 3;
|
||||
/**
|
||||
* Camera has encountered an error producing metadata for a single capture
|
||||
*/
|
||||
static final int ERROR_CAMERA_RESULT = 4;
|
||||
|
||||
public static final int ERROR_CAMERA_RESULT = 4;
|
||||
/**
|
||||
* Camera has encountered an error producing an image buffer for a single capture
|
||||
*/
|
||||
static final int ERROR_CAMERA_BUFFER = 5;
|
||||
public static final int ERROR_CAMERA_BUFFER = 5;
|
||||
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
@@ -1187,9 +1180,9 @@ public class CameraDeviceImpl extends CameraDevice {
|
||||
public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format(
|
||||
"Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
|
||||
errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
|
||||
resultExtras.getSubsequenceId()));
|
||||
"Device error received, code %d, frame number %d, request ID %d, subseq ID %d",
|
||||
errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(),
|
||||
resultExtras.getSubsequenceId()));
|
||||
}
|
||||
|
||||
synchronized(mInterfaceLock) {
|
||||
|
||||
@@ -49,6 +49,9 @@ public class CameraDeviceState {
|
||||
private static final int STATE_IDLE = 3;
|
||||
private static final int STATE_CAPTURING = 4;
|
||||
|
||||
private static final String[] sStateNames = { "ERROR", "UNCONFIGURED", "CONFIGURING", "IDLE",
|
||||
"CAPTURING"};
|
||||
|
||||
private int mCurrentState = STATE_UNCONFIGURED;
|
||||
private int mCurrentError = CameraBinderDecorator.NO_ERROR;
|
||||
|
||||
@@ -57,6 +60,11 @@ public class CameraDeviceState {
|
||||
private Handler mCurrentHandler = null;
|
||||
private CameraDeviceStateListener mCurrentListener = null;
|
||||
|
||||
/**
|
||||
* Error code used by {@link #setCaptureStart} and {@link #setCaptureResult} to indicate that no
|
||||
* error has occurred.
|
||||
*/
|
||||
public static final int NO_CAPTURE_ERROR = -1;
|
||||
|
||||
/**
|
||||
* CameraDeviceStateListener callbacks to be called after state transitions.
|
||||
@@ -126,11 +134,15 @@ public class CameraDeviceState {
|
||||
*
|
||||
* @param request A {@link RequestHolder} containing the request for the current capture.
|
||||
* @param timestamp The timestamp of the capture start in nanoseconds.
|
||||
* @param captureError Report a recoverable error for a single request using a valid
|
||||
* error code for {@code ICameraDeviceCallbacks}, or
|
||||
* {@link #NO_CAPTURE_ERROR}
|
||||
* @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
|
||||
*/
|
||||
public synchronized int setCaptureStart(final RequestHolder request, long timestamp) {
|
||||
public synchronized int setCaptureStart(final RequestHolder request, long timestamp,
|
||||
int captureError) {
|
||||
mCurrentRequest = request;
|
||||
doStateTransition(STATE_CAPTURING, timestamp);
|
||||
doStateTransition(STATE_CAPTURING, timestamp, captureError);
|
||||
return mCurrentError;
|
||||
}
|
||||
|
||||
@@ -144,12 +156,16 @@ public class CameraDeviceState {
|
||||
* the {@code ERROR} state,
|
||||
* </p>
|
||||
*
|
||||
* @param request the {@link RequestHolder} request that created this result.
|
||||
* @param result the {@link CameraMetadataNative} result to set.
|
||||
* @param request The {@link RequestHolder} request that created this result.
|
||||
* @param result The {@link CameraMetadataNative} result to set.
|
||||
* @param captureError Report a recoverable error for a single buffer or result using a valid
|
||||
* error code for {@code ICameraDeviceCallbacks}, or
|
||||
* {@link #NO_CAPTURE_ERROR}.
|
||||
* @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred.
|
||||
*/
|
||||
public synchronized int setCaptureResult(final RequestHolder request,
|
||||
final CameraMetadataNative result) {
|
||||
final CameraMetadataNative result,
|
||||
final int captureError) {
|
||||
if (mCurrentState != STATE_CAPTURING) {
|
||||
Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
|
||||
mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
|
||||
@@ -158,12 +174,21 @@ public class CameraDeviceState {
|
||||
}
|
||||
|
||||
if (mCurrentHandler != null && mCurrentListener != null) {
|
||||
mCurrentHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCurrentListener.onCaptureResult(result, request);
|
||||
}
|
||||
});
|
||||
if (captureError != NO_CAPTURE_ERROR) {
|
||||
mCurrentHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCurrentListener.onError(captureError, request);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mCurrentHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCurrentListener.onCaptureResult(result, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return mCurrentError;
|
||||
}
|
||||
@@ -181,14 +206,16 @@ public class CameraDeviceState {
|
||||
}
|
||||
|
||||
private void doStateTransition(int newState) {
|
||||
doStateTransition(newState, /*timestamp*/0);
|
||||
doStateTransition(newState, /*timestamp*/0, CameraBinderDecorator.NO_ERROR);
|
||||
}
|
||||
|
||||
private void doStateTransition(int newState, final long timestamp) {
|
||||
if (DEBUG) {
|
||||
if (newState != mCurrentState) {
|
||||
Log.d(TAG, "Transitioning to state " + newState);
|
||||
private void doStateTransition(int newState, final long timestamp, final int error) {
|
||||
if (newState != mCurrentState) {
|
||||
String stateName = "UNKNOWN";
|
||||
if (newState >= 0 && newState < sStateNames.length) {
|
||||
stateName = sStateNames[newState];
|
||||
}
|
||||
Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
|
||||
}
|
||||
switch(newState) {
|
||||
case STATE_ERROR:
|
||||
@@ -251,13 +278,23 @@ public class CameraDeviceState {
|
||||
doStateTransition(STATE_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mCurrentHandler != null && mCurrentListener != null) {
|
||||
mCurrentHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCurrentListener.onCaptureStarted(mCurrentRequest, timestamp);
|
||||
}
|
||||
});
|
||||
if (error != NO_CAPTURE_ERROR) {
|
||||
mCurrentHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCurrentListener.onError(error, mCurrentRequest);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mCurrentHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCurrentListener.onCaptureStarted(mCurrentRequest, timestamp);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
mCurrentState = STATE_CAPTURING;
|
||||
break;
|
||||
|
||||
@@ -341,6 +341,10 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
Log.d(TAG, "disconnect called.");
|
||||
}
|
||||
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.w(TAG, "Cannot disconnect, device has already been closed.");
|
||||
}
|
||||
|
||||
try {
|
||||
mLegacyDevice.close();
|
||||
} finally {
|
||||
@@ -355,6 +359,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "submitRequest called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot submit request, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
synchronized(mConfigureLock) {
|
||||
if (mConfiguring) {
|
||||
Log.e(TAG, "Cannot submit request, configuration change in progress.");
|
||||
@@ -370,6 +379,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "submitRequestList called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot submit request list, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
synchronized(mConfigureLock) {
|
||||
if (mConfiguring) {
|
||||
Log.e(TAG, "Cannot submit request, configuration change in progress.");
|
||||
@@ -384,6 +398,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "cancelRequest called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot cancel request, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
synchronized(mConfigureLock) {
|
||||
if (mConfiguring) {
|
||||
Log.e(TAG, "Cannot cancel request, configuration change in progress.");
|
||||
@@ -400,6 +419,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "beginConfigure called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot begin configure, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
synchronized(mConfigureLock) {
|
||||
if (mConfiguring) {
|
||||
Log.e(TAG, "Cannot begin configure, configuration change already in progress.");
|
||||
@@ -415,6 +439,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "endConfigure called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot end configure, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
ArrayList<Surface> surfaces = null;
|
||||
synchronized(mConfigureLock) {
|
||||
if (!mConfiguring) {
|
||||
@@ -438,6 +467,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "deleteStream called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot delete stream, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
synchronized(mConfigureLock) {
|
||||
if (!mConfiguring) {
|
||||
Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet.");
|
||||
@@ -458,6 +492,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "createStream called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot create stream, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
synchronized(mConfigureLock) {
|
||||
if (!mConfiguring) {
|
||||
Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet.");
|
||||
@@ -474,6 +513,10 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "createDefaultRequest called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot create default request, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
CameraMetadataNative template;
|
||||
try {
|
||||
@@ -503,6 +546,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "waitUntilIdle called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot wait until idle, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
synchronized(mConfigureLock) {
|
||||
if (mConfiguring) {
|
||||
Log.e(TAG, "Cannot wait until idle, configuration change in progress.");
|
||||
@@ -518,13 +566,21 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "flush called.");
|
||||
}
|
||||
if (mLegacyDevice.isClosed()) {
|
||||
Log.e(TAG, "Cannot flush, device has been closed.");
|
||||
return CameraBinderDecorator.ENODEV;
|
||||
}
|
||||
|
||||
synchronized(mConfigureLock) {
|
||||
if (mConfiguring) {
|
||||
Log.e(TAG, "Cannot flush, configuration change in progress.");
|
||||
return CameraBinderDecorator.INVALID_OPERATION;
|
||||
}
|
||||
}
|
||||
// TODO: implement flush.
|
||||
long lastFrame = mLegacyDevice.flush();
|
||||
if (lastFrameNumber != null) {
|
||||
lastFrameNumber.setNumber(lastFrame);
|
||||
}
|
||||
return CameraBinderDecorator.NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
*/
|
||||
package android.hardware.camera2.legacy;
|
||||
|
||||
import android.hardware.camera2.impl.CameraDeviceImpl;
|
||||
import android.util.Log;
|
||||
import android.util.MutableLong;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -44,7 +46,7 @@ public class CaptureCollector {
|
||||
|
||||
private static final int MAX_JPEGS_IN_FLIGHT = 1;
|
||||
|
||||
private class CaptureHolder {
|
||||
private class CaptureHolder implements Comparable<CaptureHolder>{
|
||||
private final RequestHolder mRequest;
|
||||
private final LegacyRequest mLegacy;
|
||||
public final boolean needsJpeg;
|
||||
@@ -53,6 +55,10 @@ public class CaptureCollector {
|
||||
private long mTimestamp = 0;
|
||||
private int mReceivedFlags = 0;
|
||||
private boolean mHasStarted = false;
|
||||
private boolean mFailedJpeg = false;
|
||||
private boolean mFailedPreview = false;
|
||||
private boolean mCompleted = false;
|
||||
private boolean mPreviewCompleted = false;
|
||||
|
||||
public CaptureHolder(RequestHolder request, LegacyRequest legacyHolder) {
|
||||
mRequest = request;
|
||||
@@ -74,11 +80,43 @@ public class CaptureCollector {
|
||||
}
|
||||
|
||||
public void tryComplete() {
|
||||
if (isCompleted()) {
|
||||
if (needsPreview && isPreviewCompleted()) {
|
||||
CaptureCollector.this.onPreviewCompleted();
|
||||
if (!mPreviewCompleted && needsPreview && isPreviewCompleted()) {
|
||||
CaptureCollector.this.onPreviewCompleted();
|
||||
mPreviewCompleted = true;
|
||||
}
|
||||
|
||||
if (isCompleted() && !mCompleted) {
|
||||
if (mFailedPreview || mFailedJpeg) {
|
||||
if (!mHasStarted) {
|
||||
// Send a request error if the capture has not yet started.
|
||||
mRequest.failRequest();
|
||||
CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_REQUEST);
|
||||
} else {
|
||||
// Send buffer dropped errors for each pending buffer if the request has
|
||||
// started.
|
||||
if (mFailedPreview) {
|
||||
Log.w(TAG, "Preview buffers dropped for request: " +
|
||||
mRequest.getRequestId());
|
||||
for (int i = 0; i < mRequest.numPreviewTargets(); i++) {
|
||||
CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
|
||||
/*result*/null,
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER);
|
||||
}
|
||||
}
|
||||
if (mFailedJpeg) {
|
||||
Log.w(TAG, "Jpeg buffers dropped for request: " +
|
||||
mRequest.getRequestId());
|
||||
for (int i = 0; i < mRequest.numJpegTargets(); i++) {
|
||||
CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
|
||||
/*result*/null,
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CaptureCollector.this.onRequestCompleted(this);
|
||||
CaptureCollector.this.onRequestCompleted(CaptureHolder.this);
|
||||
mCompleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +141,8 @@ public class CaptureCollector {
|
||||
|
||||
if (!mHasStarted) {
|
||||
mHasStarted = true;
|
||||
CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp);
|
||||
CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
|
||||
CameraDeviceState.NO_CAPTURE_ERROR);
|
||||
}
|
||||
|
||||
tryComplete();
|
||||
@@ -126,6 +165,20 @@ public class CaptureCollector {
|
||||
tryComplete();
|
||||
}
|
||||
|
||||
public void setJpegFailed() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setJpegFailed - called for request " + mRequest.getRequestId());
|
||||
}
|
||||
if (!needsJpeg || isJpegCompleted()) {
|
||||
return;
|
||||
}
|
||||
mFailedJpeg = true;
|
||||
|
||||
mReceivedFlags |= FLAG_RECEIVED_JPEG;
|
||||
mReceivedFlags |= FLAG_RECEIVED_JPEG_TS;
|
||||
tryComplete();
|
||||
}
|
||||
|
||||
public void setPreviewTimestamp(long timestamp) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setPreviewTimestamp - called for request " + mRequest.getRequestId());
|
||||
@@ -148,7 +201,8 @@ public class CaptureCollector {
|
||||
if (!needsJpeg) {
|
||||
if (!mHasStarted) {
|
||||
mHasStarted = true;
|
||||
CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp);
|
||||
CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
|
||||
CameraDeviceState.NO_CAPTURE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,8 +225,37 @@ public class CaptureCollector {
|
||||
mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
|
||||
tryComplete();
|
||||
}
|
||||
|
||||
public void setPreviewFailed() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setPreviewFailed - called for request " + mRequest.getRequestId());
|
||||
}
|
||||
if (!needsPreview || isPreviewCompleted()) {
|
||||
return;
|
||||
}
|
||||
mFailedPreview = true;
|
||||
|
||||
mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
|
||||
mReceivedFlags |= FLAG_RECEIVED_PREVIEW_TS;
|
||||
tryComplete();
|
||||
}
|
||||
|
||||
// Comparison and equals based on frame number.
|
||||
@Override
|
||||
public int compareTo(CaptureHolder captureHolder) {
|
||||
return (mRequest.getFrameNumber() > captureHolder.mRequest.getFrameNumber()) ? 1 :
|
||||
((mRequest.getFrameNumber() == captureHolder.mRequest.getFrameNumber()) ? 0 :
|
||||
-1);
|
||||
}
|
||||
|
||||
// Comparison and equals based on frame number.
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof CaptureHolder && compareTo((CaptureHolder) o) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
private final TreeSet<CaptureHolder> mActiveRequests;
|
||||
private final ArrayDeque<CaptureHolder> mJpegCaptureQueue;
|
||||
private final ArrayDeque<CaptureHolder> mJpegProduceQueue;
|
||||
private final ArrayDeque<CaptureHolder> mPreviewCaptureQueue;
|
||||
@@ -200,6 +283,7 @@ public class CaptureCollector {
|
||||
mJpegProduceQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
|
||||
mPreviewCaptureQueue = new ArrayDeque<>(mMaxInFlight);
|
||||
mPreviewProduceQueue = new ArrayDeque<>(mMaxInFlight);
|
||||
mActiveRequests = new TreeSet<>();
|
||||
mIsEmpty = mLock.newCondition();
|
||||
mNotFull = mLock.newCondition();
|
||||
mPreviewsEmpty = mLock.newCondition();
|
||||
@@ -263,7 +347,7 @@ public class CaptureCollector {
|
||||
mPreviewProduceQueue.add(h);
|
||||
mInFlightPreviews++;
|
||||
}
|
||||
|
||||
mActiveRequests.add(h);
|
||||
|
||||
mInFlight++;
|
||||
return true;
|
||||
@@ -440,7 +524,9 @@ public class CaptureCollector {
|
||||
try {
|
||||
CaptureHolder h = mPreviewCaptureQueue.poll();
|
||||
if (h == null) {
|
||||
Log.w(TAG, "previewCaptured called with no preview request on queue!");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "previewCaptured called with no preview request on queue!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
h.setPreviewTimestamp(timestamp);
|
||||
@@ -471,6 +557,81 @@ public class CaptureCollector {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to alert the {@link CaptureCollector} that the next pending preview capture has failed.
|
||||
*/
|
||||
public void failNextPreview() {
|
||||
final ReentrantLock lock = this.mLock;
|
||||
lock.lock();
|
||||
try {
|
||||
CaptureHolder h1 = mPreviewCaptureQueue.peek();
|
||||
CaptureHolder h2 = mPreviewProduceQueue.peek();
|
||||
|
||||
// Find the request with the lowest frame number.
|
||||
CaptureHolder h = (h1 == null) ? h2 :
|
||||
((h2 == null) ? h1 :
|
||||
((h1.compareTo(h2) <= 0) ? h1 :
|
||||
h2));
|
||||
|
||||
if (h != null) {
|
||||
mPreviewCaptureQueue.remove(h);
|
||||
mPreviewProduceQueue.remove(h);
|
||||
mActiveRequests.remove(h);
|
||||
h.setPreviewFailed();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to alert the {@link CaptureCollector} that the next pending jpeg capture has failed.
|
||||
*/
|
||||
public void failNextJpeg() {
|
||||
final ReentrantLock lock = this.mLock;
|
||||
lock.lock();
|
||||
try {
|
||||
CaptureHolder h1 = mJpegCaptureQueue.peek();
|
||||
CaptureHolder h2 = mJpegProduceQueue.peek();
|
||||
|
||||
// Find the request with the lowest frame number.
|
||||
CaptureHolder h = (h1 == null) ? h2 :
|
||||
((h2 == null) ? h1 :
|
||||
((h1.compareTo(h2) <= 0) ? h1 :
|
||||
h2));
|
||||
|
||||
if (h != null) {
|
||||
mJpegCaptureQueue.remove(h);
|
||||
mJpegProduceQueue.remove(h);
|
||||
mActiveRequests.remove(h);
|
||||
h.setJpegFailed();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to alert the {@link CaptureCollector} all pending captures have failed.
|
||||
*/
|
||||
public void failAll() {
|
||||
final ReentrantLock lock = this.mLock;
|
||||
lock.lock();
|
||||
try {
|
||||
CaptureHolder h;
|
||||
while ((h = mActiveRequests.pollFirst()) != null) {
|
||||
h.setPreviewFailed();
|
||||
h.setJpegFailed();
|
||||
}
|
||||
mPreviewCaptureQueue.clear();
|
||||
mPreviewProduceQueue.clear();
|
||||
mJpegCaptureQueue.clear();
|
||||
mJpegProduceQueue.clear();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void onPreviewCompleted() {
|
||||
mInFlightPreviews--;
|
||||
if (mInFlightPreviews < 0) {
|
||||
@@ -496,6 +657,7 @@ public class CaptureCollector {
|
||||
}
|
||||
|
||||
mCompletedRequests.add(capture);
|
||||
mActiveRequests.remove(capture);
|
||||
|
||||
mNotFull.signalAll();
|
||||
if (mInFlight == 0) {
|
||||
|
||||
@@ -23,6 +23,9 @@ import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.impl.CaptureResultExtras;
|
||||
import android.hardware.camera2.ICameraDeviceCallbacks;
|
||||
import android.hardware.camera2.params.StreamConfiguration;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.hardware.camera2.utils.ArrayUtils;
|
||||
import android.hardware.camera2.utils.LongParcelable;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
import android.hardware.camera2.utils.CameraRuntimeException;
|
||||
@@ -35,6 +38,7 @@ import android.util.Size;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@@ -56,11 +60,13 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
public static final String DEBUG_PROP = "HAL1ShimLogging";
|
||||
private final String TAG;
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
|
||||
private final int mCameraId;
|
||||
private final CameraCharacteristics mStaticCharacteristics;
|
||||
private final ICameraDeviceCallbacks mDeviceCallbacks;
|
||||
private final CameraDeviceState mDeviceState = new CameraDeviceState();
|
||||
private List<Surface> mConfiguredSurfaces;
|
||||
private boolean mClosed = false;
|
||||
|
||||
private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
|
||||
|
||||
@@ -87,14 +93,15 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
private final CameraDeviceState.CameraDeviceStateListener mStateListener =
|
||||
new CameraDeviceState.CameraDeviceStateListener() {
|
||||
@Override
|
||||
public void onError(final int errorCode, RequestHolder holder) {
|
||||
public void onError(final int errorCode, final RequestHolder holder) {
|
||||
mIdle.open();
|
||||
final CaptureResultExtras extras = getExtrasFromRequest(holder);
|
||||
mResultHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "doing onError callback.");
|
||||
Log.d(TAG, "doing onError callback for request " + holder.getRequestId() +
|
||||
", with error code " + errorCode);
|
||||
}
|
||||
try {
|
||||
mDeviceCallbacks.onDeviceError(errorCode, extras);
|
||||
@@ -135,14 +142,15 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureStarted(RequestHolder holder, final long timestamp) {
|
||||
public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
|
||||
final CaptureResultExtras extras = getExtrasFromRequest(holder);
|
||||
|
||||
mResultHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "doing onCaptureStarted callback.");
|
||||
Log.d(TAG, "doing onCaptureStarted callback for request " +
|
||||
holder.getRequestId());
|
||||
}
|
||||
try {
|
||||
mDeviceCallbacks.onCaptureStarted(extras, timestamp);
|
||||
@@ -155,14 +163,15 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureResult(final CameraMetadataNative result, RequestHolder holder) {
|
||||
public void onCaptureResult(final CameraMetadataNative result, final RequestHolder holder) {
|
||||
final CaptureResultExtras extras = getExtrasFromRequest(holder);
|
||||
|
||||
mResultHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "doing onCaptureResult callback.");
|
||||
Log.d(TAG, "doing onCaptureResult callback for request " +
|
||||
holder.getRequestId());
|
||||
}
|
||||
try {
|
||||
mDeviceCallbacks.onResultReceived(result, extras);
|
||||
@@ -216,6 +225,7 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
mCallbackHandlerThread.start();
|
||||
mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
|
||||
mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
|
||||
mStaticCharacteristics = characteristics;
|
||||
mRequestThreadManager =
|
||||
new RequestThreadManager(cameraId, camera, characteristics, mDeviceState);
|
||||
mRequestThreadManager.start();
|
||||
@@ -239,6 +249,42 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
Log.e(TAG, "configureOutputs - null outputs are not allowed");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
StreamConfigurationMap streamConfigurations = mStaticCharacteristics.
|
||||
get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||
|
||||
// Validate surface size and format.
|
||||
try {
|
||||
Size s = getSurfaceSize(output);
|
||||
int surfaceType = detectSurfaceType(output);
|
||||
Size[] sizes = streamConfigurations.getOutputSizes(surfaceType);
|
||||
|
||||
if (sizes == null) {
|
||||
// WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482
|
||||
if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 &&
|
||||
surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) {
|
||||
|
||||
// YUV_420_888 is always present in LEGACY for all IMPLEMENTATION_DEFINED
|
||||
// output sizes, and is publicly visible in the API (i.e.
|
||||
// {@code #getOutputSizes} works here).
|
||||
sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888);
|
||||
} else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) {
|
||||
sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ArrayUtils.contains(sizes, s)) {
|
||||
String reason = (sizes == null) ? "format is invalid." :
|
||||
("size not in valid set: " + Arrays.toString(sizes));
|
||||
Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format 0x%x is"
|
||||
+ " not valid, %s", s.getWidth(), s.getHeight(), surfaceType,
|
||||
reason));
|
||||
return BAD_VALUE;
|
||||
}
|
||||
} catch (BufferQueueAbandonedException e) {
|
||||
Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +294,6 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
error = mDeviceState.setIdle();
|
||||
}
|
||||
|
||||
// TODO: May also want to check the surfaces more deeply (e.g. state, formats, sizes..)
|
||||
if (error == NO_ERROR) {
|
||||
mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
|
||||
}
|
||||
@@ -342,6 +387,24 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
mIdle.block();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush any pending requests.
|
||||
*
|
||||
* @return the last frame number.
|
||||
*/
|
||||
public long flush() {
|
||||
long lastFrame = mRequestThreadManager.flush();
|
||||
waitUntilIdle();
|
||||
return lastFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the device has been closed.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return mClosed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mRequestThreadManager.quit();
|
||||
@@ -362,7 +425,7 @@ public class LegacyCameraDevice implements AutoCloseable {
|
||||
mResultThread.getName(), mResultThread.getId()));
|
||||
}
|
||||
|
||||
// TODO: throw IllegalStateException in every method after close has been called
|
||||
mClosed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.hardware.camera2.legacy;
|
||||
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Camera.CameraInfo;
|
||||
@@ -61,8 +62,10 @@ public class LegacyMetadataMapper {
|
||||
private static final long NS_PER_MS = 1000000;
|
||||
|
||||
// from graphics.h
|
||||
private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
|
||||
private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
|
||||
public static final int HAL_PIXEL_FORMAT_RGBA_8888 = PixelFormat.RGBA_8888;
|
||||
public static final int HAL_PIXEL_FORMAT_BGRA_8888 = 0x5;
|
||||
public static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
|
||||
public static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
|
||||
|
||||
// for metadata
|
||||
private static final float LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS = 0.0f;
|
||||
@@ -1170,7 +1173,7 @@ public class LegacyMetadataMapper {
|
||||
Rect activeArray = c.get(SENSOR_INFO_ACTIVE_ARRAY_SIZE);
|
||||
MeteringRectangle[] activeRegions = new MeteringRectangle[] {
|
||||
new MeteringRectangle(/*x*/0, /*y*/0, /*width*/activeArray.width() - 1,
|
||||
/*height*/activeArray.height() - 1,/*weight*/1)};
|
||||
/*height*/activeArray.height() - 1,/*weight*/0)};
|
||||
m.set(CaptureRequest.CONTROL_AE_REGIONS, activeRegions);
|
||||
m.set(CaptureRequest.CONTROL_AWB_REGIONS, activeRegions);
|
||||
m.set(CaptureRequest.CONTROL_AF_REGIONS, activeRegions);
|
||||
@@ -1274,6 +1277,11 @@ public class LegacyMetadataMapper {
|
||||
// flash.mode
|
||||
m.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF);
|
||||
|
||||
/*
|
||||
* noiseReduction.*
|
||||
*/
|
||||
m.set(CaptureRequest.NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_FAST);
|
||||
|
||||
/*
|
||||
* lens.*
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,9 @@ import java.util.Collection;
|
||||
import static com.android.internal.util.Preconditions.*;
|
||||
|
||||
/**
|
||||
* Immutable container for a single capture request and associated information.
|
||||
* Semi-immutable container for a single capture request and associated information,
|
||||
* the only mutable characteristic of this container is whether or not is has been
|
||||
* marked as "failed" using {@code #failRequest}.
|
||||
*/
|
||||
public class RequestHolder {
|
||||
private static final String TAG = "RequestHolder";
|
||||
@@ -36,8 +38,9 @@ public class RequestHolder {
|
||||
private final int mRequestId;
|
||||
private final int mSubsequeceId;
|
||||
private final long mFrameNumber;
|
||||
private final boolean mHasJpegTargets;
|
||||
private final boolean mHasPreviewTargets;
|
||||
private final int mNumJpegTargets;
|
||||
private final int mNumPreviewTargets;
|
||||
private volatile boolean mFailed = false;
|
||||
|
||||
/**
|
||||
* Returns true if the given surface requires jpeg buffers.
|
||||
@@ -71,36 +74,37 @@ public class RequestHolder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any of the surfaces targeted by the contained request require jpeg buffers.
|
||||
* Returns the number of surfaces targeted by the request that require jpeg buffers.
|
||||
*/
|
||||
private static boolean requestContainsJpegTargets(CaptureRequest request) {
|
||||
private static int numJpegTargets(CaptureRequest request) {
|
||||
int count = 0;
|
||||
for (Surface s : request.getTargets()) {
|
||||
try {
|
||||
if (jpegType(s)) {
|
||||
return true;
|
||||
++count;
|
||||
}
|
||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any of the surfaces targeted by the contained request require a
|
||||
* non-jpeg buffer type.
|
||||
* Returns the number of surfaces targeted by the request that require non-jpeg buffers.
|
||||
*/
|
||||
private static boolean requestContainsPreviewTargets(CaptureRequest request) {
|
||||
private static int numPreviewTargets(CaptureRequest request) {
|
||||
int count = 0;
|
||||
for (Surface s : request.getTargets()) {
|
||||
try {
|
||||
if (previewType(s)) {
|
||||
return true;
|
||||
++count;
|
||||
}
|
||||
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
|
||||
Log.w(TAG, "Surface abandoned, skipping...", e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,8 +119,8 @@ public class RequestHolder {
|
||||
private final int mSubsequenceId;
|
||||
private final CaptureRequest mRequest;
|
||||
private final boolean mRepeating;
|
||||
private final boolean mHasJpegTargets;
|
||||
private final boolean mHasPreviewTargets;
|
||||
private final int mNumJpegTargets;
|
||||
private final int mNumPreviewTargets;
|
||||
|
||||
/**
|
||||
* Construct a new {@link Builder} to generate {@link RequestHolder} objects.
|
||||
@@ -134,8 +138,8 @@ public class RequestHolder {
|
||||
mSubsequenceId = subsequenceId;
|
||||
mRequest = request;
|
||||
mRepeating = repeating;
|
||||
mHasJpegTargets = requestContainsJpegTargets(mRequest);
|
||||
mHasPreviewTargets = requestContainsPreviewTargets(mRequest);
|
||||
mNumJpegTargets = numJpegTargets(mRequest);
|
||||
mNumPreviewTargets = numPreviewTargets(mRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,20 +151,20 @@ public class RequestHolder {
|
||||
*/
|
||||
public RequestHolder build(long frameNumber) {
|
||||
return new RequestHolder(mRequestId, mSubsequenceId, mRequest, mRepeating, frameNumber,
|
||||
mHasJpegTargets, mHasPreviewTargets);
|
||||
mNumJpegTargets, mNumPreviewTargets);
|
||||
}
|
||||
}
|
||||
|
||||
private RequestHolder(int requestId, int subsequenceId, CaptureRequest request,
|
||||
boolean repeating, long frameNumber, boolean hasJpegTargets,
|
||||
boolean hasPreviewTargets) {
|
||||
boolean repeating, long frameNumber, int numJpegTargets,
|
||||
int numPreviewTargets) {
|
||||
mRepeating = repeating;
|
||||
mRequest = request;
|
||||
mRequestId = requestId;
|
||||
mSubsequeceId = subsequenceId;
|
||||
mFrameNumber = frameNumber;
|
||||
mHasJpegTargets = hasJpegTargets;
|
||||
mHasPreviewTargets = hasPreviewTargets;
|
||||
mNumJpegTargets = numJpegTargets;
|
||||
mNumPreviewTargets = numPreviewTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +213,7 @@ public class RequestHolder {
|
||||
* Returns true if any of the surfaces targeted by the contained request require jpeg buffers.
|
||||
*/
|
||||
public boolean hasJpegTargets() {
|
||||
return mHasJpegTargets;
|
||||
return mNumJpegTargets > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,7 +221,36 @@ public class RequestHolder {
|
||||
* non-jpeg buffer type.
|
||||
*/
|
||||
public boolean hasPreviewTargets(){
|
||||
return mHasPreviewTargets;
|
||||
return mNumPreviewTargets > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of jpeg-type surfaces targeted by this request.
|
||||
*/
|
||||
public int numJpegTargets() {
|
||||
return mNumJpegTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of non-jpeg-type surfaces targeted by this request.
|
||||
*/
|
||||
public int numPreviewTargets() {
|
||||
return mNumPreviewTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this request as failed.
|
||||
*/
|
||||
public void failRequest() {
|
||||
Log.w(TAG, "Capture failed for request: " + getRequestId());
|
||||
mFailed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if this request failed.
|
||||
*/
|
||||
public boolean requestFailed() {
|
||||
return mFailed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -80,12 +80,27 @@ public class RequestQueue {
|
||||
ret = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
|
||||
mCurrentRepeatingFrameNumber - 1;
|
||||
mCurrentRepeatingFrameNumber = INVALID_FRAME;
|
||||
Log.i(TAG, "Repeating capture request cancelled.");
|
||||
} else {
|
||||
Log.e(TAG, "cancel failed: no repeating request exists for request id: " + requestId);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a repeating request.
|
||||
*
|
||||
* @return the last frame to be returned from the HAL for the given repeating request, or
|
||||
* {@code INVALID_FRAME} if none exists.
|
||||
*/
|
||||
public synchronized long stopRepeating() {
|
||||
if (mRepeatingRequest == null) {
|
||||
Log.e(TAG, "cancel failed: no repeating request exists.");
|
||||
return INVALID_FRAME;
|
||||
}
|
||||
return stopRepeating(mRepeatingRequest.getRequestId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a the given burst to the queue.
|
||||
*
|
||||
@@ -105,6 +120,7 @@ public class RequestQueue {
|
||||
BurstHolder burst = new BurstHolder(requestId, repeating, requests);
|
||||
long ret = INVALID_FRAME;
|
||||
if (burst.isRepeating()) {
|
||||
Log.i(TAG, "Repeating capture request set.");
|
||||
if (mRepeatingRequest != null) {
|
||||
ret = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
|
||||
mCurrentRepeatingFrameNumber - 1;
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.graphics.SurfaceTexture;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.impl.CameraDeviceImpl;
|
||||
import android.hardware.camera2.utils.LongParcelable;
|
||||
import android.hardware.camera2.utils.SizeAreaComparator;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
@@ -33,7 +34,6 @@ import android.util.Pair;
|
||||
import android.util.Size;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -284,10 +284,9 @@ public class RequestThreadManager {
|
||||
startPreview();
|
||||
}
|
||||
|
||||
private void configureOutputs(Collection<Surface> outputs) throws IOException {
|
||||
private void configureOutputs(Collection<Surface> outputs) {
|
||||
if (DEBUG) {
|
||||
String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
|
||||
|
||||
Log.d(TAG, "configureOutputs with " + outputsStr);
|
||||
}
|
||||
|
||||
@@ -297,7 +296,11 @@ public class RequestThreadManager {
|
||||
* using a different one; this also reduces the likelihood of getting into a deadlock
|
||||
* when disconnecting from the old previous texture at a later time.
|
||||
*/
|
||||
mCamera.setPreviewTexture(/*surfaceTexture*/null);
|
||||
try {
|
||||
mCamera.setPreviewTexture(/*surfaceTexture*/null);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
|
||||
}
|
||||
|
||||
if (mGLThreadManager != null) {
|
||||
mGLThreadManager.waitUntilStarted();
|
||||
@@ -568,26 +571,23 @@ public class RequestThreadManager {
|
||||
case MSG_CONFIGURE_OUTPUTS:
|
||||
ConfigureHolder config = (ConfigureHolder) msg.obj;
|
||||
int sizes = config.surfaces != null ? config.surfaces.size() : 0;
|
||||
Log.i(TAG, "Configure outputs: " + sizes +
|
||||
" surfaces configured.");
|
||||
Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
|
||||
|
||||
try {
|
||||
boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
|
||||
TimeUnit.MILLISECONDS);
|
||||
if (!success) {
|
||||
Log.e(TAG, "Timed out while queueing configure request.");
|
||||
mCaptureCollector.failAll();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// TODO: report error to CameraDevice
|
||||
Log.e(TAG, "Interrupted while waiting for requests to complete.");
|
||||
mDeviceState.setError(
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
configureOutputs(config.surfaces);
|
||||
} catch (IOException e) {
|
||||
// TODO: report error to CameraDevice
|
||||
throw new IOError(e);
|
||||
}
|
||||
configureOutputs(config.surfaces);
|
||||
config.condition.open();
|
||||
if (DEBUG) {
|
||||
long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
@@ -599,16 +599,23 @@ public class RequestThreadManager {
|
||||
|
||||
// Get the next burst from the request queue.
|
||||
Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
|
||||
|
||||
if (nextBurst == null) {
|
||||
// If there are no further requests queued, wait for any currently executing
|
||||
// requests to complete, then switch to idle state.
|
||||
try {
|
||||
boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
|
||||
TimeUnit.MILLISECONDS);
|
||||
if (!success) {
|
||||
Log.e(TAG, "Timed out while waiting for empty.");
|
||||
Log.e(TAG,
|
||||
"Timed out while waiting for prior requests to complete.");
|
||||
mCaptureCollector.failAll();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// TODO: report error to CameraDevice
|
||||
Log.e(TAG, "Interrupted while waiting for requests to complete.");
|
||||
Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
|
||||
mDeviceState.setError(
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
|
||||
break;
|
||||
}
|
||||
mDeviceState.setIdle();
|
||||
break;
|
||||
@@ -625,27 +632,39 @@ public class RequestThreadManager {
|
||||
|
||||
boolean paramsChanged = false;
|
||||
|
||||
// Lazily process the rest of the request
|
||||
// Only update parameters if the request has changed
|
||||
if (mLastRequest == null || mLastRequest.captureRequest != request) {
|
||||
|
||||
// The intermediate buffer is sometimes null, but we always need
|
||||
// the camera1's configured preview size
|
||||
// the Camera1 API configured preview size
|
||||
Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
|
||||
|
||||
LegacyRequest legacyRequest = new LegacyRequest(
|
||||
mCharacteristics, request, previewSize,
|
||||
mParams); // params are copied
|
||||
LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
|
||||
request, previewSize, mParams); // params are copied
|
||||
|
||||
|
||||
mLastRequest = legacyRequest;
|
||||
// Parameters are mutated as a side-effect
|
||||
LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
|
||||
|
||||
// If the parameters have changed, set them in the Camera1 API.
|
||||
if (!mParams.same(legacyRequest.parameters)) {
|
||||
mParams = legacyRequest.parameters;
|
||||
mCamera.setParameters(mParams);
|
||||
|
||||
try {
|
||||
mCamera.setParameters(legacyRequest.parameters);
|
||||
} catch (RuntimeException e) {
|
||||
// If setting the parameters failed, report a request error to
|
||||
// the camera client, and skip any further work for this request
|
||||
Log.e(TAG, "Exception while setting camera parameters: ", e);
|
||||
holder.failRequest();
|
||||
mDeviceState.setCaptureStart(holder, /*timestamp*/0,
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.
|
||||
ERROR_CAMERA_REQUEST);
|
||||
continue;
|
||||
}
|
||||
paramsChanged = true;
|
||||
mParams = legacyRequest.parameters;
|
||||
}
|
||||
|
||||
mLastRequest = legacyRequest;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -653,19 +672,27 @@ public class RequestThreadManager {
|
||||
mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
|
||||
if (!success) {
|
||||
// Report a request error if we timed out while queuing this.
|
||||
Log.e(TAG, "Timed out while queueing capture request.");
|
||||
holder.failRequest();
|
||||
mDeviceState.setCaptureStart(holder, /*timestamp*/0,
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.
|
||||
ERROR_CAMERA_REQUEST);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Starting the preview needs to happen before enabling
|
||||
// face detection or auto focus
|
||||
if (holder.hasPreviewTargets()) {
|
||||
doPreviewCapture(holder);
|
||||
}
|
||||
if (holder.hasJpegTargets()) {
|
||||
success = mCaptureCollector.
|
||||
waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT *
|
||||
MAX_IN_FLIGHT_REQUESTS, TimeUnit.MILLISECONDS);
|
||||
if (!success) {
|
||||
Log.e(TAG, "Timed out waiting for prior requests to complete.");
|
||||
while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
|
||||
TimeUnit.MILLISECONDS)) {
|
||||
// Fail preview requests until the queue is empty.
|
||||
Log.e(TAG, "Timed out while waiting for preview requests to " +
|
||||
"complete.");
|
||||
mCaptureCollector.failNextPreview();
|
||||
}
|
||||
mReceivedJpeg.close();
|
||||
doJpegCapturePrepare(holder);
|
||||
@@ -686,17 +713,21 @@ public class RequestThreadManager {
|
||||
if (holder.hasJpegTargets()) {
|
||||
doJpegCapture(holder);
|
||||
if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
|
||||
// TODO: report error to CameraDevice
|
||||
Log.e(TAG, "Hit timeout for jpeg callback!");
|
||||
mCaptureCollector.failNextJpeg();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
// TODO: report error to CameraDevice
|
||||
throw new IOError(e);
|
||||
Log.e(TAG, "Received device exception: ", e);
|
||||
mDeviceState.setError(
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
|
||||
break;
|
||||
} catch (InterruptedException e) {
|
||||
// TODO: report error to CameraDevice
|
||||
Log.e(TAG, "Interrupted during capture.", e);
|
||||
Log.e(TAG, "Interrupted during capture: ", e);
|
||||
mDeviceState.setError(
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (paramsChanged) {
|
||||
@@ -717,10 +748,13 @@ public class RequestThreadManager {
|
||||
|
||||
if (!success) {
|
||||
Log.e(TAG, "Timed out while waiting for request to complete.");
|
||||
mCaptureCollector.failAll();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// TODO: report error to CameraDevice
|
||||
Log.e(TAG, "Interrupted during request completition.", e);
|
||||
Log.e(TAG, "Interrupted waiting for request completion: ", e);
|
||||
mDeviceState.setError(
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
|
||||
break;
|
||||
}
|
||||
|
||||
CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
|
||||
@@ -736,7 +770,10 @@ public class RequestThreadManager {
|
||||
// Update face-related results
|
||||
mFaceDetectMapper.mapResultFaces(result, mLastRequest);
|
||||
|
||||
mDeviceState.setCaptureResult(holder, result);
|
||||
if (!holder.requestFailed()) {
|
||||
mDeviceState.setCaptureResult(holder, result,
|
||||
CameraDeviceState.NO_CAPTURE_ERROR);
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
@@ -751,10 +788,12 @@ public class RequestThreadManager {
|
||||
TimeUnit.MILLISECONDS);
|
||||
if (!success) {
|
||||
Log.e(TAG, "Timed out while queueing cleanup request.");
|
||||
mCaptureCollector.failAll();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// TODO: report error to CameraDevice
|
||||
Log.e(TAG, "Interrupted while waiting for requests to complete.");
|
||||
Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
|
||||
mDeviceState.setError(
|
||||
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
|
||||
}
|
||||
if (mGLThreadManager != null) {
|
||||
mGLThreadManager.quit();
|
||||
@@ -802,11 +841,15 @@ public class RequestThreadManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the pending requests.
|
||||
* Flush any pending requests.
|
||||
*
|
||||
* @return the last frame number.
|
||||
*/
|
||||
public void flush() {
|
||||
// TODO: Implement flush.
|
||||
Log.e(TAG, "flush not yet implemented.");
|
||||
public long flush() {
|
||||
Log.i(TAG, "Flushing all pending requests.");
|
||||
long lastFrame = mRequestQueue.stopRepeating();
|
||||
mCaptureCollector.failAll();
|
||||
return lastFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -856,7 +899,6 @@ public class RequestThreadManager {
|
||||
return mRequestQueue.stopRepeating(requestId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure with the current list of output Surfaces.
|
||||
*
|
||||
|
||||
@@ -652,7 +652,9 @@ public class SurfaceTextureRenderer {
|
||||
|
||||
// No preview request queued, drop frame.
|
||||
if (captureHolder == null) {
|
||||
Log.w(TAG, "Dropping preview frame.");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Dropping preview frame.");
|
||||
}
|
||||
if (doTiming) {
|
||||
endGlTiming();
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ArrayUtils {
|
||||
|
||||
/** Return the index of {@code needle} in the {@code array}, or else {@code -1} */
|
||||
public static <T> int getArrayIndex(T[] array, T needle) {
|
||||
if (needle == null) {
|
||||
if (array == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -167,6 +167,17 @@ public class ArrayUtils {
|
||||
return getArrayIndex(array, elem) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given {@code array} contains the given element.
|
||||
*
|
||||
* @param array {@code array} to check for {@code elem}
|
||||
* @param elem {@code elem} to test for
|
||||
* @return {@code true} if the given element is contained
|
||||
*/
|
||||
public static <T> boolean contains(T[] array, T elem) {
|
||||
return getArrayIndex(array, elem) != -1;
|
||||
}
|
||||
|
||||
private ArrayUtils() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user