From 3931c0448a5692d0f0152a66dfa3c56c8f3d575d Mon Sep 17 00:00:00 2001 From: Alan Newberger Date: Thu, 7 Aug 2014 17:57:15 -0700 Subject: Fix missing zoom fields in copy constructor of CameraCapabilities These fields are deprecated but while we have them, should copy them in the constructor. This contributes to a zoom bug in the app, since ratios and indices are dropped upon copy. Bug: 16527845 Change-Id: I008384faa77e0c13a2790e095fa311909c1e9b44 --- .../src/com/android/ex/camera2/portability/CameraCapabilities.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java index 9b7ca6e..1f2781b 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java @@ -485,6 +485,8 @@ public class CameraCapabilities { mMaxNumOfFacesSupported = src.mMaxNumOfFacesSupported; mMaxNumOfFocusAreas = src.mMaxNumOfFocusAreas; mMaxNumOfMeteringArea = src.mMaxNumOfMeteringArea; + mMaxZoomIndex = src.mMaxZoomIndex; + mZoomRatioList.addAll(src.mZoomRatioList); mMaxZoomRatio = src.mMaxZoomRatio; mHorizontalViewAngle = src.mHorizontalViewAngle; mVerticalViewAngle = src.mVerticalViewAngle; -- cgit v1.2.3 From e12fd4abbad22f83b6a6d5f2fa391ca56849563c Mon Sep 17 00:00:00 2001 From: Sol Boucher Date: Fri, 22 Aug 2014 10:33:44 -0700 Subject: camera2-portability: Play shutter sound for camera2 captures Bug: 17109582 Change-Id: I73321cdbb2972b2d1e9bc2de93192fbb506347e7 --- .../com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java index e675796..d139c62 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java @@ -35,6 +35,7 @@ import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.MeteringRectangle; import android.media.Image; import android.media.ImageReader; +import android.media.MediaActionSound; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; @@ -63,6 +64,7 @@ class AndroidCamera2AgentImpl extends CameraAgent { private final CameraStateHolder mCameraState; private final DispatchThread mDispatchThread; private final CameraManager mCameraManager; + private final MediaActionSound mNoisemaker; /** * Number of camera devices. The length of {@code mCameraDevices} does not reveal this @@ -88,6 +90,8 @@ class AndroidCamera2AgentImpl extends CameraAgent { mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread); mDispatchThread.start(); mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); + mNoisemaker = new MediaActionSound(); + mNoisemaker.load(MediaActionSound.SHUTTER_CLICK); mNumCameraDevices = 0; mCameraDevices = new ArrayList(); @@ -915,7 +919,6 @@ class AndroidCamera2AgentImpl extends CameraAgent { }}); } - // TODO: Implement @Override public void takePicture(final Handler handler, final CameraShutterCallback shutter, @@ -932,6 +935,7 @@ class AndroidCamera2AgentImpl extends CameraAgent { handler.post(new Runnable() { @Override public void run() { + mNoisemaker.play(MediaActionSound.SHUTTER_CLICK); shutter.onShutter(AndroidCamera2ProxyImpl.this); }}); } -- cgit v1.2.3 From fbd0084b89fe1c3b7be014d13b15b20a08ed583a Mon Sep 17 00:00:00 2001 From: Sol Boucher Date: Wed, 27 Aug 2014 14:07:41 -0700 Subject: camera2-portability: Fix camera2 takePicture AE regression This fixes a regression introduced by 984a086412a94ebea1bd9af8cd8bbf4afab38034 that resulted in captures sometimes hanging until the AE state changed. It includes explanatory comments to guard against this sort of error in the future. Bug: 17302778 Change-Id: I668f5a5470a0ae7712f62c7574d18360d47e410b --- .../android/ex/camera2/portability/AndroidCamera2AgentImpl.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java index 44f19bc..c4eea50 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java @@ -854,6 +854,9 @@ class AndroidCamera2AgentImpl extends CameraAgent { case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED: case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: { + // This check must be made regardless of whether the focus state has + // changed recently to avoid infinite waiting during autoFocus() + // when the algorithm has already either converged or failed to. if (mOneshotAfCallback != null) { // A call to autoFocus() was just made to request a focus lock. // Notify the caller that the lens is now indefinitely fixed, @@ -876,8 +879,7 @@ class AndroidCamera2AgentImpl extends CameraAgent { // might get the final callbacks for an earlier frame after receiving one or // more that correspond to the next one. To prevent our data from oscillating, // we never consider AE states that are older than the last one we've seen. - if (aeState != mCurrentAeState && - result.getFrameNumber() > mLastAeFrameNumber) { + if (result.getFrameNumber() > mLastAeFrameNumber) { mCurrentAeState = aeStateMaybe; mLastAeFrameNumber = result.getFrameNumber(); @@ -885,6 +887,9 @@ class AndroidCamera2AgentImpl extends CameraAgent { case CaptureResult.CONTROL_AE_STATE_CONVERGED: case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED: case CaptureResult.CONTROL_AE_STATE_LOCKED: { + // This check must be made regardless of whether the exposure state + // has changed recently to avoid infinite waiting during + // takePicture() when the algorithm has already converged. if (mOneshotCaptureCallback != null) { // A call to takePicture() was just made, and autoexposure // converged so it's time to initiate the capture! -- cgit v1.2.3 -- cgit v1.2.3 From bcf9446a175d8f5090a2832296e5e42d71b1a601 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Tue, 9 Sep 2014 11:50:23 -0700 Subject: portability: Fix a bug where long AF callbacks caused ISE timeouts Bug: 17403384 Change-Id: I2f452f79ffb4c0c3327ea5bf7db3f9d26e98ff51 --- .../portability/AndroidCamera2AgentImpl.java | 19 ++++++ .../portability/AndroidCameraAgentImpl.java | 19 ++++++ .../ex/camera2/portability/CameraActions.java | 70 ++++++++++++++++++++++ .../ex/camera2/portability/CameraAgent.java | 39 +++++++++--- .../ex/camera2/portability/CameraStateHolder.java | 5 ++ 5 files changed, 144 insertions(+), 8 deletions(-) diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java index 62cb700..88be114 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java @@ -168,6 +168,7 @@ class AndroidCamera2AgentImpl extends CameraAgent { private CameraOpenCallback mOpenCallback; private int mCameraIndex; private String mCameraId; + private int mCancelAfPending = 0; // Available in CAMERA_UNCONFIGURED state and above: private CameraDevice mCamera; @@ -208,6 +209,7 @@ class AndroidCamera2AgentImpl extends CameraAgent { @Override public void handleMessage(final Message msg) { super.handleMessage(msg); + Log.v(TAG, "handleMessage - action = '" + CameraActions.stringify(msg.what) + "'"); try { switch(msg.what) { case CameraActions.OPEN_CAMERA: @@ -360,6 +362,11 @@ class AndroidCamera2AgentImpl extends CameraAgent { } case CameraActions.AUTO_FOCUS: { + if (mCancelAfPending > 0) { + Log.v(TAG, "handleMessage - Ignored AUTO_FOCUS because there was " + + mCancelAfPending + " pending CANCEL_AUTO_FOCUS messages"); + break; // ignore AF because a CANCEL_AF is queued after this + } // We only support locking the focus while a preview is being displayed. // However, it can be requested multiple times in succession; the effect of // the subsequent invocations is determined by the focus mode defined in the @@ -437,6 +444,9 @@ class AndroidCamera2AgentImpl extends CameraAgent { } case CameraActions.CANCEL_AUTO_FOCUS: { + // Ignore all AFs that were already queued until we see + // a CANCEL_AUTO_FOCUS_FINISH + mCancelAfPending++; // Why would you want to unlock the lens if it isn't already locked? if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) { @@ -462,6 +472,13 @@ class AndroidCamera2AgentImpl extends CameraAgent { break; } + case CameraActions.CANCEL_AUTO_FOCUS_FINISH: { + // Stop ignoring AUTO_FOCUS messages unless there are additional + // CANCEL_AUTO_FOCUSes that were added + mCancelAfPending--; + break; + } + case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: { mPassiveAfCallback = (CameraAFMoveCallback) msg.obj; break; @@ -628,6 +645,8 @@ class AndroidCamera2AgentImpl extends CameraAgent { sCameraExceptionCallback.onCameraException((RuntimeException) ex); }}); } + } finally { + WaitDoneBundle.unblockSyncWaiters(msg); } } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java index 949ac62..3704f59 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java @@ -242,6 +242,7 @@ class AndroidCameraAgentImpl extends CameraAgent { private Camera mCamera; private int mCameraId; private ParametersCache mParameterCache; + private int mCancelAfPending = 0; private class CaptureCallbacks { public final ShutterCallback mShutter; @@ -323,6 +324,7 @@ class AndroidCameraAgentImpl extends CameraAgent { @Override public void handleMessage(final Message msg) { super.handleMessage(msg); + Log.v(TAG, "handleMessage - action = '" + CameraActions.stringify(msg.what) + "'"); try { switch (msg.what) { case CameraActions.OPEN_CAMERA: { @@ -451,17 +453,32 @@ class AndroidCameraAgentImpl extends CameraAgent { } case CameraActions.AUTO_FOCUS: { + if (mCancelAfPending > 0) { + Log.v(TAG, "handleMessage - Ignored AUTO_FOCUS because there was " + + mCancelAfPending + " pending CANCEL_AUTO_FOCUS messages"); + break; // ignore AF because a CANCEL_AF is queued after this + } mCameraState.setState(AndroidCameraStateHolder.CAMERA_FOCUSING); mCamera.autoFocus((AutoFocusCallback) msg.obj); break; } case CameraActions.CANCEL_AUTO_FOCUS: { + // Ignore all AFs that were already queued until we see + // a CANCEL_AUTO_FOCUS_FINISH + mCancelAfPending++; mCamera.cancelAutoFocus(); mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE); break; } + case CameraActions.CANCEL_AUTO_FOCUS_FINISH: { + // Stop ignoring AUTO_FOCUS messages unless there are additional + // CANCEL_AUTO_FOCUSes that were added + mCancelAfPending--; + break; + } + case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: { setAutoFocusMoveCallback(mCamera, msg.obj); break; @@ -585,6 +602,8 @@ class AndroidCameraAgentImpl extends CameraAgent { } }); } + } finally { + WaitDoneBundle.unblockSyncWaiters(msg); } } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java index aae122b..03bfc03 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java @@ -42,6 +42,7 @@ class CameraActions { public static final int CANCEL_AUTO_FOCUS = 302; public static final int SET_AUTO_FOCUS_MOVE_CALLBACK = 303; public static final int SET_ZOOM_CHANGE_LISTENER = 304; + public static final int CANCEL_AUTO_FOCUS_FINISH = 305; // Face detection public static final int SET_FACE_DETECTION_LISTENER = 461; public static final int START_FACE_DETECTION = 462; @@ -52,4 +53,73 @@ class CameraActions { public static final int SET_DISPLAY_ORIENTATION = 502; // Capture public static final int CAPTURE_PHOTO = 601; + + public static String stringify(int action) { + switch (action) { + case OPEN_CAMERA: + return "OPEN_CAMERA"; + case RELEASE: + return "RELEASE"; + case RECONNECT: + return "RECONNECT"; + case UNLOCK: + return "UNLOCK"; + case LOCK: + return "LOCK"; + case SET_PREVIEW_TEXTURE_ASYNC: + return "SET_PREVIEW_TEXTURE_ASYNC"; + case START_PREVIEW_ASYNC: + return "START_PREVIEW_ASYNC"; + case STOP_PREVIEW: + return "STOP_PREVIEW"; + case SET_PREVIEW_CALLBACK_WITH_BUFFER: + return "SET_PREVIEW_CALLBACK_WITH_BUFFER"; + case ADD_CALLBACK_BUFFER: + return "ADD_CALLBACK_BUFFER"; + case SET_PREVIEW_DISPLAY_ASYNC: + return "SET_PREVIEW_DISPLAY_ASYNC"; + case SET_PREVIEW_CALLBACK: + return "SET_PREVIEW_CALLBACK"; + case SET_ONE_SHOT_PREVIEW_CALLBACK: + return "SET_ONE_SHOT_PREVIEW_CALLBACK"; + case SET_PARAMETERS: + return "SET_PARAMETERS"; + case GET_PARAMETERS: + return "GET_PARAMETERS"; + case REFRESH_PARAMETERS: + return "REFRESH_PARAMETERS"; + case APPLY_SETTINGS: + return "APPLY_SETTINGS"; + case AUTO_FOCUS: + return "AUTO_FOCUS"; + case CANCEL_AUTO_FOCUS: + return "CANCEL_AUTO_FOCUS"; + case SET_AUTO_FOCUS_MOVE_CALLBACK: + return "SET_AUTO_FOCUS_MOVE_CALLBACK"; + case SET_ZOOM_CHANGE_LISTENER: + return "SET_ZOOM_CHANGE_LISTENER"; + case CANCEL_AUTO_FOCUS_FINISH: + return "CANCEL_AUTO_FOCUS_FINISH"; + case SET_FACE_DETECTION_LISTENER: + return "SET_FACE_DETECTION_LISTENER"; + case START_FACE_DETECTION: + return "START_FACE_DETECTION"; + case STOP_FACE_DETECTION: + return "STOP_FACE_DETECTION"; + case SET_ERROR_CALLBACK: + return "SET_ERROR_CALLBACK"; + case ENABLE_SHUTTER_SOUND: + return "ENABLE_SHUTTER_SOUND"; + case SET_DISPLAY_ORIENTATION: + return "SET_DISPLAY_ORIENTATION"; + case CAPTURE_PHOTO: + return "CAPTURE_PHOTO"; + default: + return "UNKNOWN(" + action + ")"; + } + } + + private CameraActions() { + throw new AssertionError(); + } } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java index b624b47..475523d 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java @@ -23,6 +23,7 @@ import android.hardware.Camera.OnZoomChangeListener; import android.os.Build; import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.view.SurfaceHolder; import com.android.ex.camera2.portability.debug.Log; @@ -541,8 +542,8 @@ public abstract class CameraAgent { getDispatchThread().runJobSync(new Runnable() { @Override public void run() { - getCameraHandler().sendEmptyMessage(CameraActions.STOP_PREVIEW); - getCameraHandler().post(bundle.mUnlockRunnable); + getCameraHandler().obtainMessage(CameraActions.STOP_PREVIEW, bundle) + .sendToTarget(); }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview"); } @@ -602,14 +603,18 @@ public abstract class CameraAgent { /** * Cancels the auto-focus process. + * + *

This action has the highest priority and will get processed before anything + * else that is pending. Moreover, any pending auto-focuses that haven't yet + * began will also be ignored.

*/ public void cancelAutoFocus() { - getDispatchThread().runJob(new Runnable() { - @Override - public void run() { - getCameraHandler().removeMessages(CameraActions.AUTO_FOCUS); - getCameraHandler().sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS); - }}); + // Do not use the dispatch thread since we want to avoid a wait-cycle + // between applySettingsHelper which waits until the state is not FOCUSING. + // cancelAutoFocus should get executed asap, set the state back to idle. + getCameraHandler().sendMessageAtFrontOfQueue( + getCameraHandler().obtainMessage(CameraActions.CANCEL_AUTO_FOCUS)); + getCameraHandler().sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS_FINISH); } /** @@ -778,6 +783,9 @@ public abstract class CameraAgent { /** * Applies the settings to the camera device. * + *

If the camera is either focusing or capturing; settings applications + * will be (asynchronously) deferred until those operations complete.

+ * * @param settings The settings to use on the device. * @return Whether the settings can be applied. */ @@ -851,5 +859,20 @@ public abstract class CameraAgent { } }}; } + + /** + * Notify all synchronous waiters waiting on message completion with {@link #mWaitLock}. + * + *

This assumes that the message was sent with {@code this} as the {@code Message#obj}. + * Otherwise the message is ignored.

+ */ + /*package*/ static void unblockSyncWaiters(Message msg) { + if (msg == null) return; + + if (msg.obj instanceof WaitDoneBundle) { + WaitDoneBundle bundle = (WaitDoneBundle)msg.obj; + bundle.mUnlockRunnable.run(); + } + } } } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java index 35ae51c..c8d82b6 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java @@ -30,6 +30,9 @@ public abstract class CameraStateHolder { } public synchronized void setState(int state) { + if (mState != state) { + Log.v(TAG, "setState - state = " + Integer.toBinaryString(state)); + } mState = state; this.notifyAll(); } @@ -83,6 +86,7 @@ public abstract class CameraStateHolder { * reached. */ public boolean waitForStates(final int states) { + Log.v(TAG, "waitForStates - states = " + Integer.toBinaryString(states)); return waitForCondition(new ConditionChecker() { @Override public boolean success() { @@ -100,6 +104,7 @@ public abstract class CameraStateHolder { * reached. */ public boolean waitToAvoidStates(final int states) { + Log.v(TAG, "waitToAvoidStates - states = " + Integer.toBinaryString(states)); return waitForCondition(new ConditionChecker() { @Override public boolean success() { -- cgit v1.2.3