OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |