diff options
author | braveyao <braveyao@chromium.org> | 2015-08-24 21:21:08 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-25 04:21:39 +0000 |
commit | e4f0b8fffdaa39c929fde3dccd1561efbc7b6935 (patch) | |
tree | d128a1133fc51d065d97f49132393565dc28d4ce /media/base/android/java | |
parent | 33050eaa1f93d596b6fa166dfede492645a440f9 (diff) | |
download | chromium_src-e4f0b8fffdaa39c929fde3dccd1561efbc7b6935.zip chromium_src-e4f0b8fffdaa39c929fde3dccd1561efbc7b6935.tar.gz chromium_src-e4f0b8fffdaa39c929fde3dccd1561efbc7b6935.tar.bz2 |
On Nexus9, if you open two webrtc tabs and switch them very quickly, then chrome will stop responding soon.
The reason is tab switching on Android will cause capture start/stop accordingly on the OnWasHidden/OnWasShown events. On N9 it's Camera2 API deployed. And with Camera2, capture will start asynchronously. If stopCapture comes too soon, it will fail and the next startCapture will cause the Camera2 into error status(You can't start capture twice in a row with Camera2 API) and freezes Chrome.
This is a simple/straight patch to make the startCapture return synchronously. Please help to advice if this is proper here, or what other ideas you may have.
BUG=501790
Test=Manual Test
Review URL: https://codereview.chromium.org/1294953004
Cr-Commit-Position: refs/heads/master@{#345254}
Diffstat (limited to 'media/base/android/java')
-rw-r--r-- | media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java | 61 |
1 files changed, 40 insertions, 21 deletions
diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java index 81c147f..81db67a 100644 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java +++ b/media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java @@ -46,10 +46,9 @@ public class VideoCaptureCamera2 extends VideoCapture { @Override public void onOpened(CameraDevice cameraDevice) { mCameraDevice = cameraDevice; - mOpeningCamera = false; - mConfiguringCamera = true; + changeCameraStateAndNotify(CameraState.CONFIGURING); if (!createCaptureObjects()) { - mConfiguringCamera = false; + changeCameraStateAndNotify(CameraState.STOPPED); nativeOnError(mNativeVideoCaptureDeviceAndroid, "Error configuring camera"); } @@ -59,14 +58,14 @@ public class VideoCaptureCamera2 extends VideoCapture { public void onDisconnected(CameraDevice cameraDevice) { cameraDevice.close(); mCameraDevice = null; - mOpeningCamera = false; + changeCameraStateAndNotify(CameraState.STOPPED); } @Override public void onError(CameraDevice cameraDevice, int error) { cameraDevice.close(); mCameraDevice = null; - mOpeningCamera = false; + changeCameraStateAndNotify(CameraState.STOPPED); nativeOnError(mNativeVideoCaptureDeviceAndroid, "Camera device error " + Integer.toString(error)); } @@ -78,14 +77,15 @@ public class VideoCaptureCamera2 extends VideoCapture { public void onConfigured(CameraCaptureSession cameraCaptureSession) { Log.d(TAG, "onConfigured"); mCaptureSession = cameraCaptureSession; - mConfiguringCamera = false; createCaptureRequest(); + changeCameraStateAndNotify(CameraState.STARTED); } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { // TODO(mcasas): When signalling error, C++ will tear us down. Is there need for // cleanup? + changeCameraStateAndNotify(CameraState.STOPPED); nativeOnError(mNativeVideoCaptureDeviceAndroid, "Camera session configuration error"); } @@ -125,14 +125,6 @@ public class VideoCaptureCamera2 extends VideoCapture { private byte[] mCapturedData; - // |mOpeningCamera| is used to signal the transient between openCamera() and - // CrStateListener.onOpened(). - private boolean mOpeningCamera = false; - // |mConfiguringCamera| marks the transient between CrStateListener.onOpened() - // and CrCaptureSessionListener.onConfigured(), including the time it takes - // to createCaptureObjects(). - private boolean mConfiguringCamera = false; - private CameraDevice mCameraDevice = null; private CaptureRequest.Builder mPreviewBuilder = null; private CameraCaptureSession mCaptureSession = null; @@ -141,6 +133,10 @@ public class VideoCaptureCamera2 extends VideoCapture { private static final double kNanoSecondsToFps = 1.0E-9; private static final String TAG = "cr.media"; + private static enum CameraState {OPENING, CONFIGURING, STARTED, STOPPED} + private CameraState mCameraState = CameraState.STOPPED; + private final Object mCameraStateLock = new Object(); + // Service function to grab CameraCharacteristics and handle exceptions. private static CameraCharacteristics getCameraCharacteristics(Context appContext, int id) { final CameraManager manager = @@ -283,6 +279,13 @@ public class VideoCaptureCamera2 extends VideoCapture { } } + private void changeCameraStateAndNotify(CameraState state) { + synchronized (mCameraStateLock) { + mCameraState = state; + mCameraStateLock.notifyAll(); + } + } + static boolean isLegacyDevice(Context appContext, int id) { final CameraCharacteristics cameraCharacteristics = getCameraCharacteristics(appContext, id); @@ -387,9 +390,11 @@ public class VideoCaptureCamera2 extends VideoCapture { @Override public boolean allocate(int width, int height, int frameRate) { Log.d(TAG, "allocate: requested (%d x %d) @%dfps", width, height, frameRate); - if (mOpeningCamera || mConfiguringCamera) { - Log.e(TAG, "allocate() invoked while Camera is busy opening/configuring."); - return false; + synchronized (mCameraStateLock) { + if (mCameraState == CameraState.OPENING || mCameraState == CameraState.CONFIGURING) { + Log.e(TAG, "allocate() invoked while Camera is busy opening/configuring."); + return false; + } } // |mCaptureFormat| is also used to configure the ImageReader. mCaptureFormat = new VideoCaptureFormat(width, height, frameRate, ImageFormat.YUV_420_888); @@ -411,8 +416,7 @@ public class VideoCaptureCamera2 extends VideoCapture { @Override public boolean startCapture() { Log.d(TAG, "startCapture"); - mOpeningCamera = true; - mConfiguringCamera = false; + changeCameraStateAndNotify(CameraState.OPENING); final CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); final Handler mainHandler = new Handler(mContext.getMainLooper()); @@ -429,13 +433,28 @@ public class VideoCaptureCamera2 extends VideoCapture { Log.e(TAG, "allocate: manager.openCamera: " + ex); return false; } + return true; } @Override public boolean stopCapture() { Log.d(TAG, "stopCapture"); - if (mCaptureSession == null) return false; + + // With Camera2 API, the capture is started asynchronously, which will cause problem if + // stopCapture comes too quickly. Without stopping the previous capture properly, the next + // startCapture will fail and make Chrome no-responding. So wait camera to be STARTED. + synchronized (mCameraStateLock) { + while (mCameraState != CameraState.STARTED && mCameraState != CameraState.STOPPED) { + try { + mCameraStateLock.wait(); + } catch (InterruptedException ex) { + Log.e(TAG, "CaptureStartedEvent: " + ex); + } + } + if (mCameraState == CameraState.STOPPED) return true; + } + try { mCaptureSession.abortCaptures(); } catch (CameraAccessException ex) { @@ -447,7 +466,7 @@ public class VideoCaptureCamera2 extends VideoCapture { } if (mCameraDevice == null) return false; mCameraDevice.close(); - + changeCameraStateAndNotify(CameraState.STOPPED); return true; } |