Changes in VideoCapturerAndroid.

- Do not handle more than one camera switch request at a time
to avoid blocking camera thread with multiple switch requests.
- Add a callback to notify when camera switch has been done.

R=perkj@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/46859004

Cr-Commit-Position: refs/heads/master@{#8978}
diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java
index 169a678..c7f3fff 100644
--- a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java
+++ b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java
@@ -201,9 +201,9 @@
     VideoTrack track = factory.createVideoTrack("dummy", source);
 
     if (HaveTwoCameras())
-      assertTrue(capturer.switchCamera());
+      assertTrue(capturer.switchCamera(null));
     else
-      assertFalse(capturer.switchCamera());
+      assertFalse(capturer.switchCamera(null));
 
     // Wait until the camera have been switched.
     capturer.runCameraThreadUntilIdle();
diff --git a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
index e2f67c5..3731355 100644
--- a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
+++ b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
@@ -86,6 +86,7 @@
   private int width;
   private int height;
   private int framerate;
+  private volatile boolean pendingCameraSwitch;
   private CapturerObserver frameObserver = null;
   // List of formats supported by all cameras. This list is filled once in order
   // to be able to switch cameras.
@@ -165,7 +166,7 @@
   // the camera is running.
   // Returns true on success. False if the next camera does not support the
   // current resolution.
-  public synchronized boolean switchCamera() {
+  public synchronized boolean switchCamera(final Runnable switchDoneEvent) {
     if (Camera.getNumberOfCameras() < 2 )
       return false;
 
@@ -173,6 +174,12 @@
       Log.e(TAG, "Camera has not been started");
       return false;
     }
+    if (pendingCameraSwitch) {
+      // Do not handle multiple camera switch request to avoid blocking
+      // camera thread by handling too many switch request from a queue.
+      Log.w(TAG, "Ignoring camera switch request.");
+      return false;
+    }
 
     int new_id = (id + 1) % Camera.getNumberOfCameras();
 
@@ -190,10 +197,11 @@
       return false;
     }
 
+    pendingCameraSwitch = true;
     id = new_id;
     cameraThreadHandler.post(new Runnable() {
       @Override public void run() {
-        switchCameraOnCameraThread();
+        switchCameraOnCameraThread(switchDoneEvent);
       }
     });
     return true;
@@ -347,6 +355,9 @@
     if (width % 16 != 0) {
       throw new RuntimeException("width must be a multiple of 16." );
     }
+    if (cameraThreadHandler != null) {
+      throw new RuntimeException("Camera has already been started.");
+    }
     this.width = width;
     this.height = height;
     this.framerate = framerate;
@@ -442,7 +453,6 @@
     Log.e(TAG, "startCapture failed", error);
     if (camera != null) {
       stopCaptureOnCameraThread();
-      frameObserver.OnCapturerStarted(false);
     }
     frameObserver.OnCapturerStarted(false);
     return;
@@ -450,6 +460,9 @@
 
   // Called by native code.  Returns true when camera is known to be stopped.
   synchronized void stopCapture() throws InterruptedException {
+    if (cameraThreadHandler == null) {
+      throw new RuntimeException("Calling stopCapture() for already stopped camera.");
+    }
     Log.d(TAG, "stopCapture");
     cameraThreadHandler.post(new Runnable() {
         @Override public void run() {
@@ -488,12 +501,17 @@
     }
   }
 
-  private void switchCameraOnCameraThread() {
+  private void switchCameraOnCameraThread(Runnable switchDoneEvent) {
     Log.d(TAG, "switchCameraOnCameraThread");
 
     doStopCaptureOnCamerathread();
     startCaptureOnCameraThread(width, height, framerate, frameObserver,
         applicationContext);
+    pendingCameraSwitch = false;
+    Log.d(TAG, "switchCameraOnCameraThread done");
+    if (switchDoneEvent != null) {
+      switchDoneEvent.run();
+    }
   }
 
   synchronized void returnBuffer(final long timeStamp) {
diff --git a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
index c5beec4..76235a8 100644
--- a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
+++ b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
@@ -818,7 +818,7 @@
       return;  // No video is sent or only one camera is available or error happened.
     }
     Log.d(TAG, "Switch camera");
-    videoCapturer.switchCamera();
+    videoCapturer.switchCamera(null);
   }
 
   public void switchCamera() {