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
--- 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(