Index: talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java |
diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java |
deleted file mode 100644 |
index 0b42e33785659a360db717a022d377f6671241d4..0000000000000000000000000000000000000000 |
--- a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java |
+++ /dev/null |
@@ -1,615 +0,0 @@ |
-/* |
- * libjingle |
- * Copyright 2015 Google Inc. |
- * |
- * Redistribution and use in source and binary forms, with or without |
- * modification, are permitted provided that the following conditions are met: |
- * |
- * 1. Redistributions of source code must retain the above copyright notice, |
- * this list of conditions and the following disclaimer. |
- * 2. Redistributions in binary form must reproduce the above copyright notice, |
- * this list of conditions and the following disclaimer in the documentation |
- * and/or other materials provided with the distribution. |
- * 3. The name of the author may not be used to endorse or promote products |
- * derived from this software without specific prior written permission. |
- * |
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- */ |
-package org.webrtc; |
- |
-import android.content.Context; |
-import android.hardware.Camera; |
- |
-import org.webrtc.VideoCapturerAndroidTestFixtures; |
-import org.webrtc.CameraEnumerationAndroid.CaptureFormat; |
-import org.webrtc.VideoRenderer.I420Frame; |
- |
-import java.util.ArrayList; |
-import java.util.List; |
-import java.util.concurrent.CountDownLatch; |
- |
-import static junit.framework.Assert.*; |
- |
-public class VideoCapturerAndroidTestFixtures { |
- static class RendererCallbacks implements VideoRenderer.Callbacks { |
- private int framesRendered = 0; |
- private Object frameLock = 0; |
- private int width = 0; |
- private int height = 0; |
- |
- @Override |
- public void renderFrame(I420Frame frame) { |
- synchronized (frameLock) { |
- ++framesRendered; |
- width = frame.rotatedWidth(); |
- height = frame.rotatedHeight(); |
- frameLock.notify(); |
- } |
- VideoRenderer.renderFrameDone(frame); |
- } |
- |
- public int frameWidth() { |
- synchronized (frameLock) { |
- return width; |
- } |
- } |
- |
- public int frameHeight() { |
- synchronized (frameLock) { |
- return height; |
- } |
- } |
- |
- public int WaitForNextFrameToRender() throws InterruptedException { |
- synchronized (frameLock) { |
- frameLock.wait(); |
- return framesRendered; |
- } |
- } |
- } |
- |
- static class FakeAsyncRenderer implements VideoRenderer.Callbacks { |
- private final List<I420Frame> pendingFrames = new ArrayList<I420Frame>(); |
- |
- @Override |
- public void renderFrame(I420Frame frame) { |
- synchronized (pendingFrames) { |
- pendingFrames.add(frame); |
- pendingFrames.notifyAll(); |
- } |
- } |
- |
- // Wait until at least one frame have been received, before returning them. |
- public List<I420Frame> waitForPendingFrames() throws InterruptedException { |
- synchronized (pendingFrames) { |
- while (pendingFrames.isEmpty()) { |
- pendingFrames.wait(); |
- } |
- return new ArrayList<I420Frame>(pendingFrames); |
- } |
- } |
- } |
- |
- static class FakeCapturerObserver implements |
- VideoCapturerAndroid.CapturerObserver { |
- private int framesCaptured = 0; |
- private int frameSize = 0; |
- private int frameWidth = 0; |
- private int frameHeight = 0; |
- private Object frameLock = 0; |
- private Object capturerStartLock = 0; |
- private boolean captureStartResult = false; |
- private List<Long> timestamps = new ArrayList<Long>(); |
- |
- @Override |
- public void onCapturerStarted(boolean success) { |
- synchronized (capturerStartLock) { |
- captureStartResult = success; |
- capturerStartLock.notify(); |
- } |
- } |
- |
- @Override |
- public void onByteBufferFrameCaptured(byte[] frame, int width, int height, int rotation, |
- long timeStamp) { |
- synchronized (frameLock) { |
- ++framesCaptured; |
- frameSize = frame.length; |
- frameWidth = width; |
- frameHeight = height; |
- timestamps.add(timeStamp); |
- frameLock.notify(); |
- } |
- } |
- @Override |
- public void onTextureFrameCaptured( |
- int width, int height, int oesTextureId, float[] transformMatrix, int rotation, |
- long timeStamp) { |
- synchronized (frameLock) { |
- ++framesCaptured; |
- frameWidth = width; |
- frameHeight = height; |
- frameSize = 0; |
- timestamps.add(timeStamp); |
- frameLock.notify(); |
- } |
- } |
- |
- @Override |
- public void onOutputFormatRequest(int width, int height, int fps) {} |
- |
- public boolean WaitForCapturerToStart() throws InterruptedException { |
- synchronized (capturerStartLock) { |
- capturerStartLock.wait(); |
- return captureStartResult; |
- } |
- } |
- |
- public int WaitForNextCapturedFrame() throws InterruptedException { |
- synchronized (frameLock) { |
- frameLock.wait(); |
- return framesCaptured; |
- } |
- } |
- |
- int frameSize() { |
- synchronized (frameLock) { |
- return frameSize; |
- } |
- } |
- |
- int frameWidth() { |
- synchronized (frameLock) { |
- return frameWidth; |
- } |
- } |
- |
- int frameHeight() { |
- synchronized (frameLock) { |
- return frameHeight; |
- } |
- } |
- |
- List<Long> getCopyAndResetListOftimeStamps() { |
- synchronized (frameLock) { |
- ArrayList<Long> list = new ArrayList<Long>(timestamps); |
- timestamps.clear(); |
- return list; |
- } |
- } |
- } |
- |
- static class CameraEvents implements |
- VideoCapturerAndroid.CameraEventsHandler { |
- public boolean onCameraOpeningCalled; |
- public boolean onFirstFrameAvailableCalled; |
- public final Object onCameraFreezedLock = new Object(); |
- private String onCameraFreezedDescription; |
- |
- @Override |
- public void onCameraError(String errorDescription) { |
- } |
- |
- @Override |
- public void onCameraFreezed(String errorDescription) { |
- synchronized (onCameraFreezedLock) { |
- onCameraFreezedDescription = errorDescription; |
- onCameraFreezedLock.notifyAll(); |
- } |
- } |
- |
- @Override |
- public void onCameraOpening(int cameraId) { |
- onCameraOpeningCalled = true; |
- } |
- |
- @Override |
- public void onFirstFrameAvailable() { |
- onFirstFrameAvailableCalled = true; |
- } |
- |
- @Override |
- public void onCameraClosed() { } |
- |
- public String WaitForCameraFreezed() throws InterruptedException { |
- synchronized (onCameraFreezedLock) { |
- onCameraFreezedLock.wait(); |
- return onCameraFreezedDescription; |
- } |
- } |
- } |
- |
- static public CameraEvents createCameraEvents() { |
- return new CameraEvents(); |
- } |
- |
- // Return true if the device under test have at least two cameras. |
- @SuppressWarnings("deprecation") |
- static public boolean HaveTwoCameras() { |
- return (Camera.getNumberOfCameras() >= 2); |
- } |
- |
- static public void release(VideoCapturerAndroid capturer) { |
- assertNotNull(capturer); |
- capturer.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static public void startCapturerAndRender(VideoCapturerAndroid capturer) |
- throws InterruptedException { |
- PeerConnectionFactory factory = new PeerConnectionFactory(); |
- VideoSource source = |
- factory.createVideoSource(capturer, new MediaConstraints()); |
- VideoTrack track = factory.createVideoTrack("dummy", source); |
- RendererCallbacks callbacks = new RendererCallbacks(); |
- track.addRenderer(new VideoRenderer(callbacks)); |
- assertTrue(callbacks.WaitForNextFrameToRender() > 0); |
- track.dispose(); |
- source.dispose(); |
- factory.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static public void switchCamera(VideoCapturerAndroid capturer) throws InterruptedException { |
- PeerConnectionFactory factory = new PeerConnectionFactory(); |
- VideoSource source = |
- factory.createVideoSource(capturer, new MediaConstraints()); |
- VideoTrack track = factory.createVideoTrack("dummy", source); |
- |
- // Array with one element to avoid final problem in nested classes. |
- final boolean[] cameraSwitchSuccessful = new boolean[1]; |
- final CountDownLatch barrier = new CountDownLatch(1); |
- capturer.switchCamera(new VideoCapturerAndroid.CameraSwitchHandler() { |
- @Override |
- public void onCameraSwitchDone(boolean isFrontCamera) { |
- cameraSwitchSuccessful[0] = true; |
- barrier.countDown(); |
- } |
- @Override |
- public void onCameraSwitchError(String errorDescription) { |
- cameraSwitchSuccessful[0] = false; |
- barrier.countDown(); |
- } |
- }); |
- // Wait until the camera has been switched. |
- barrier.await(); |
- |
- // Check result. |
- if (HaveTwoCameras()) { |
- assertTrue(cameraSwitchSuccessful[0]); |
- } else { |
- assertFalse(cameraSwitchSuccessful[0]); |
- } |
- // Ensure that frames are received. |
- RendererCallbacks callbacks = new RendererCallbacks(); |
- track.addRenderer(new VideoRenderer(callbacks)); |
- assertTrue(callbacks.WaitForNextFrameToRender() > 0); |
- track.dispose(); |
- source.dispose(); |
- factory.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static public void cameraEventsInvoked(VideoCapturerAndroid capturer, CameraEvents events, |
- Context appContext) throws InterruptedException { |
- final List<CaptureFormat> 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 and first frame is received and then stop it. |
- assertTrue(observer.WaitForCapturerToStart()); |
- observer.WaitForNextCapturedFrame(); |
- capturer.stopCapture(); |
- if (capturer.isCapturingToTexture()) { |
- capturer.surfaceHelper.returnTextureFrame(); |
- } |
- capturer.dispose(); |
- |
- assertTrue(capturer.isReleased()); |
- assertTrue(events.onCameraOpeningCalled); |
- assertTrue(events.onFirstFrameAvailableCalled); |
- } |
- |
- static public void cameraCallsAfterStop( |
- VideoCapturerAndroid capturer, Context appContext) throws InterruptedException { |
- final List<CaptureFormat> 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 and then stop it. |
- assertTrue(observer.WaitForCapturerToStart()); |
- capturer.stopCapture(); |
- if (capturer.isCapturingToTexture()) { |
- capturer.surfaceHelper.returnTextureFrame(); |
- } |
- |
- // We can't change |capturer| at this point, but we should not crash. |
- capturer.switchCamera(null); |
- capturer.onOutputFormatRequest(640, 480, 15); |
- capturer.changeCaptureFormat(640, 480, 15); |
- |
- capturer.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static public void stopRestartVideoSource(VideoCapturerAndroid capturer) |
- throws InterruptedException { |
- PeerConnectionFactory factory = new PeerConnectionFactory(); |
- VideoSource source = |
- factory.createVideoSource(capturer, new MediaConstraints()); |
- VideoTrack track = factory.createVideoTrack("dummy", source); |
- RendererCallbacks callbacks = new RendererCallbacks(); |
- track.addRenderer(new VideoRenderer(callbacks)); |
- assertTrue(callbacks.WaitForNextFrameToRender() > 0); |
- assertEquals(MediaSource.State.LIVE, source.state()); |
- |
- source.stop(); |
- assertEquals(MediaSource.State.ENDED, source.state()); |
- |
- source.restart(); |
- assertTrue(callbacks.WaitForNextFrameToRender() > 0); |
- assertEquals(MediaSource.State.LIVE, source.state()); |
- track.dispose(); |
- source.dispose(); |
- factory.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static public void startStopWithDifferentResolutions(VideoCapturerAndroid capturer, |
- Context appContext) throws InterruptedException { |
- FakeCapturerObserver observer = new FakeCapturerObserver(); |
- List<CaptureFormat> formats = capturer.getSupportedFormats(); |
- |
- for(int i = 0; i < 3 ; ++i) { |
- CameraEnumerationAndroid.CaptureFormat format = formats.get(i); |
- capturer.startCapture(format.width, format.height, format.maxFramerate, |
- appContext, observer); |
- assertTrue(observer.WaitForCapturerToStart()); |
- observer.WaitForNextCapturedFrame(); |
- |
- // Check the frame size. The actual width and height depend on how the capturer is mounted. |
- final boolean identicalResolution = (observer.frameWidth() == format.width |
- && observer.frameHeight() == format.height); |
- final boolean flippedResolution = (observer.frameWidth() == format.height |
- && observer.frameHeight() == format.width); |
- if (!identicalResolution && !flippedResolution) { |
- fail("Wrong resolution, got: " + observer.frameWidth() + "x" + observer.frameHeight() |
- + " expected: " + format.width + "x" + format.height + " or " + format.height + "x" |
- + format.width); |
- } |
- |
- if (capturer.isCapturingToTexture()) { |
- assertEquals(0, observer.frameSize()); |
- } else { |
- assertTrue(format.frameSize() <= observer.frameSize()); |
- } |
- capturer.stopCapture(); |
- if (capturer.isCapturingToTexture()) { |
- capturer.surfaceHelper.returnTextureFrame(); |
- } |
- } |
- capturer.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static void waitUntilIdle(VideoCapturerAndroid capturer) throws InterruptedException { |
- final CountDownLatch barrier = new CountDownLatch(1); |
- capturer.getCameraThreadHandler().post(new Runnable() { |
- @Override public void run() { |
- barrier.countDown(); |
- } |
- }); |
- barrier.await(); |
- } |
- |
- static public void startWhileCameraIsAlreadyOpen( |
- VideoCapturerAndroid capturer, Context appContext) throws InterruptedException { |
- Camera camera = Camera.open(capturer.getCurrentCameraId()); |
- |
- final List<CaptureFormat> 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); |
- |
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) { |
- // The first opened camera client will be evicted. |
- assertTrue(observer.WaitForCapturerToStart()); |
- capturer.stopCapture(); |
- } else { |
- assertFalse(observer.WaitForCapturerToStart()); |
- } |
- |
- capturer.dispose(); |
- camera.release(); |
- } |
- |
- static public void startWhileCameraIsAlreadyOpenAndCloseCamera( |
- VideoCapturerAndroid capturer, Context appContext) throws InterruptedException { |
- Camera camera = Camera.open(capturer.getCurrentCameraId()); |
- |
- final List<CaptureFormat> 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); |
- waitUntilIdle(capturer); |
- |
- camera.release(); |
- |
- // Make sure camera is started and first frame is received and then stop it. |
- assertTrue(observer.WaitForCapturerToStart()); |
- observer.WaitForNextCapturedFrame(); |
- capturer.stopCapture(); |
- if (capturer.isCapturingToTexture()) { |
- capturer.surfaceHelper.returnTextureFrame(); |
- } |
- capturer.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static public void startWhileCameraIsAlreadyOpenAndStop( |
- VideoCapturerAndroid capturer, Context appContext) throws InterruptedException { |
- Camera camera = Camera.open(capturer.getCurrentCameraId()); |
- final List<CaptureFormat> 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); |
- capturer.stopCapture(); |
- capturer.dispose(); |
- assertTrue(capturer.isReleased()); |
- camera.release(); |
- } |
- |
- static public void returnBufferLate(VideoCapturerAndroid capturer, |
- Context appContext) throws InterruptedException { |
- FakeCapturerObserver observer = new FakeCapturerObserver(); |
- |
- List<CaptureFormat> formats = capturer.getSupportedFormats(); |
- CameraEnumerationAndroid.CaptureFormat format = formats.get(0); |
- capturer.startCapture(format.width, format.height, format.maxFramerate, |
- appContext, observer); |
- assertTrue(observer.WaitForCapturerToStart()); |
- |
- observer.WaitForNextCapturedFrame(); |
- capturer.stopCapture(); |
- List<Long> listOftimestamps = observer.getCopyAndResetListOftimeStamps(); |
- assertTrue(listOftimestamps.size() >= 1); |
- |
- format = formats.get(1); |
- capturer.startCapture(format.width, format.height, format.maxFramerate, |
- appContext, observer); |
- observer.WaitForCapturerToStart(); |
- if (capturer.isCapturingToTexture()) { |
- capturer.surfaceHelper.returnTextureFrame(); |
- } |
- |
- observer.WaitForNextCapturedFrame(); |
- capturer.stopCapture(); |
- |
- listOftimestamps = observer.getCopyAndResetListOftimeStamps(); |
- assertTrue(listOftimestamps.size() >= 1); |
- if (capturer.isCapturingToTexture()) { |
- capturer.surfaceHelper.returnTextureFrame(); |
- } |
- |
- capturer.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static public void returnBufferLateEndToEnd(VideoCapturerAndroid capturer) |
- throws InterruptedException { |
- final PeerConnectionFactory factory = new PeerConnectionFactory(); |
- final VideoSource source = factory.createVideoSource(capturer, new MediaConstraints()); |
- final VideoTrack track = factory.createVideoTrack("dummy", source); |
- final FakeAsyncRenderer renderer = new FakeAsyncRenderer(); |
- |
- track.addRenderer(new VideoRenderer(renderer)); |
- // Wait for at least one frame that has not been returned. |
- assertFalse(renderer.waitForPendingFrames().isEmpty()); |
- |
- capturer.stopCapture(); |
- |
- // Dispose everything. |
- track.dispose(); |
- source.dispose(); |
- factory.dispose(); |
- assertTrue(capturer.isReleased()); |
- |
- // Return the frame(s), on a different thread out of spite. |
- final List<I420Frame> pendingFrames = renderer.waitForPendingFrames(); |
- final Thread returnThread = new Thread(new Runnable() { |
- @Override |
- public void run() { |
- for (I420Frame frame : pendingFrames) { |
- VideoRenderer.renderFrameDone(frame); |
- } |
- } |
- }); |
- returnThread.start(); |
- returnThread.join(); |
- } |
- |
- static public void cameraFreezedEventOnBufferStarvationUsingTextures( |
- VideoCapturerAndroid capturer, |
- CameraEvents events, Context appContext) throws InterruptedException { |
- assertTrue("Not capturing to textures.", capturer.isCapturingToTexture()); |
- |
- final List<CaptureFormat> 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 return the buffer, we should get a starvation message if we are |
- // capturing to a texture. |
- assertEquals("Camera failure. Client must return video buffers.", |
- events.WaitForCameraFreezed()); |
- |
- capturer.stopCapture(); |
- if (capturer.isCapturingToTexture()) { |
- capturer.surfaceHelper.returnTextureFrame(); |
- } |
- |
- capturer.dispose(); |
- assertTrue(capturer.isReleased()); |
- } |
- |
- static public void scaleCameraOutput(VideoCapturerAndroid capturer) throws InterruptedException { |
- PeerConnectionFactory factory = new PeerConnectionFactory(); |
- VideoSource source = |
- factory.createVideoSource(capturer, new MediaConstraints()); |
- VideoTrack track = factory.createVideoTrack("dummy", source); |
- RendererCallbacks renderer = new RendererCallbacks(); |
- track.addRenderer(new VideoRenderer(renderer)); |
- assertTrue(renderer.WaitForNextFrameToRender() > 0); |
- |
- final int startWidth = renderer.frameWidth(); |
- final int startHeight = renderer.frameHeight(); |
- final int frameRate = 30; |
- final int scaledWidth = startWidth / 2; |
- final int scaledHeight = startHeight / 2; |
- |
- // Request the captured frames to be scaled. |
- capturer.onOutputFormatRequest(scaledWidth, scaledHeight, frameRate); |
- |
- boolean gotExpectedResolution = false; |
- int numberOfInspectedFrames = 0; |
- |
- do { |
- renderer.WaitForNextFrameToRender(); |
- ++numberOfInspectedFrames; |
- |
- gotExpectedResolution = (renderer.frameWidth() == scaledWidth |
- && renderer.frameHeight() == scaledHeight); |
- } while (!gotExpectedResolution && numberOfInspectedFrames < 30); |
- |
- source.stop(); |
- track.dispose(); |
- source.dispose(); |
- factory.dispose(); |
- assertTrue(capturer.isReleased()); |
- |
- assertTrue(gotExpectedResolution); |
- } |
- |
-} |