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}
This commit is contained in:
Alex Glaznev 2015-04-10 11:19:52 -07:00
parent f4acf46c86
commit e4ae8d8558
3 changed files with 25 additions and 7 deletions

View File

@ -201,9 +201,9 @@ public class VideoCapturerAndroidTest extends ActivityTestCase {
VideoTrack track = factory.createVideoTrack("dummy", source); VideoTrack track = factory.createVideoTrack("dummy", source);
if (HaveTwoCameras()) if (HaveTwoCameras())
assertTrue(capturer.switchCamera()); assertTrue(capturer.switchCamera(null));
else else
assertFalse(capturer.switchCamera()); assertFalse(capturer.switchCamera(null));
// Wait until the camera have been switched. // Wait until the camera have been switched.
capturer.runCameraThreadUntilIdle(); capturer.runCameraThreadUntilIdle();

View File

@ -86,6 +86,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
private int width; private int width;
private int height; private int height;
private int framerate; private int framerate;
private volatile boolean pendingCameraSwitch;
private CapturerObserver frameObserver = null; private CapturerObserver frameObserver = null;
// List of formats supported by all cameras. This list is filled once in order // List of formats supported by all cameras. This list is filled once in order
// to be able to switch cameras. // to be able to switch cameras.
@ -165,7 +166,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
// the camera is running. // the camera is running.
// Returns true on success. False if the next camera does not support the // Returns true on success. False if the next camera does not support the
// current resolution. // current resolution.
public synchronized boolean switchCamera() { public synchronized boolean switchCamera(final Runnable switchDoneEvent) {
if (Camera.getNumberOfCameras() < 2 ) if (Camera.getNumberOfCameras() < 2 )
return false; return false;
@ -173,6 +174,12 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
Log.e(TAG, "Camera has not been started"); Log.e(TAG, "Camera has not been started");
return false; 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(); int new_id = (id + 1) % Camera.getNumberOfCameras();
@ -190,10 +197,11 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
return false; return false;
} }
pendingCameraSwitch = true;
id = new_id; id = new_id;
cameraThreadHandler.post(new Runnable() { cameraThreadHandler.post(new Runnable() {
@Override public void run() { @Override public void run() {
switchCameraOnCameraThread(); switchCameraOnCameraThread(switchDoneEvent);
} }
}); });
return true; return true;
@ -347,6 +355,9 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
if (width % 16 != 0) { if (width % 16 != 0) {
throw new RuntimeException("width must be a multiple of 16." ); 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.width = width;
this.height = height; this.height = height;
this.framerate = framerate; this.framerate = framerate;
@ -442,7 +453,6 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
Log.e(TAG, "startCapture failed", error); Log.e(TAG, "startCapture failed", error);
if (camera != null) { if (camera != null) {
stopCaptureOnCameraThread(); stopCaptureOnCameraThread();
frameObserver.OnCapturerStarted(false);
} }
frameObserver.OnCapturerStarted(false); frameObserver.OnCapturerStarted(false);
return; return;
@ -450,6 +460,9 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
// Called by native code. Returns true when camera is known to be stopped. // Called by native code. Returns true when camera is known to be stopped.
synchronized void stopCapture() throws InterruptedException { synchronized void stopCapture() throws InterruptedException {
if (cameraThreadHandler == null) {
throw new RuntimeException("Calling stopCapture() for already stopped camera.");
}
Log.d(TAG, "stopCapture"); Log.d(TAG, "stopCapture");
cameraThreadHandler.post(new Runnable() { cameraThreadHandler.post(new Runnable() {
@Override public void run() { @Override public void run() {
@ -488,12 +501,17 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba
} }
} }
private void switchCameraOnCameraThread() { private void switchCameraOnCameraThread(Runnable switchDoneEvent) {
Log.d(TAG, "switchCameraOnCameraThread"); Log.d(TAG, "switchCameraOnCameraThread");
doStopCaptureOnCamerathread(); doStopCaptureOnCamerathread();
startCaptureOnCameraThread(width, height, framerate, frameObserver, startCaptureOnCameraThread(width, height, framerate, frameObserver,
applicationContext); applicationContext);
pendingCameraSwitch = false;
Log.d(TAG, "switchCameraOnCameraThread done");
if (switchDoneEvent != null) {
switchDoneEvent.run();
}
} }
synchronized void returnBuffer(final long timeStamp) { synchronized void returnBuffer(final long timeStamp) {

View File

@ -818,7 +818,7 @@ public class PeerConnectionClient {
return; // No video is sent or only one camera is available or error happened. return; // No video is sent or only one camera is available or error happened.
} }
Log.d(TAG, "Switch camera"); Log.d(TAG, "Switch camera");
videoCapturer.switchCamera(); videoCapturer.switchCamera(null);
} }
public void switchCamera() { public void switchCamera() {