diff options
4 files changed, 105 insertions, 69 deletions
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java index 6aab53cf3c03..e96c15f7a270 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java @@ -16,8 +16,8 @@ package android.hardware.camera2.legacy; +import android.hardware.camera2.impl.CameraDeviceImpl; import android.hardware.camera2.impl.CameraMetadataNative; -import android.hardware.camera2.utils.CameraBinderDecorator; import android.os.Handler; import android.util.Log; @@ -53,7 +53,7 @@ public class CameraDeviceState { "CAPTURING"}; private int mCurrentState = STATE_UNCONFIGURED; - private int mCurrentError = CameraBinderDecorator.NO_ERROR; + private int mCurrentError = NO_CAPTURE_ERROR; private RequestHolder mCurrentRequest = null; @@ -87,7 +87,7 @@ public class CameraDeviceState { * </p> * * @param error the error to set. Should be one of the error codes defined in - * {@link android.hardware.camera2.utils.CameraBinderDecorator}. + * {@link CameraDeviceImpl.CameraDeviceCallbacks}. */ public synchronized void setError(int error) { mCurrentError = error; @@ -102,11 +102,11 @@ public class CameraDeviceState { * {@link CameraDeviceStateListener#onConfiguring()} will be called. * </p> * - * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred. + * @return {@code false} if an error has occurred. */ - public synchronized int setConfiguring() { + public synchronized boolean setConfiguring() { doStateTransition(STATE_CONFIGURING); - return mCurrentError; + return mCurrentError == NO_CAPTURE_ERROR; } /** @@ -117,11 +117,11 @@ public class CameraDeviceState { * {@link CameraDeviceStateListener#onIdle()} will be called. * </p> * - * @return {@link CameraBinderDecorator#NO_ERROR}, or an error if one has occurred. + * @return {@code false} if an error has occurred. */ - public synchronized int setIdle() { + public synchronized boolean setIdle() { doStateTransition(STATE_IDLE); - return mCurrentError; + return mCurrentError == NO_CAPTURE_ERROR; } /** @@ -137,13 +137,13 @@ public class CameraDeviceState { * @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. + * @return {@code false} if an error has occurred. */ - public synchronized int setCaptureStart(final RequestHolder request, long timestamp, + public synchronized boolean setCaptureStart(final RequestHolder request, long timestamp, int captureError) { mCurrentRequest = request; doStateTransition(STATE_CAPTURING, timestamp, captureError); - return mCurrentError; + return mCurrentError == NO_CAPTURE_ERROR; } /** @@ -161,16 +161,16 @@ public class CameraDeviceState { * @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. + * @return {@code false} if an error has occurred. */ - public synchronized int setCaptureResult(final RequestHolder request, + public synchronized boolean setCaptureResult(final RequestHolder request, final CameraMetadataNative result, final int captureError) { if (mCurrentState != STATE_CAPTURING) { Log.e(TAG, "Cannot receive result while in state: " + mCurrentState); - mCurrentError = CameraBinderDecorator.INVALID_OPERATION; + mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE; doStateTransition(STATE_ERROR); - return mCurrentError; + return mCurrentError == NO_CAPTURE_ERROR; } if (mCurrentHandler != null && mCurrentListener != null) { @@ -190,7 +190,7 @@ public class CameraDeviceState { }); } } - return mCurrentError; + return mCurrentError == NO_CAPTURE_ERROR; } /** @@ -206,7 +206,7 @@ public class CameraDeviceState { } private void doStateTransition(int newState) { - doStateTransition(newState, /*timestamp*/0, CameraBinderDecorator.NO_ERROR); + doStateTransition(newState, /*timestamp*/0, NO_CAPTURE_ERROR); } private void doStateTransition(int newState, final long timestamp, final int error) { @@ -233,7 +233,7 @@ public class CameraDeviceState { case STATE_CONFIGURING: if (mCurrentState != STATE_UNCONFIGURED && mCurrentState != STATE_IDLE) { Log.e(TAG, "Cannot call configure while in state: " + mCurrentState); - mCurrentError = CameraBinderDecorator.INVALID_OPERATION; + mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE; doStateTransition(STATE_ERROR); break; } @@ -255,7 +255,7 @@ public class CameraDeviceState { if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) { Log.e(TAG, "Cannot call idle while in state: " + mCurrentState); - mCurrentError = CameraBinderDecorator.INVALID_OPERATION; + mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE; doStateTransition(STATE_ERROR); break; } @@ -274,7 +274,7 @@ public class CameraDeviceState { case STATE_CAPTURING: if (mCurrentState != STATE_IDLE && mCurrentState != STATE_CAPTURING) { Log.e(TAG, "Cannot call capture while in state: " + mCurrentState); - mCurrentError = CameraBinderDecorator.INVALID_OPERATION; + mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE; doStateTransition(STATE_ERROR); break; } diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java index 2c584ef7752c..c8e014783239 100644 --- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java @@ -17,6 +17,7 @@ package android.hardware.camera2.legacy; import android.graphics.SurfaceTexture; +import android.hardware.camera2.impl.CameraDeviceImpl; import android.os.ConditionVariable; import android.os.Handler; import android.os.Message; @@ -42,6 +43,8 @@ public class GLThreadManager { private CaptureCollector mCaptureCollector; + private final CameraDeviceState mDeviceState; + private final SurfaceTextureRenderer mTextureRenderer; private final RequestHandlerThread mGLHandlerThread; @@ -76,42 +79,47 @@ public class GLThreadManager { if (mCleanup) { return true; } - switch (msg.what) { - case MSG_NEW_CONFIGURATION: - ConfigureHolder configure = (ConfigureHolder) msg.obj; - mTextureRenderer.cleanupEGLContext(); - mTextureRenderer.configureSurfaces(configure.surfaces); - mCaptureCollector = checkNotNull(configure.collector); - configure.condition.open(); - mConfigured = true; - break; - case MSG_NEW_FRAME: - if (mDroppingFrames) { - Log.w(TAG, "Ignoring frame."); + try { + switch (msg.what) { + case MSG_NEW_CONFIGURATION: + ConfigureHolder configure = (ConfigureHolder) msg.obj; + mTextureRenderer.cleanupEGLContext(); + mTextureRenderer.configureSurfaces(configure.surfaces); + mCaptureCollector = checkNotNull(configure.collector); + configure.condition.open(); + mConfigured = true; + break; + case MSG_NEW_FRAME: + if (mDroppingFrames) { + Log.w(TAG, "Ignoring frame."); + break; + } + if (DEBUG) { + mPrevCounter.countAndLog(); + } + if (!mConfigured) { + Log.e(TAG, "Dropping frame, EGL context not configured!"); + } + mTextureRenderer.drawIntoSurfaces(mCaptureCollector); + break; + case MSG_CLEANUP: + mTextureRenderer.cleanupEGLContext(); + mCleanup = true; + mConfigured = false; + break; + case MSG_DROP_FRAMES: + mDroppingFrames = true; + break; + case MSG_ALLOW_FRAMES: + mDroppingFrames = false; + break; + default: + Log.e(TAG, "Unhandled message " + msg.what + " on GLThread."); break; - } - if (DEBUG) { - mPrevCounter.countAndLog(); - } - if (!mConfigured) { - Log.e(TAG, "Dropping frame, EGL context not configured!"); - } - mTextureRenderer.drawIntoSurfaces(mCaptureCollector); - break; - case MSG_CLEANUP: - mTextureRenderer.cleanupEGLContext(); - mCleanup = true; - mConfigured = false; - break; - case MSG_DROP_FRAMES: - mDroppingFrames = true; - break; - case MSG_ALLOW_FRAMES: - mDroppingFrames = false; - break; - default: - Log.e(TAG, "Unhandled message " + msg.what + " on GLThread."); - break; + } + } catch (Exception e) { + Log.e(TAG, "Received exception on GL render thread: ", e); + mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); } return true; } @@ -122,11 +130,13 @@ public class GLThreadManager { * * @param cameraId the camera id for this thread. * @param facing direction the camera is facing. + * @param state {@link CameraDeviceState} to use for error handling. */ - public GLThreadManager(int cameraId, int facing) { + public GLThreadManager(int cameraId, int facing, CameraDeviceState state) { mTextureRenderer = new SurfaceTextureRenderer(facing); TAG = String.format("CameraDeviceGLThread-%d", cameraId); mGLHandlerThread = new RequestHandlerThread(TAG, mGLHandlerCb); + mDeviceState = state; } /** diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index 9143152d0522..a724b4142274 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -26,6 +26,7 @@ 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.CameraBinderDecorator; import android.hardware.camera2.utils.LongParcelable; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.utils.CameraRuntimeException; @@ -288,17 +289,18 @@ public class LegacyCameraDevice implements AutoCloseable { } } - int error = mDeviceState.setConfiguring(); - if (error == NO_ERROR) { + boolean success = false; + if (mDeviceState.setConfiguring()) { mRequestThreadManager.configure(outputs); - error = mDeviceState.setIdle(); + success = mDeviceState.setIdle(); } - if (error == NO_ERROR) { + if (success) { mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null; + } else { + return CameraBinderDecorator.INVALID_OPERATION; } - - return error; + return CameraBinderDecorator.NO_ERROR; } /** diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index 87ee1eef64f2..788b6d8db16a 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -98,6 +98,7 @@ public class RequestThreadManager { private SurfaceTexture mDummyTexture; private Surface mDummySurface; + private final Object mIdleLock = new Object(); private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview"); private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests"); @@ -173,6 +174,14 @@ public class RequestThreadManager { } } + private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() { + @Override + public void onError(int i, Camera camera) { + Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback"); + mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); + } + }; + private final ConditionVariable mReceivedJpeg = new ConditionVariable(false); private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() { @@ -405,7 +414,7 @@ public class RequestThreadManager { // TODO: Detect and optimize single-output paths here to skip stream teeing. if (mGLThreadManager == null) { - mGLThreadManager = new GLThreadManager(mCameraId, facing); + mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState); mGLThreadManager.start(); } mGLThreadManager.waitUntilStarted(); @@ -617,9 +626,20 @@ public class RequestThreadManager { CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); break; } - mDeviceState.setIdle(); - break; - } else { + + synchronized (mIdleLock) { + // Retry the the request queue. + nextBurst = mRequestQueue.getNext(); + + // If we still have no queued requests, go idle. + if (nextBurst == null) { + mDeviceState.setIdle(); + break; + } + } + } + + if (nextBurst != null) { // Queue another capture if we did not get the last burst. handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST); } @@ -831,6 +851,7 @@ public class RequestThreadManager { mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics); mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState); mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb); + mCamera.setErrorCallback(mErrorCallback); } /** @@ -883,8 +904,11 @@ public class RequestThreadManager { public int submitCaptureRequests(List<CaptureRequest> requests, boolean repeating, /*out*/LongParcelable frameNumber) { Handler handler = mRequestThread.waitAndGetHandler(); - int ret = mRequestQueue.submit(requests, repeating, frameNumber); - handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST); + int ret; + synchronized (mIdleLock) { + ret = mRequestQueue.submit(requests, repeating, frameNumber); + handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST); + } return ret; } |