VideoCaptureAndroid: quit & join the camera thread on stopCapture.

Also fix latent bug where setPreviewRotation() wouldn't hold
the lock while its delegate setPreviewRotationOnCameraThread()
was running, allowing the camera to be freed between the
null-check and the use.

BUG=3389
R=wu@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6266 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java b/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
index 80f6f63..0e555f9 100644
--- a/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
+++ b/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
@@ -66,10 +66,6 @@
     this.native_capturer = native_capturer;
     this.info = new Camera.CameraInfo();
     Camera.getCameraInfo(id, info);
-    Exchanger<Handler> handlerExchanger = new Exchanger<Handler>();
-    cameraThread = new CameraThread(handlerExchanger);
-    cameraThread.start();
-    cameraThreadHandler = exchange(handlerExchanger, null);
   }
 
   private class CameraThread extends Thread {
@@ -93,6 +89,14 @@
   private synchronized boolean startCapture(
       final int width, final int height,
       final int min_mfps, final int max_mfps) {
+    if (cameraThread != null || cameraThreadHandler != null) {
+      throw new RuntimeException("Camera thread already started!");
+    }
+    Exchanger<Handler> handlerExchanger = new Exchanger<Handler>();
+    cameraThread = new CameraThread(handlerExchanger);
+    cameraThread.start();
+    cameraThreadHandler = exchange(handlerExchanger, null);
+
     final Exchanger<Boolean> result = new Exchanger<Boolean>();
     cameraThreadHandler.post(new Runnable() {
         @Override public void run() {
@@ -174,12 +178,21 @@
           stopCaptureOnCameraThread(result);
         }
       });
-    return exchange(result, false);  // |false| is a dummy value here.
+    boolean status = exchange(result, false);  // |false| is a dummy value here.
+    try {
+      cameraThread.join();
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    }
+    cameraThreadHandler = null;
+    cameraThread = null;
+    return status;
   }
 
   private void stopCaptureOnCameraThread(
       Exchanger<Boolean> result) {
     Log.d(TAG, "stopCapture");
+    Looper.myLooper().quit();
     if (camera == null) {
       throw new RuntimeException("Camera is already stopped!");
     }
@@ -225,19 +238,24 @@
   // Does not affect the captured video image.
   // Called by native code.
   private synchronized void setPreviewRotation(final int rotation) {
-    cameraThreadHandler.post(new Runnable() {
-        @Override public void run() {
-          setPreviewRotationOnCameraThread(rotation);
-        }
-      });
-  }
-
-  private void setPreviewRotationOnCameraThread(int rotation) {
-    Log.v(TAG, "setPreviewRotation:" + rotation);
-
-    if (camera == null) {
+    if (camera == null || cameraThreadHandler == null) {
       return;
     }
+    final Exchanger<IOException> result = new Exchanger<IOException>();
+    cameraThreadHandler.post(new Runnable() {
+        @Override public void run() {
+          setPreviewRotationOnCameraThread(rotation, result);
+        }
+      });
+    // Use the exchanger below to block this function until
+    // setPreviewRotationOnCameraThread() completes, holding the synchronized
+    // lock for the duration.  The exchanged value itself is ignored.
+    exchange(result, null);
+  }
+
+  private void setPreviewRotationOnCameraThread(
+      int rotation, Exchanger<IOException> result) {
+    Log.v(TAG, "setPreviewRotation:" + rotation);
 
     int resultRotation = 0;
     if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
@@ -249,6 +267,7 @@
       resultRotation = rotation;
     }
     camera.setDisplayOrientation(resultRotation);
+    exchange(result, null);
   }
 
   public synchronized void surfaceChanged(
@@ -259,7 +278,7 @@
 
   public synchronized void surfaceCreated(final SurfaceHolder holder) {
     Log.d(TAG, "VideoCaptureAndroid::surfaceCreated");
-    if (camera == null) {
+    if (camera == null || cameraThreadHandler == null) {
       return;
     }
     final Exchanger<IOException> result = new Exchanger<IOException>();
@@ -276,7 +295,7 @@
 
   public synchronized void surfaceDestroyed(SurfaceHolder holder) {
     Log.d(TAG, "VideoCaptureAndroid::surfaceDestroyed");
-    if (camera == null) {
+    if (camera == null || cameraThreadHandler == null) {
       return;
     }
     final Exchanger<IOException> result = new Exchanger<IOException>();