| Index: media/base/android/java/src/org/chromium/media/VideoCaptureCamera2.java
|
| 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 49b8cf34153771e8ceba7fd63d561ea2763f2967..91aeee8cc93ce21aafce1194080b974878b8ee6d 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 @@
|
| @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 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 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 @@
|
|
|
| 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;
|
| @@ -140,6 +132,10 @@
|
|
|
| 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) {
|
| @@ -280,6 +276,13 @@
|
| data[offset++] = rowData[col * pixelStride];
|
| }
|
| }
|
| + }
|
| + }
|
| +
|
| + private void changeCameraStateAndNotify(CameraState state) {
|
| + synchronized (mCameraStateLock) {
|
| + mCameraState = state;
|
| + mCameraStateLock.notifyAll();
|
| }
|
| }
|
|
|
| @@ -387,9 +390,11 @@
|
| @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 @@
|
| @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 @@
|
| 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) {
|
| @@ -450,7 +469,7 @@
|
| }
|
| if (mCameraDevice == null) return false;
|
| mCameraDevice.close();
|
| -
|
| + changeCameraStateAndNotify(CameraState.STOPPED);
|
| return true;
|
| }
|
|
|
|
|