From 92379de5c66f27a9a643c0c7fbe785d0dde35542 Mon Sep 17 00:00:00 2001 From: nisse Date: Mon, 30 May 2016 23:36:01 -0700 Subject: [PATCH] Reorder actions on stopCapturer, to avoid crashing on camera timeout. BUG= Review-Url: https://codereview.webrtc.org/2003973003 Cr-Commit-Position: refs/heads/master@{#12963} --- .../org/webrtc/VideoCapturerAndroid.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java b/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java index d9793cf591..9efd23971f 100644 --- a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java +++ b/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java @@ -372,12 +372,7 @@ public class VideoCapturerAndroid implements } Logging.e(TAG, "startCapture failed", error); // Make sure the camera is released. - stopCaptureOnCameraThread(); - synchronized (handlerLock) { - // Remove all pending Runnables posted from |this|. - cameraThreadHandler.removeCallbacksAndMessages(this /* token */); - cameraThreadHandler = null; - } + stopCaptureOnCameraThread(true /* stopHandler */); frameObserver.onCapturerStarted(false); if (eventsHandler != null) { eventsHandler.onCameraError("Camera can not be started."); @@ -488,13 +483,7 @@ public class VideoCapturerAndroid implements final CountDownLatch barrier = new CountDownLatch(1); final boolean didPost = maybePostOnCameraThread(new Runnable() { @Override public void run() { - stopCaptureOnCameraThread(); - synchronized (handlerLock) { - // Remove all pending Runnables posted from |this|. - cameraThreadHandler.removeCallbacksAndMessages(this /* token */); - cameraThreadHandler = null; - surfaceHelper = null; - } + stopCaptureOnCameraThread(true /* stopHandler */); barrier.countDown(); } }); @@ -512,7 +501,7 @@ public class VideoCapturerAndroid implements Logging.d(TAG, "stopCapture done"); } - private void stopCaptureOnCameraThread() { + private void stopCaptureOnCameraThread(boolean stopHandler) { checkIsOnCameraThread(); Logging.d(TAG, "stopCaptureOnCameraThread"); // Note that the camera might still not be started here if startCaptureOnCameraThread failed @@ -522,6 +511,21 @@ public class VideoCapturerAndroid implements if (surfaceHelper != null) { surfaceHelper.stopListening(); } + if (stopHandler) { + synchronized (handlerLock) { + // Clear the cameraThreadHandler first, in case stopPreview or + // other driver code deadlocks. Deadlock in + // android.hardware.Camera._stopPreview(Native Method) has + // been observed on Nexus 5 (hammerhead), OS version LMY48I. + // The camera might post another one or two preview frames + // before stopped, so we have to check for a null + // cameraThreadHandler in our handler. Remove all pending + // Runnables posted from |this|. + cameraThreadHandler.removeCallbacksAndMessages(this /* token */); + cameraThreadHandler = null; + surfaceHelper = null; + } + } if (cameraStatistics != null) { cameraStatistics.release(); cameraStatistics = null; @@ -548,7 +552,7 @@ public class VideoCapturerAndroid implements private void switchCameraOnCameraThread() { checkIsOnCameraThread(); Logging.d(TAG, "switchCameraOnCameraThread"); - stopCaptureOnCameraThread(); + stopCaptureOnCameraThread(false /* stopHandler */); synchronized (cameraIdLock) { id = (id + 1) % android.hardware.Camera.getNumberOfCameras(); } @@ -637,9 +641,7 @@ public class VideoCapturerAndroid implements @Override public void onTextureFrameAvailable( int oesTextureId, float[] transformMatrix, long timestampNs) { - if (cameraThreadHandler == null) { - throw new RuntimeException("onTextureFrameAvailable() called after stopCapture()."); - } + checkIsOnCameraThread(); if (eventsHandler != null && !firstFrameReported) { eventsHandler.onFirstFrameAvailable();