summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuben Brunk <rubenbrunk@google.com>2014-09-21 19:16:55 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-09-21 19:16:57 +0000
commit2610a1ba5d2657932d46b818472c2713bec7293e (patch)
tree9088d99ccf3cd6fddf70d90665c90b9707e30312
parent33d34b89d11c65db2891dc8fd9a6d0fb95b5f4d2 (diff)
parente663cb77281c4c76241b820f6126543f1c2d859f (diff)
downloadframeworks_base-2610a1ba5d2657932d46b818472c2713bec7293e.zip
frameworks_base-2610a1ba5d2657932d46b818472c2713bec7293e.tar.gz
frameworks_base-2610a1ba5d2657932d46b818472c2713bec7293e.tar.bz2
Merge "camera2: refactor LEGACY mode error handling." into lmp-dev
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java25
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceState.java81
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java58
-rw-r--r--core/java/android/hardware/camera2/legacy/CaptureCollector.java180
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java81
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java14
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestHolder.java79
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestQueue.java16
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestThreadManager.java132
-rw-r--r--core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java4
-rw-r--r--core/java/android/hardware/camera2/utils/ArrayUtils.java13
11 files changed, 553 insertions, 130 deletions
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1dc5533..2578093 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -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) {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 2fa9d85..6aab53c 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -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;
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 410934e..4aa330d 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -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;
}
diff --git a/core/java/android/hardware/camera2/legacy/CaptureCollector.java b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
index 307e466..8404e86 100644
--- a/core/java/android/hardware/camera2/legacy/CaptureCollector.java
+++ b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
@@ -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) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index ffc55f1..9143152 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -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
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index c36b63a..907d2ae 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -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);
@@ -1275,6 +1278,11 @@ public class LegacyMetadataMapper {
m.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF);
/*
+ * noiseReduction.*
+ */
+ m.set(CaptureRequest.NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_FAST);
+
+ /*
* lens.*
*/
diff --git a/core/java/android/hardware/camera2/legacy/RequestHolder.java b/core/java/android/hardware/camera2/legacy/RequestHolder.java
index 9f27093..69c140b 100644
--- a/core/java/android/hardware/camera2/legacy/RequestHolder.java
+++ b/core/java/android/hardware/camera2/legacy/RequestHolder.java
@@ -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;
}
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestQueue.java b/core/java/android/hardware/camera2/legacy/RequestQueue.java
index 7820648..7598f99 100644
--- a/core/java/android/hardware/camera2/legacy/RequestQueue.java
+++ b/core/java/android/hardware/camera2/legacy/RequestQueue.java
@@ -80,6 +80,7 @@ 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);
}
@@ -87,6 +88,20 @@ public class RequestQueue {
}
/**
+ * 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.
*
* <p>If the burst is repeating, replace the current repeating burst.</p>
@@ -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;
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index e6da670..87ee1ee 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -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.
*
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index a35883c..c018c3e 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -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();
}
diff --git a/core/java/android/hardware/camera2/utils/ArrayUtils.java b/core/java/android/hardware/camera2/utils/ArrayUtils.java
index ae97079..5a78bbd 100644
--- a/core/java/android/hardware/camera2/utils/ArrayUtils.java
+++ b/core/java/android/hardware/camera2/utils/ArrayUtils.java
@@ -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();
}