Index: talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java |
diff --git a/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java |
index 88163ef6c8d2870ee17ac07d7dfea74b4a480b5f..6dbf0039a3bc6cf705923dfef531237b8bd0cc28 100644 |
--- a/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java |
+++ b/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java |
@@ -100,6 +100,12 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba |
// The camera API can output one old frame after the camera has been switched or the resolution |
// has been changed. This flag is used for dropping the first frame after camera restart. |
private boolean dropNextFrame = false; |
+ // |openCameraOnCodecThreadRunner| is used for retrying to open the camera if it is in use by |
+ // another application when startCaptureOnCameraThread is called. |
+ private Runnable openCameraOnCodecThreadRunner; |
+ private final static int MAX_OPEN_CAMERA_ATTEMPTS = 3; |
+ private final static int OPEN_CAMERA_DELAY_MS = 300; |
+ private int openCameraAttempts; |
// Camera error callback. |
private final Camera.ErrorCallback cameraErrorCallback = |
@@ -313,7 +319,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba |
// Helper function to retrieve the current camera id synchronously. Note that the camera id might |
// change at any point by switchCamera() calls. |
- private int getCurrentCameraId() { |
+ int getCurrentCameraId() { |
synchronized (cameraIdLock) { |
return id; |
} |
@@ -423,6 +429,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba |
if (frameObserver == null) { |
throw new RuntimeException("frameObserver not set."); |
} |
+ |
cameraThreadHandler.post(new Runnable() { |
@Override public void run() { |
startCaptureOnCameraThread(width, height, framerate, frameObserver, |
@@ -432,8 +439,8 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba |
} |
private void startCaptureOnCameraThread( |
- int width, int height, int framerate, CapturerObserver frameObserver, |
- Context applicationContext) { |
+ final int width, final int height, final int framerate, final CapturerObserver frameObserver, |
+ final Context applicationContext) { |
Throwable error = null; |
checkIsOnCameraThread(); |
if (camera != null) { |
@@ -441,17 +448,36 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba |
} |
this.applicationContext = applicationContext; |
this.frameObserver = frameObserver; |
+ this.firstFrameReported = false; |
+ |
try { |
- synchronized (cameraIdLock) { |
- Logging.d(TAG, "Opening camera " + id); |
- firstFrameReported = false; |
- if (eventsHandler != null) { |
- eventsHandler.onCameraOpening(id); |
+ try { |
+ synchronized (cameraIdLock) { |
+ Logging.d(TAG, "Opening camera " + id); |
+ if (eventsHandler != null) { |
+ eventsHandler.onCameraOpening(id); |
+ } |
+ camera = Camera.open(id); |
+ info = new Camera.CameraInfo(); |
+ Camera.getCameraInfo(id, info); |
+ } |
+ } catch (RuntimeException e) { |
+ openCameraAttempts++; |
+ if (openCameraAttempts < MAX_OPEN_CAMERA_ATTEMPTS) { |
+ Logging.e(TAG, "Camera.open failed, retrying", e); |
+ openCameraOnCodecThreadRunner = new Runnable() { |
+ @Override public void run() { |
+ startCaptureOnCameraThread(width, height, framerate, frameObserver, |
+ applicationContext); |
+ } |
+ }; |
+ cameraThreadHandler.postDelayed(openCameraOnCodecThreadRunner, OPEN_CAMERA_DELAY_MS); |
+ return; |
} |
- camera = Camera.open(id); |
- info = new Camera.CameraInfo(); |
- Camera.getCameraInfo(id, info); |
+ openCameraAttempts = 0; |
+ throw new RuntimeException(e); |
} |
+ |
try { |
camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); |
} catch (IOException e) { |
@@ -571,6 +597,10 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba |
private void stopCaptureOnCameraThread() { |
checkIsOnCameraThread(); |
Logging.d(TAG, "stopCaptureOnCameraThread"); |
+ if (openCameraOnCodecThreadRunner != null) { |
+ cameraThreadHandler.removeCallbacks(openCameraOnCodecThreadRunner); |
+ } |
+ openCameraAttempts = 0; |
if (camera == null) { |
Logging.e(TAG, "Calling stopCapture() for already stopped camera."); |
return; |
@@ -622,6 +652,11 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba |
frameObserver.onOutputFormatRequest(width, height, framerate); |
} |
+ // Exposed for testing purposes only. |
+ Handler getCameraThreadHandler() { |
+ return cameraThreadHandler; |
+ } |
+ |
public void returnBuffer(final long timeStamp) { |
cameraThreadHandler.post(new Runnable() { |
@Override public void run() { |