diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java index dbbe5963cd..54fa5d4c86 100644 --- a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java +++ b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java @@ -258,4 +258,16 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { VideoCapturerAndroid.create("", null, EGL10.EGL_NO_CONTEXT); VideoCapturerAndroidTestFixtures.returnBufferLateEndToEnd(capturer); } + + + @MediumTest + // This test that CameraEventsHandler.onError is triggered if video buffers are not returned to + // the capturer. + public void testCameraErrorEventOnBufferStarvation() throws InterruptedException { + VideoCapturerAndroidTestFixtures.CameraEvents cameraEvents = + VideoCapturerAndroidTestFixtures.createCameraEvents(); + VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", cameraEvents); + VideoCapturerAndroidTestFixtures.cameraErrorEventOnBufferStarvation(capturer, + cameraEvents, getInstrumentation().getContext()); + } } diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java index 11b3ce98a0..237c6d8a49 100644 --- a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java +++ b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java @@ -174,9 +174,16 @@ public class VideoCapturerAndroidTestFixtures { VideoCapturerAndroid.CameraEventsHandler { public boolean onCameraOpeningCalled; public boolean onFirstFrameAvailableCalled; + public final Object onCameraErrorLock = new Object(); + private String onCameraErrorDescription; @Override - public void onCameraError(String errorDescription) { } + public void onCameraError(String errorDescription) { + synchronized (onCameraErrorLock) { + onCameraErrorDescription = errorDescription; + onCameraErrorLock.notifyAll(); + } + } @Override public void onCameraOpening(int cameraId) { @@ -190,6 +197,13 @@ public class VideoCapturerAndroidTestFixtures { @Override public void onCameraClosed() { } + + public String WaitForCameraError() throws InterruptedException { + synchronized (onCameraErrorLock) { + onCameraErrorLock.wait(); + return onCameraErrorDescription; + } + } } static public CameraEvents createCameraEvents() { @@ -440,4 +454,25 @@ public class VideoCapturerAndroidTestFixtures { // Check that frames have successfully returned. This will cause |capturer| to be released. assertTrue(capturer.isReleased()); } + + static public void cameraErrorEventOnBufferStarvation(VideoCapturerAndroid capturer, + CameraEvents events, Context appContext) throws InterruptedException { + final List formats = capturer.getSupportedFormats(); + final CameraEnumerationAndroid.CaptureFormat format = formats.get(0); + + final FakeCapturerObserver observer = new FakeCapturerObserver(); + capturer.startCapture(format.width, format.height, format.maxFramerate, + appContext, observer); + // Make sure camera is started. + assertTrue(observer.WaitForCapturerToStart()); + // Since we don't call returnBuffer, we should get a starvation message. + assertEquals("Camera failure. Client must return video buffers.", events.WaitForCameraError()); + + capturer.stopCapture(); + for (long timeStamp : observer.getCopyAndResetListOftimeStamps()) { + capturer.returnBuffer(timeStamp); + } + capturer.dispose(); + assertTrue(capturer.isReleased()); + } } diff --git a/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java index 4caefc513d..88163ef6c8 100644 --- a/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java +++ b/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java @@ -72,6 +72,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba SurfaceTextureHelper.OnTextureFrameAvailableListener { private final static String TAG = "VideoCapturerAndroid"; private final static int CAMERA_OBSERVER_PERIOD_MS = 2000; + private final static int CAMERA_FREEZE_REPORT_TIMOUT_MS = 6000; private Camera camera; // Only non-null while capturing. private HandlerThread cameraThread; @@ -120,6 +121,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba // Camera observer - monitors camera framerate. Observer is executed on camera thread. private final Runnable cameraObserver = new Runnable() { + private int freezePeriodCount; @Override public void run() { int cameraFramesCount = cameraStatistics.getAndResetFrameCount(); @@ -129,13 +131,21 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba Logging.d(TAG, "Camera fps: " + cameraFps + ". Pending buffers: " + cameraStatistics.pendingFramesTimeStamps()); if (cameraFramesCount == 0) { - Logging.e(TAG, "Camera freezed."); - if (eventsHandler != null) { - eventsHandler.onCameraError("Camera failure."); + ++freezePeriodCount; + if (CAMERA_OBSERVER_PERIOD_MS * freezePeriodCount > CAMERA_FREEZE_REPORT_TIMOUT_MS && + eventsHandler != null) { + Logging.e(TAG, "Camera freezed."); + if (cameraStatistics.pendingFramesCount() > 0) { + eventsHandler.onCameraError("Camera failure. Client must return video buffers."); + } else { + eventsHandler.onCameraError("Camera failure."); + } + return; } } else { - cameraThreadHandler.postDelayed(this, CAMERA_OBSERVER_PERIOD_MS); + freezePeriodCount = 0; } + cameraThreadHandler.postDelayed(this, CAMERA_OBSERVER_PERIOD_MS); } };