From 3bfef44a4de22c562bdfd787872ef4a13aa1ad60 Mon Sep 17 00:00:00 2001 From: perkj Date: Sun, 8 Nov 2015 06:58:15 -0800 Subject: [PATCH] Changed timeout to 6s for reporting android camera freeze. Also distinguish between camera failures and failures due to that buffers has not been returned. Adds unit tests for making sure CameraEventHandler.onError is triggered if frames are not returned. BUG=b/25514149 Review URL: https://codereview.webrtc.org/1415013006 Cr-Commit-Position: refs/heads/master@{#10555} --- .../org/webrtc/VideoCapturerAndroidTest.java | 12 ++++++ .../VideoCapturerAndroidTestFixtures.java | 37 ++++++++++++++++++- .../org/webrtc/VideoCapturerAndroid.java | 18 +++++++-- 3 files changed, 62 insertions(+), 5 deletions(-) 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); } };