diff options
author | Ruben Brunk <rubenbrunk@google.com> | 2014-09-21 19:16:55 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-09-21 19:16:57 +0000 |
commit | 2610a1ba5d2657932d46b818472c2713bec7293e (patch) | |
tree | 9088d99ccf3cd6fddf70d90665c90b9707e30312 | |
parent | 33d34b89d11c65db2891dc8fd9a6d0fb95b5f4d2 (diff) | |
parent | e663cb77281c4c76241b820f6126543f1c2d859f (diff) | |
download | frameworks_base-2610a1ba5d2657932d46b818472c2713bec7293e.zip frameworks_base-2610a1ba5d2657932d46b818472c2713bec7293e.tar.gz frameworks_base-2610a1ba5d2657932d46b818472c2713bec7293e.tar.bz2 |
Merge "camera2: refactor LEGACY mode error handling." into lmp-dev
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(); } |