Make CameraCapturer.switchCamera try again if session is still opening.

R=magjed@webrtc.org

Review-Url: https://codereview.webrtc.org/2238263002
Cr-Original-Commit-Position: refs/heads/master@{#13747}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 92c09509bdf0287c7af5c1bbde87dd188acf1879
diff --git a/api/android/java/src/org/webrtc/Camera2Session.java b/api/android/java/src/org/webrtc/Camera2Session.java
index 5e32c27..9bcdfaf 100644
--- a/api/android/java/src/org/webrtc/Camera2Session.java
+++ b/api/android/java/src/org/webrtc/Camera2Session.java
@@ -318,21 +318,35 @@
   @Override
   public void stop() {
     Logging.d(TAG, "Stop camera2 session on camera " + cameraId);
-    final CountDownLatch stopLatch = new CountDownLatch(1);
-
-    cameraThreadHandler.post(new Runnable() {
-      @Override
-      public void run() {
-        if (state != SessionState.STOPPED) {
-          state = SessionState.STOPPED;
-          capturerObserver.onCapturerStopped();
-          stopLatch.countDown();
-          stopInternal();
-        }
+    if (Thread.currentThread() == cameraThreadHandler.getLooper().getThread()) {
+      if (state != SessionState.STOPPED) {
+        state = SessionState.STOPPED;
+        capturerObserver.onCapturerStopped();
+        // Post the stopInternal to return earlier.
+        cameraThreadHandler.post(new Runnable() {
+          @Override
+          public void run() {
+            stopInternal();
+          }
+        });
       }
-    });
+    } else {
+      final CountDownLatch stopLatch = new CountDownLatch(1);
 
-    ThreadUtils.awaitUninterruptibly(stopLatch);
+      cameraThreadHandler.post(new Runnable() {
+        @Override
+        public void run() {
+          if (state != SessionState.STOPPED) {
+            state = SessionState.STOPPED;
+            capturerObserver.onCapturerStopped();
+            stopLatch.countDown();
+            stopInternal();
+          }
+        }
+      });
+
+      ThreadUtils.awaitUninterruptibly(stopLatch);
+    }
   }
 
   private void stopInternal() {
diff --git a/api/android/java/src/org/webrtc/CameraCapturer.java b/api/android/java/src/org/webrtc/CameraCapturer.java
index 75712cc..7e7d236 100644
--- a/api/android/java/src/org/webrtc/CameraCapturer.java
+++ b/api/android/java/src/org/webrtc/CameraCapturer.java
@@ -10,17 +10,19 @@
 
 package org.webrtc;
 
-import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
-
 import android.content.Context;
 import android.os.Handler;
-import android.os.SystemClock;
 
 import java.util.Arrays;
-import java.util.List;
 
 @SuppressWarnings("deprecation")
 public abstract class CameraCapturer implements CameraVideoCapturer {
+  enum SwitchState {
+    IDLE,        // No switch requested.
+    PENDING,     // Waiting for previous capture session to open.
+    IN_PROGRESS, // Waiting for new switched capture session to start.
+  }
+
   private static final String TAG = "CameraCapturer";
   private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3;
   private final static int OPEN_CAMERA_DELAY_MS = 500;
@@ -39,12 +41,17 @@
             currentSession = session;
             stateLock.notifyAll();
 
-            if (switchEventsHandler != null) {
-              switchEventsHandler.onCameraSwitchDone(
-                  cameraEnumerator.isFrontFacing(cameraName));
-              switchEventsHandler = null;
+            if (switchState == SwitchState.IN_PROGRESS) {
+              if (switchEventsHandler != null) {
+                switchEventsHandler.onCameraSwitchDone(
+                    cameraEnumerator.isFrontFacing(cameraName));
+                switchEventsHandler = null;
+              }
+              switchState = SwitchState.IDLE;
+            } else if (switchState == SwitchState.PENDING) {
+              switchState = SwitchState.IDLE;
+              switchCameraInternal(switchEventsHandler);
             }
-            switchInProgress = false;
           }
         }
 
@@ -58,11 +65,13 @@
               sessionOpening = false;
               stateLock.notifyAll();
 
-              if (switchEventsHandler != null) {
-                switchEventsHandler.onCameraSwitchError(error);
-                switchEventsHandler = null;
+              if (switchState != SwitchState.IDLE) {
+                if (switchEventsHandler != null) {
+                  switchEventsHandler.onCameraSwitchError(error);
+                  switchEventsHandler = null;
+                }
+                switchState = SwitchState.IDLE;
               }
-              switchInProgress = false;
 
               eventsHandler.onCameraError(error);
             } else {
@@ -76,8 +85,6 @@
 
   // Initialized on initialize
   // -------------------------
-  // Use postOnCameraThread() instead of posting directly to the handler - this way all
-  // callbacks with a specifed token can be removed at once.
   private Handler cameraThreadHandler;
   private Context applicationContext;
   private CapturerObserver capturerObserver;
@@ -91,7 +98,7 @@
   private int height;                              /* guarded by stateLock */
   private int framerate;                           /* guarded by stateLock */
   private int openAttemptsRemaining;               /* guarded by stateLock */
-  private boolean switchInProgress;                /* guarded by stateLock */
+  private SwitchState switchState = SwitchState.IDLE; /* guarded by stateLock */
   private CameraSwitchHandler switchEventsHandler; /* guarded by stateLock */
 
   public CameraCapturer(
@@ -219,6 +226,16 @@
   @Override
   public void switchCamera(final CameraSwitchHandler switchEventsHandler) {
     Logging.d(TAG, "switchCamera");
+    cameraThreadHandler.post(new Runnable() {
+      @Override
+      public void run() {
+        switchCameraInternal(switchEventsHandler);
+      }
+    });
+  }
+
+  private void switchCameraInternal(final CameraSwitchHandler switchEventsHandler) {
+    Logging.d(TAG, "switchCamera internal");
 
     final String[] deviceNames = cameraEnumerator.getDeviceNames();
 
@@ -230,7 +247,7 @@
     }
 
     synchronized (stateLock) {
-      if (switchInProgress) {
+      if (switchState != SwitchState.IDLE) {
         Logging.d(TAG, "switchCamera switchInProgress");
         if (switchEventsHandler != null) {
           switchEventsHandler.onCameraSwitchError("Camera switch already in progress.");
@@ -238,12 +255,12 @@
         return;
       }
 
+      this.switchEventsHandler = switchEventsHandler;
       if (sessionOpening) {
-        Logging.d(TAG, "switchCamera sessionOpening");
-        if (switchEventsHandler != null) {
-          switchEventsHandler.onCameraSwitchError("Session is still opening.");
-        }
+        switchState = SwitchState.PENDING;
         return;
+      } else {
+        switchState = SwitchState.IN_PROGRESS;
       }
 
       if (currentSession == null) {
@@ -261,8 +278,6 @@
       int cameraNameIndex = Arrays.asList(deviceNames).indexOf(cameraName);
       cameraName = deviceNames[(cameraNameIndex + 1) % deviceNames.length];
 
-      switchInProgress = true;
-      this.switchEventsHandler = switchEventsHandler;
       sessionOpening = true;
       openAttemptsRemaining = 1;
       createSessionInternal(0);