Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java

Issue 1350863002: VideoCapturerAndroid: Fix threading issues (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Updated comments and renamed FakeAsyncRenderer Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * libjingle 2 * libjingle
3 * Copyright 2015 Google Inc. 3 * Copyright 2015 Google Inc.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met: 6 * modification, are permitted provided that the following conditions are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright notice, 8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer. 9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice, 10 * 2. Redistributions in binary form must reproduce the above copyright notice,
(...skipping 11 matching lines...) Expand all
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 package org.webrtc; 27 package org.webrtc;
28 28
29 import android.hardware.Camera; 29 import android.hardware.Camera;
30 import android.test.ActivityTestCase; 30 import android.test.ActivityTestCase;
31 import android.test.suitebuilder.annotation.SmallTest; 31 import android.test.suitebuilder.annotation.SmallTest;
32 import android.test.suitebuilder.annotation.MediumTest;
32 import android.util.Size; 33 import android.util.Size;
33 34
34 import org.webrtc.CameraEnumerationAndroid.CaptureFormat; 35 import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
35 import org.webrtc.VideoRenderer.I420Frame; 36 import org.webrtc.VideoRenderer.I420Frame;
36 37
37 import java.util.ArrayList; 38 import java.util.ArrayList;
38 import java.util.HashSet; 39 import java.util.HashSet;
39 import java.util.List; 40 import java.util.List;
40 import java.util.Set; 41 import java.util.Set;
42 import java.util.concurrent.CountDownLatch;
41 43
42 @SuppressWarnings("deprecation") 44 @SuppressWarnings("deprecation")
43 public class VideoCapturerAndroidTest extends ActivityTestCase { 45 public class VideoCapturerAndroidTest extends ActivityTestCase {
44 static class RendererCallbacks implements VideoRenderer.Callbacks { 46 static class RendererCallbacks implements VideoRenderer.Callbacks {
45 private int framesRendered = 0; 47 private int framesRendered = 0;
46 private Object frameLock = 0; 48 private Object frameLock = 0;
47 49
48 @Override 50 @Override
49 public void renderFrame(I420Frame frame) { 51 public void renderFrame(I420Frame frame) {
50 synchronized (frameLock) { 52 synchronized (frameLock) {
51 ++framesRendered; 53 ++framesRendered;
52 frameLock.notify(); 54 frameLock.notify();
53 } 55 }
54 VideoRenderer.renderFrameDone(frame); 56 VideoRenderer.renderFrameDone(frame);
55 } 57 }
56 58
57 public int WaitForNextFrameToRender() throws InterruptedException { 59 public int WaitForNextFrameToRender() throws InterruptedException {
58 synchronized (frameLock) { 60 synchronized (frameLock) {
59 frameLock.wait(); 61 frameLock.wait();
60 return framesRendered; 62 return framesRendered;
61 } 63 }
62 } 64 }
63 } 65 }
64 66
65 static class AsyncRenderer implements VideoRenderer.Callbacks { 67 static class FakeAsyncRenderer implements VideoRenderer.Callbacks {
66 private final List<I420Frame> pendingFrames = new ArrayList<I420Frame>(); 68 private final List<I420Frame> pendingFrames = new ArrayList<I420Frame>();
67 69
68 @Override 70 @Override
69 public void renderFrame(I420Frame frame) { 71 public void renderFrame(I420Frame frame) {
70 synchronized (pendingFrames) { 72 synchronized (pendingFrames) {
71 pendingFrames.add(frame); 73 pendingFrames.add(frame);
72 pendingFrames.notifyAll(); 74 pendingFrames.notifyAll();
73 } 75 }
74 } 76 }
75 77
76 // Wait until at least one frame have been received, before returning them. 78 // Wait until at least one frame have been received, before returning them.
77 public List<I420Frame> WaitForFrames() { 79 public List<I420Frame> WaitForFrames() throws InterruptedException {
hbos 2015/09/21 12:59:06 nit: waitForPendingFrames()
magjed_webrtc 2015/09/21 15:27:08 Done.
78 synchronized (pendingFrames) { 80 synchronized (pendingFrames) {
79 while (pendingFrames.isEmpty()) { 81 while (pendingFrames.isEmpty()) {
80 try { 82 pendingFrames.wait();
81 pendingFrames.wait();
82 } catch (InterruptedException e) {
83 // Ignore.
84 }
85 } 83 }
86 final List<I420Frame> frames = new ArrayList<I420Frame>(pendingFrames); 84 final List<I420Frame> frames = new ArrayList<I420Frame>(pendingFrames);
87 pendingFrames.clear(); 85 pendingFrames.clear();
88 return frames; 86 return frames;
89 } 87 }
90 } 88 }
91 } 89 }
92 90
93 static class FakeCapturerObserver implements 91 static class FakeCapturerObserver implements
94 VideoCapturerAndroid.CapturerObserver { 92 VideoCapturerAndroid.CapturerObserver {
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 // to a Java video renderer using the back facing video capturer. 241 // to a Java video renderer using the back facing video capturer.
244 // It tests both the Java and the C++ layer. 242 // It tests both the Java and the C++ layer.
245 public void testStartBackFacingVideoCapturer() throws Exception { 243 public void testStartBackFacingVideoCapturer() throws Exception {
246 if (!HaveTwoCameras()) { 244 if (!HaveTwoCameras()) {
247 return; 245 return;
248 } 246 }
249 startCapturerAndRender(CameraEnumerationAndroid.getNameOfBackFacingDevice()) ; 247 startCapturerAndRender(CameraEnumerationAndroid.getNameOfBackFacingDevice()) ;
250 } 248 }
251 249
252 @SmallTest 250 @SmallTest
253 // This test that the default camera can be started and but the camera can 251 // This test that the default camera can be started and that the camera can
254 // later be switched to another camera. 252 // later be switched to another camera.
255 // It tests both the Java and the C++ layer. 253 // It tests both the Java and the C++ layer.
256 public void testSwitchVideoCapturer() throws Exception { 254 public void testSwitchVideoCapturer() throws Exception {
257 PeerConnectionFactory factory = new PeerConnectionFactory(); 255 PeerConnectionFactory factory = new PeerConnectionFactory();
258 VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null); 256 VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null);
259 VideoSource source = 257 VideoSource source =
260 factory.createVideoSource(capturer, new MediaConstraints()); 258 factory.createVideoSource(capturer, new MediaConstraints());
261 VideoTrack track = factory.createVideoTrack("dummy", source); 259 VideoTrack track = factory.createVideoTrack("dummy", source);
262 260
263 if (HaveTwoCameras()) 261 // Array with one element to avoid final problem in nested classes.
264 assertTrue(capturer.switchCamera(null)); 262 final boolean[] cameraSwitchSuccessful = new boolean[1];
265 else 263 final CountDownLatch barrier = new CountDownLatch(1);
266 assertFalse(capturer.switchCamera(null)); 264 capturer.switchCamera(new VideoCapturerAndroid.CameraSwitchHandler() {
265 @Override
266 public void onCameraSwitchDone(boolean isFrontCamera) {
267 cameraSwitchSuccessful[0] = true;
268 barrier.countDown();
269 }
270 @Override
271 public void onCameraSwitchError(String errorDescription) {
272 cameraSwitchSuccessful[0] = false;
273 barrier.countDown();
274 }
275 });
276 // Wait until the camera has been switched.
277 barrier.await();
267 278
268 // Wait until the camera have been switched. 279 // Check result.
269 capturer.runCameraThreadUntilIdle(); 280 if (HaveTwoCameras()) {
270 281 assertTrue(cameraSwitchSuccessful[0]);
282 } else {
283 assertFalse(cameraSwitchSuccessful[0]);
284 }
271 // Ensure that frames are received. 285 // Ensure that frames are received.
272 RendererCallbacks callbacks = new RendererCallbacks(); 286 RendererCallbacks callbacks = new RendererCallbacks();
273 track.addRenderer(new VideoRenderer(callbacks)); 287 track.addRenderer(new VideoRenderer(callbacks));
274 assertTrue(callbacks.WaitForNextFrameToRender() > 0); 288 assertTrue(callbacks.WaitForNextFrameToRender() > 0);
275 track.dispose(); 289 track.dispose();
276 source.dispose(); 290 source.dispose();
277 factory.dispose(); 291 factory.dispose();
278 } 292 }
279 293
280 @SmallTest 294 @SmallTest
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
360 observer.WaitForNextCapturedFrame(); 374 observer.WaitForNextCapturedFrame();
361 capturer.stopCapture(); 375 capturer.stopCapture();
362 376
363 listOftimestamps = observer.getCopyAndResetListOftimeStamps(); 377 listOftimestamps = observer.getCopyAndResetListOftimeStamps();
364 assertTrue(listOftimestamps.size() >= 2); 378 assertTrue(listOftimestamps.size() >= 2);
365 for (Long timeStamp : listOftimestamps) { 379 for (Long timeStamp : listOftimestamps) {
366 capturer.returnBuffer(timeStamp); 380 capturer.returnBuffer(timeStamp);
367 } 381 }
368 } 382 }
369 383
370 @SmallTest 384 @MediumTest
371 // This test that we can capture frames, stop capturing, keep the frames for r endering, and then 385 // This test that we can capture frames, keep the frames in a local renderer, stop capturing,
372 // return the frames. It tests both the Java and the C++ layer. 386 // and then return the frames. The difference between the test testReturnBuffe rLate() is that we
373 public void testCaptureAndAsyncRender() { 387 // also test the JNI and C++ AndroidVideoCapturer parts.
388 public void testReturnBufferLateEndToEnd() throws InterruptedException {
374 final VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null); 389 final VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null);
375 // Helper class that sets everything up, captures at least one frame, and th en shuts 390 final PeerConnectionFactory factory = new PeerConnectionFactory();
376 // everything down. 391 final VideoSource source = factory.createVideoSource(capturer, new MediaCons traints());
377 class CaptureFramesRunnable implements Runnable { 392 final VideoTrack track = factory.createVideoTrack("dummy", source);
378 public List<I420Frame> frames; 393 final FakeAsyncRenderer renderer = new FakeAsyncRenderer();
394 track.addRenderer(new VideoRenderer(renderer));
395 // Wait for at least one frame.
396 final List<I420Frame> pendingFrames = renderer.WaitForFrames();
397 // Check that we have frames that have not been returned.
398 assertFalse(pendingFrames.isEmpty());
379 399
400 capturer.stopCapture();
hbos 2015/09/21 12:59:06 waitForFrames() should happen after stopCapture()
magjed_webrtc 2015/09/21 15:27:08 Thanks. Done.
401
402 // Return the frame(s), on a different thread out of spite.
403 final Thread returnThread = new Thread(new Runnable() {
380 @Override 404 @Override
381 public void run() { 405 public void run() {
382 PeerConnectionFactory factory = new PeerConnectionFactory(); 406 for (I420Frame frame : pendingFrames) {
383 VideoSource source = factory.createVideoSource(capturer, new MediaConstr aints()); 407 VideoRenderer.renderFrameDone(frame);
384 VideoTrack track = factory.createVideoTrack("dummy", source); 408 }
385 AsyncRenderer renderer = new AsyncRenderer(); 409 }
386 track.addRenderer(new VideoRenderer(renderer)); 410 });
411 returnThread.start();
412 returnThread.join();
387 413
388 // Wait until we get at least one frame. 414 // Check that frames have successfully returned.
389 frames = renderer.WaitForFrames(); 415 assertEquals(capturer.pendingFramesTimeStamps(), "[]");
hbos 2015/09/21 12:59:06 nit: Checking pending frames count == 0 would be n
magjed_webrtc 2015/09/21 15:27:08 Done.
390 416
391 // Stop everything. 417 // Stop everything.
392 track.dispose(); 418 track.dispose();
393 source.dispose(); 419 source.dispose();
394 factory.dispose(); 420 factory.dispose();
hbos 2015/09/21 12:59:06 It is not clear that the C++ resources should be f
magjed_webrtc 2015/09/21 15:27:08 The capturer is owned by |source| I think, so it d
hbos 2015/09/22 09:12:46 How about a simple has been released flag as discu
magjed_webrtc 2015/09/22 15:04:21 Done.
395 }
396 }
397
398 // Capture frames on a separate thread.
399 CaptureFramesRunnable captureFramesRunnable = new CaptureFramesRunnable();
400 Thread captureThread = new Thread(captureFramesRunnable);
401 captureThread.start();
402
403 // Wait until frames are captured, and then kill the thread.
404 try {
405 captureThread.join();
406 } catch (InterruptedException e) {
407 fail("Capture thread was interrupted");
408 }
409 captureThread = null;
410
411 // Assert that we have frames that have not been returned.
412 assertTrue(!captureFramesRunnable.frames.isEmpty());
413 // Return the frame(s).
414 for (I420Frame frame : captureFramesRunnable.frames) {
415 VideoRenderer.renderFrameDone(frame);
416 }
417 assertEquals(capturer.pendingFramesTimeStamps(), "[]");
418 } 421 }
419 } 422 }
OLDNEW
« no previous file with comments | « no previous file | talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698