summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErin Dahlgren <edahlgren@google.com>2013-11-06 11:57:51 -0800
committerErin Dahlgren <edahlgren@google.com>2013-11-07 10:02:55 -0800
commitb09b53efcd179d55836014dcd054cff6f7d688d8 (patch)
tree4344716326c40cce060215dba89a5f741de33cf0
parenteb605b4a27be212915db5bc23e9a82b857036a1c (diff)
downloadandroid_packages_apps_Snap-b09b53efcd179d55836014dcd054cff6f7d688d8.tar.gz
android_packages_apps_Snap-b09b53efcd179d55836014dcd054cff6f7d688d8.tar.bz2
android_packages_apps_Snap-b09b53efcd179d55836014dcd054cff6f7d688d8.zip
Parallelize opening the camera with view handling in photo mode.
Bug: 11255097 Change-Id: I8da16a97ee46555267ae8cfee0e7940d3f53f98f
-rw-r--r--src/com/android/camera/PhotoModule.java239
-rw-r--r--src/com/android/camera/PhotoUI.java33
2 files changed, 192 insertions, 80 deletions
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 9523c9429..ce0deed54 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -40,6 +40,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
@@ -103,6 +104,10 @@ public class PhotoModule
private static final int OPEN_CAMERA_FAIL = 9;
private static final int CAMERA_DISABLED = 10;
private static final int SWITCH_TO_GCAM_MODULE = 11;
+ private static final int CAMERA_PREVIEW_DONE = 12;
+
+ private static final int OPEN_CAMERA_ASYNC = 1;
+ private static final int START_PREVIEW_ASYNC = 2;
// The subset of parameters we need to update in setCameraParameters().
private static final int UPDATE_PARAM_INITIALIZE = 1;
@@ -238,6 +243,14 @@ public class PhotoModule
private String mSceneMode;
private final Handler mHandler = new MainHandler();
+
+ /** A thread separate from the UI thread for camera startup. */
+ private HandlerThread mOpenCameraThread;
+ /** A handler to run on the camera startup thread. */
+ private Handler mOpenCameraHandler;
+ /** This lock should always protect openCamera and closeCamera. */
+ private final Object mCameraOpenLock = new Object();
+
private PreferenceGroup mPreferenceGroup;
private boolean mQuickCapture;
@@ -280,10 +293,83 @@ public class PhotoModule
}
/**
+ * This Handler is used to open the camera.
+ */
+ private class OpenCameraHandler extends Handler {
+ public OpenCameraHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OPEN_CAMERA_ASYNC: {
+ // Prevent closeCamera from thinking the camera
+ // is already closed when it is still opening.
+ //
+ // This happens during the lockscreen sequence:
+ // onResume -> onPause -> onResume.
+ synchronized (mCameraOpenLock) {
+ Log.v(TAG, "openCamera");
+ if (mCameraDevice != null) {
+ throw new IllegalArgumentException("Camera already open.");
+ }
+
+ mCameraDevice = CameraUtil.openCamera(
+ mActivity, mCameraId, mHandler,
+ mActivity.getCameraOpenErrorCallback());
+
+ if (mCameraDevice == null) {
+ Log.e(TAG, "Failed to open camera:" + mCameraId);
+ break;
+ }
+ mParameters = mCameraDevice.getParameters();
+
+ initializeCapabilities();
+ if (mFocusManager == null) {
+ initializeFocusManager();
+ }
+
+ // The views can't be updated from a non UI thread.
+ mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
+
+ setCameraParameters(UPDATE_PARAM_ALL);
+ mCameraPreviewParamsReady = true;
+
+ // This will exit early if the surface texture
+ // isn't ready. We also need to protect the surface
+ // texture from concurrent updates/checks.
+ startPreview();
+ }
+ break;
+ }
+
+ case START_PREVIEW_ASYNC: {
+ if (mCameraDevice == null) {
+ throw new IllegalStateException("Camera not yet opened.");
+ }
+
+ startPreview();
+ mHandler.sendEmptyMessage(CAMERA_PREVIEW_DONE);
+ break;
+ }
+
+ default: {
+ throw new UnsupportedOperationException("Unknown message " + msg.what);
+ }
+ }
+ };
+ }
+
+ /**
* This Handler is used to post message back onto the main thread of the
* application
*/
private class MainHandler extends Handler {
+ public MainHandler() {
+ super(Looper.getMainLooper());
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -345,6 +431,12 @@ public class PhotoModule
case SWITCH_TO_GCAM_MODULE: {
mActivity.onModuleSelected(ModuleSwitcher.GCAM_MODULE_INDEX);
+ break;
+ }
+
+ case CAMERA_PREVIEW_DONE: {
+ // Modifies views, so must be executed on the UI thread.
+ onPreviewStarted();
}
}
}
@@ -413,7 +505,8 @@ public class PhotoModule
@Override
public void onPreviewUIReady() {
- startPreview();
+ // Requires that OPEN_CAMERA_ASYNC has been already sent.
+ mOpenCameraHandler.sendEmptyMessage(START_PREVIEW_ASYNC);
}
@Override
@@ -453,7 +546,11 @@ public class PhotoModule
setCameraId(mCameraId);
// from onPause
- closeCamera();
+ mOpenCameraHandler.removeMessages(OPEN_CAMERA_ASYNC);
+ mOpenCameraHandler.removeMessages(START_PREVIEW_ASYNC);
+ synchronized (mCameraOpenLock) {
+ closeCamera();
+ }
mUI.collapseCameraControls();
mUI.clearFaces();
if (mFocusManager != null) mFocusManager.removeMessages();
@@ -461,9 +558,12 @@ public class PhotoModule
// Restart the camera and initialize the UI. From onCreate.
mPreferences.setLocalId(mActivity, mCameraId);
CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
- mCameraDevice = CameraUtil.openCamera(
+ synchronized (mCameraOpenLock) {
+ Log.v(TAG, "openCamera");
+ mCameraDevice = CameraUtil.openCamera(
mActivity, mCameraId, mHandler,
mActivity.getCameraOpenErrorCallback());
+ }
if (mCameraDevice == null) {
Log.e(TAG, "Failed to open camera:" + mCameraId + ", aborting.");
return;
@@ -1161,47 +1261,25 @@ public class PhotoModule
mPaused = false;
}
- /**
- * Opens the camera device.
- *
- * @return Whether the camera was opened successfully.
- */
- private boolean prepareCamera() {
- // We need to check whether the activity is paused before long
- // operations to ensure that onPause() can be done ASAP.
- mCameraDevice = CameraUtil.openCamera(
- mActivity, mCameraId, mHandler,
- mActivity.getCameraOpenErrorCallback());
- if (mCameraDevice == null) {
- Log.e(TAG, "Failed to open camera:" + mCameraId);
- return false;
- }
- mParameters = mCameraDevice.getParameters();
-
- initializeCapabilities();
- if (mFocusManager == null) initializeFocusManager();
- setCameraParameters(UPDATE_PARAM_ALL);
- mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
- mCameraPreviewParamsReady = true;
- startPreview();
- mOnResumeTime = SystemClock.uptimeMillis();
- checkDisplayRotation();
- return true;
- }
-
-
@Override
public void onResumeAfterSuper() {
Log.v(TAG, "On resume.");
if (mOpenCameraFail || mCameraDisabled) return;
+ if (mOpenCameraThread == null) {
+ Log.e("DEBUG", "new OpenCameraThread");
+ mOpenCameraThread = new HandlerThread("OpenCameraThread");
+ mOpenCameraThread.start();
+ mOpenCameraHandler = new OpenCameraHandler(mOpenCameraThread.getLooper());
+ }
+
mJpegPictureCallbackTime = 0;
mZoomValue = 0;
resetExposureCompensation();
- if (!prepareCamera()) {
- // Camera failure.
- return;
- }
+
+ mOpenCameraHandler.sendEmptyMessage(OPEN_CAMERA_ASYNC);
+ mOnResumeTime = SystemClock.uptimeMillis();
+ checkDisplayRotation();
// If first time initialization is not finished, put it in the
// message queue.
@@ -1259,6 +1337,9 @@ public class PhotoModule
if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
mCameraDevice.cancelAutoFocus();
}
+ // If the camera has not been opened asynchronously yet,
+ // and startPreview hasn't been called, then this is a no-op.
+ // (e.g. onResume -> onPause -> onResume).
stopPreview();
mNamedImages = null;
@@ -1272,7 +1353,18 @@ public class PhotoModule
// Remove the messages and runnables in the queue.
mHandler.removeCallbacksAndMessages(null);
- closeCamera();
+ // Postpones actually releasing for KEEP_CAMERA_TIMEOUT,
+ // so if onResume is directly called after this, the camera
+ // simply needs to reconnect (takes about 2-5ms).
+ mOpenCameraHandler.removeMessages(OPEN_CAMERA_ASYNC);
+ mOpenCameraHandler.removeMessages(START_PREVIEW_ASYNC);
+ synchronized (mCameraOpenLock) {
+ closeCamera();
+ }
+ // Stop the long running open camera thread.
+ mOpenCameraThread.quitSafely();
+ mOpenCameraThread = null;
+ Log.e(TAG, "Done quiting safely.");
resetScreenOn();
mUI.onPause();
@@ -1434,6 +1526,7 @@ public class PhotoModule
}
private void closeCamera() {
+ Log.v(TAG, "closeCamera");
if (mCameraDevice != null) {
mCameraDevice.setZoomChangeListener(null);
mCameraDevice.setFaceDetectionCallback(null, null);
@@ -1473,48 +1566,58 @@ public class PhotoModule
startPreview();
}
- // This can only be called by UI Thread.
private void startPreview() {
- if (mPaused || mCameraDevice == null) {
+ if (mCameraState != PREVIEW_STOPPED) {
+ Log.v(TAG, "Already previewing");
return;
}
- SurfaceTexture st = mUI.getSurfaceTexture();
- if (st == null) {
- Log.w(TAG, "startPreview: surfaceTexture is not ready.");
- return;
- }
- if (!mCameraPreviewParamsReady) {
- Log.w(TAG, "startPreview: parameters for preview is not ready.");
+
+ if (mPaused || mCameraDevice == null) {
return;
}
- mCameraDevice.setErrorCallback(mErrorCallback);
- // ICS camera frameworks has a bug. Face detection state is not cleared
- // after taking a picture. Stop the preview to work around it. The bug
- // was fixed in JB.
- if (mCameraState != PREVIEW_STOPPED) stopPreview();
+ Object textureLock = mUI.getSurfaceTextureLock();
- setDisplayOrientation();
+ // Any decisions we make based on the surface texture state
+ // need to be protected.
+ synchronized (textureLock) {
+ SurfaceTexture st = mUI.getSurfaceTexture();
+ if (st == null) {
+ Log.w(TAG, "startPreview: surfaceTexture is not ready.");
+ return;
+ }
- if (!mSnapshotOnIdle) {
- // If the focus mode is continuous autofocus, call cancelAutoFocus to
- // resume it because it may have been paused by autoFocus call.
- if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
- mCameraDevice.cancelAutoFocus();
+ if (!mCameraPreviewParamsReady) {
+ Log.w(TAG, "startPreview: parameters for preview is not ready.");
+ return;
}
- mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
- }
- setCameraParameters(UPDATE_PARAM_ALL);
- // Let UI set its expected aspect ratio
- mCameraDevice.setPreviewTexture(st);
+ mCameraDevice.setErrorCallback(mErrorCallback);
- Log.v(TAG, "startPreview");
- mCameraDevice.startPreview();
- mFocusManager.onPreviewStarted();
- onPreviewStarted();
+ setDisplayOrientation();
- if (mSnapshotOnIdle) {
- mHandler.post(mDoSnapRunnable);
+ if (!mSnapshotOnIdle) {
+ // If the focus mode is continuous autofocus, call cancelAutoFocus to
+ // resume it because it may have been paused by autoFocus call.
+ if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
+ mCameraDevice.cancelAutoFocus();
+ }
+ mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
+ }
+ setCameraParameters(UPDATE_PARAM_ALL);
+ // Let UI set its expected aspect ratio
+ mCameraDevice.setPreviewTexture(st);
+
+ Log.v(TAG, "startPreview");
+ mCameraDevice.startPreview();
+
+ // Since the preview actually started, remove any messages to
+ // start it again.
+ mOpenCameraHandler.removeMessages(START_PREVIEW_ASYNC);
+ mFocusManager.onPreviewStarted();
+
+ if (mSnapshotOnIdle) {
+ mHandler.post(mDoSnapRunnable);
+ }
}
}
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
index ffb19d4e1..524cd4654 100644
--- a/src/com/android/camera/PhotoUI.java
+++ b/src/com/android/camera/PhotoUI.java
@@ -117,6 +117,7 @@ public class PhotoUI implements PieListener,
private Matrix mMatrix = null;
private float mAspectRatio = 4f / 3f;
private View mPreviewCover;
+ private final Object mSurfaceTextureLock = new Object();
public interface SurfaceTextureSizeChangedListener {
public void onSurfaceTextureSizeChanged(int uncroppedWidth, int uncroppedHeight);
@@ -277,16 +278,22 @@ public class PhotoUI implements PieListener,
mController.onPreviewRectChanged(CameraUtil.rectFToRect(previewRect));
}
+ protected Object getSurfaceTextureLock() {
+ return mSurfaceTextureLock;
+ }
+
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- Log.v(TAG, "SurfaceTexture ready.");
- mPreviewCover.setVisibility(View.GONE);
- mSurfaceTexture = surface;
- mController.onPreviewUIReady();
- // Workaround for b/11168275, see b/10981460 for more details
- if (mPreviewWidth != 0 && mPreviewHeight != 0) {
- // Re-apply transform matrix for new surface texture
- setTransformMatrix(mPreviewWidth, mPreviewHeight);
+ synchronized (mSurfaceTextureLock) {
+ Log.v(TAG, "SurfaceTexture ready.");
+ mPreviewCover.setVisibility(View.GONE);
+ mSurfaceTexture = surface;
+ mController.onPreviewUIReady();
+ // Workaround for b/11168275, see b/10981460 for more details
+ if (mPreviewWidth != 0 && mPreviewHeight != 0) {
+ // Re-apply transform matrix for new surface texture
+ setTransformMatrix(mPreviewWidth, mPreviewHeight);
+ }
}
}
@@ -297,10 +304,12 @@ public class PhotoUI implements PieListener,
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- mSurfaceTexture = null;
- mController.onPreviewUIDestroyed();
- Log.w(TAG, "SurfaceTexture destroyed");
- return true;
+ synchronized (mSurfaceTextureLock) {
+ mSurfaceTexture = null;
+ mController.onPreviewUIDestroyed();
+ Log.w(TAG, "SurfaceTexture destroyed");
+ return true;
+ }
}
@Override