Bug 1452048 - Give Looper lifetime control to external callers. r?dminor draft
authorAndreas Pehrson <pehrsons@mozilla.com>
Fri, 06 Apr 2018 13:59:45 +0200
changeset 779266 f9e890d8421b2755b9301d86993faba8f0aefa6d
parent 779146 30d72755b1749953d438199456f1524ce84ab5e5
child 779267 d1f09e0adfc2fb8c0ff795d473712c5b93ff7b73
push id105725
push userbmo:apehrson@mozilla.com
push dateMon, 09 Apr 2018 15:22:26 +0000
reviewersdminor
bugs1452048
milestone61.0a1
Bug 1452048 - Give Looper lifetime control to external callers. r?dminor Looper lifetime must be handled by external callers as otherwise internal callers may accidentally and unknowingly quit the looper and essentially terminate the camera thread, stopping any flow of frames and preventing future messages. In this case, a reconfig of the capture settings through startCapture() causes startCaptureOnCameraThread() to restart the capture by calling into stopCaptureOnCameraThread() (quitting the looper) and then re-calling startCaptureOnCameraThread() (not restarting the looper as that is invalid). The camera thread is set up by startCapture() on the external calling thread, which will never know that a seemingly graceful camera thread operation stopped the camera thread altogether, and so it cannot take any action. MozReview-Commit-ID: DUTFdanTan1
media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
--- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
@@ -116,16 +116,19 @@ public class VideoCaptureAndroid impleme
       cameraThreadHandler = exchange(handlerExchanger, null);
     }
 
     final Exchanger<Boolean> result = new Exchanger<Boolean>();
     cameraThreadHandler.post(new Runnable() {
         @Override public void run() {
           boolean startResult =
             startCaptureOnCameraThread(width, height, min_mfps, max_mfps);
+          if (!startResult) {
+            Looper.myLooper().quit();
+          }
           exchange(result, startResult);
         }
       });
     boolean startResult = exchange(result, false); // |false| is a dummy value.
 
     if (!startResult) {
       // Starting failed on the camera thread. The looper has now quit and the
       // camera thread is dead.
@@ -311,16 +314,17 @@ public class VideoCaptureAndroid impleme
   // Called by native code.  Returns true when camera is known to be stopped.
   @WebRTCJNITarget
   private synchronized boolean stopCapture() {
     Log.d(TAG, "stopCapture");
     final Exchanger<Boolean> result = new Exchanger<Boolean>();
     cameraThreadHandler.post(new Runnable() {
         @Override public void run() {
           boolean stopResult = stopCaptureOnCameraThread();
+          Looper.myLooper().quit();
           exchange(result, stopResult);
         }
       });
     boolean status = exchange(result, false);  // |false| is a dummy value here.
     try {
       cameraThread.join();
     } catch (InterruptedException e) {
       throw new RuntimeException(e);
@@ -343,25 +347,23 @@ public class VideoCaptureAndroid impleme
       camera.setPreviewTexture(null);
       cameraSurfaceTexture = null;
       if (cameraGlTextures != null) {
         GLES20.glDeleteTextures(1, cameraGlTextures, 0);
         cameraGlTextures = null;
       }
       camera.release();
       camera = null;
-      Looper.myLooper().quit();
       return true;
     } catch (IOException e) {
       error = e;
     } catch (RuntimeException e) {
       error = e;
     }
     Log.e(TAG, "Failed to stop camera", error);
-    Looper.myLooper().quit();
     return false;
   }
 
   @WebRTCJNITarget
   private int getDeviceOrientation() {
     int orientation = 0;
     if (context != null) {
       WindowManager wm = (WindowManager) context.getSystemService(