diff --git a/webrtc/api/androidtests/src/org/webrtc/Camera1CapturerUsingByteBufferTest.java b/webrtc/api/androidtests/src/org/webrtc/Camera1CapturerUsingByteBufferTest.java new file mode 100644 index 0000000000..c6d23cd26a --- /dev/null +++ b/webrtc/api/androidtests/src/org/webrtc/Camera1CapturerUsingByteBufferTest.java @@ -0,0 +1,194 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +import org.webrtc.CameraEnumerationAndroid.CaptureFormat; + +import android.content.Context; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.LargeTest; + +public class Camera1CapturerUsingByteBufferTest extends InstrumentationTestCase { + static final String TAG = "Camera1CapturerUsingByteBufferTest"; + + private class TestObjectFactory + implements CameraVideoCapturerTestFixtures.TestObjectFactory { + @Override + public CameraVideoCapturer createCapturer( + String name, + CameraVideoCapturer.CameraEventsHandler eventsHandler) { + return new VideoCapturerAndroid(name, eventsHandler, isCapturingToTexture()); + } + + @Override + public String getNameOfFrontFacingDevice() { + return CameraEnumerationAndroid.getNameOfFrontFacingDevice(); + } + + @Override + public String getNameOfBackFacingDevice() { + return CameraEnumerationAndroid.getNameOfBackFacingDevice(); + } + + // Return true if the device under test have at least two cameras. + @SuppressWarnings("deprecation") + @Override + public boolean haveTwoCameras() { + return (android.hardware.Camera.getNumberOfCameras() >= 2); + } + + @Override + public boolean isCapturingToTexture() { + return false; + } + + @Override + public Context getAppContext() { + return getInstrumentation().getTargetContext(); + } + + @SuppressWarnings("deprecation") + @Override + public Object rawOpenCamera(String cameraName) { + return android.hardware.Camera.open(CameraEnumerationAndroid.getCameraIndex(cameraName)); + } + + @SuppressWarnings("deprecation") + @Override + public void rawCloseCamera(Object camera) { + ((android.hardware.Camera) camera).release(); + } + } + + private CameraVideoCapturerTestFixtures fixtures; + + @Override + protected void setUp() { + fixtures = new CameraVideoCapturerTestFixtures(new TestObjectFactory()); + } + + @Override + protected void tearDown() { + fixtures.dispose(); + } + + @SmallTest + public void testCreateAndDispose() { + fixtures.createCapturerAndDispose(); + } + + @SmallTest + public void testCreateNonExistingCamera() { + fixtures.createNonExistingCamera(); + } + + // This test that the camera can be started and that the frames are forwarded + // to a Java video renderer using a "default" capturer. + // It tests both the Java and the C++ layer. + @MediumTest + public void testCreateCapturerAndRender() throws InterruptedException { + fixtures.createCapturerAndRender(); + } + + // This test that the camera can be started and that the frames are forwarded + // to a Java video renderer using the front facing video capturer. + // It tests both the Java and the C++ layer. + @MediumTest + public void testStartFrontFacingVideoCapturer() throws InterruptedException { + fixtures.createFrontFacingCapturerAndRender(); + } + + // This test that the camera can be started and that the frames are forwarded + // to a Java video renderer using the back facing video capturer. + // It tests both the Java and the C++ layer. + @MediumTest + public void testStartBackFacingVideoCapturer() throws InterruptedException { + fixtures.createBackFacingCapturerAndRender(); + } + + // This test that the default camera can be started and that the camera can + // later be switched to another camera. + // It tests both the Java and the C++ layer. + @MediumTest + public void testSwitchVideoCapturer() throws InterruptedException { + fixtures.switchCamera(); + } + + @MediumTest + public void testCameraEvents() throws InterruptedException { + fixtures.cameraEventsInvoked(); + } + + // Test what happens when attempting to call e.g. switchCamera() after camera has been stopped. + @MediumTest + public void testCameraCallsAfterStop() throws InterruptedException { + fixtures.cameraCallsAfterStop(); + } + + // This test that the VideoSource that the CameraVideoCapturer is connected to can + // be stopped and restarted. It tests both the Java and the C++ layer. + @LargeTest + public void testStopRestartVideoSource() throws InterruptedException { + fixtures.stopRestartVideoSource(); + } + + // This test that the camera can be started at different resolutions. + // It does not test or use the C++ layer. + @LargeTest + public void testStartStopWithDifferentResolutions() throws InterruptedException { + fixtures.startStopWithDifferentResolutions(); + } + + // This test what happens if buffers are returned after the capturer have + // been stopped and restarted. It does not test or use the C++ layer. + @LargeTest + public void testReturnBufferLate() throws InterruptedException { + fixtures.returnBufferLateEndToEnd(); + } + + // This test that we can capture frames, keep the frames in a local renderer, stop capturing, + // and then return the frames. The difference between the test testReturnBufferLate() is that we + // also test the JNI and C++ AndroidVideoCapturer parts. + @MediumTest + public void testReturnBufferLateEndToEnd() throws InterruptedException { + fixtures.returnBufferLateEndToEnd(); + } + + // This test that frames forwarded to a renderer is scaled if onOutputFormatRequest is + // called. This test both Java and C++ parts of of the stack. + @MediumTest + public void testScaleCameraOutput() throws InterruptedException { + fixtures.scaleCameraOutput(); + } + + // This test that an error is reported if the camera is already opened + // when CameraVideoCapturer is started. + @LargeTest + public void testStartWhileCameraIsAlreadyOpen() throws InterruptedException { + fixtures.startWhileCameraIsAlreadyOpen(); + } + + // This test that CameraVideoCapturer can be started, even if the camera is already opened + // if the camera is closed while CameraVideoCapturer is re-trying to start. + @LargeTest + public void testStartWhileCameraIsAlreadyOpenAndCloseCamera() throws InterruptedException { + fixtures.startWhileCameraIsAlreadyOpenAndCloseCamera(); + } + + // This test that CameraVideoCapturer.stop can be called while CameraVideoCapturer is + // re-trying to start. + @MediumTest + public void testStartWhileCameraIsAlreadyOpenAndStop() throws InterruptedException { + fixtures.startWhileCameraIsAlreadyOpenAndStop(); + } +} diff --git a/webrtc/api/androidtests/src/org/webrtc/Camera1CapturerUsingTextureTest.java b/webrtc/api/androidtests/src/org/webrtc/Camera1CapturerUsingTextureTest.java new file mode 100644 index 0000000000..27b54dbe3e --- /dev/null +++ b/webrtc/api/androidtests/src/org/webrtc/Camera1CapturerUsingTextureTest.java @@ -0,0 +1,201 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +import org.webrtc.CameraEnumerationAndroid.CaptureFormat; + +import android.content.Context; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.LargeTest; + +public class Camera1CapturerUsingTextureTest extends InstrumentationTestCase { + static final String TAG = "Camera1CapturerUsingTextureTest"; + + private class TestObjectFactory + implements CameraVideoCapturerTestFixtures.TestObjectFactory { + @Override + public CameraVideoCapturer createCapturer( + String name, + CameraVideoCapturer.CameraEventsHandler eventsHandler) { + return new VideoCapturerAndroid(name, eventsHandler, isCapturingToTexture()); + } + + @Override + public String getNameOfFrontFacingDevice() { + return CameraEnumerationAndroid.getNameOfFrontFacingDevice(); + } + + @Override + public String getNameOfBackFacingDevice() { + return CameraEnumerationAndroid.getNameOfBackFacingDevice(); + } + + // Return true if the device under test have at least two cameras. + @SuppressWarnings("deprecation") + @Override + public boolean haveTwoCameras() { + return (android.hardware.Camera.getNumberOfCameras() >= 2); + } + + @Override + public boolean isCapturingToTexture() { + return true; + } + + @Override + public Context getAppContext() { + return getInstrumentation().getTargetContext(); + } + + @SuppressWarnings("deprecation") + @Override + public Object rawOpenCamera(String cameraName) { + return android.hardware.Camera.open(CameraEnumerationAndroid.getCameraIndex(cameraName)); + } + + @SuppressWarnings("deprecation") + @Override + public void rawCloseCamera(Object camera) { + ((android.hardware.Camera) camera).release(); + } + } + + private CameraVideoCapturerTestFixtures fixtures; + + @Override + protected void setUp() { + fixtures = new CameraVideoCapturerTestFixtures(new TestObjectFactory()); + } + + @Override + protected void tearDown() { + fixtures.dispose(); + } + + @SmallTest + public void testCreateAndDispose() { + fixtures.createCapturerAndDispose(); + } + + @SmallTest + public void testCreateNonExistingCamera() { + fixtures.createNonExistingCamera(); + } + + // This test that the camera can be started and that the frames are forwarded + // to a Java video renderer using a "default" capturer. + // It tests both the Java and the C++ layer. + @MediumTest + public void testCreateCapturerAndRender() throws InterruptedException { + fixtures.createCapturerAndRender(); + } + + // This test that the camera can be started and that the frames are forwarded + // to a Java video renderer using the front facing video capturer. + // It tests both the Java and the C++ layer. + @MediumTest + public void testStartFrontFacingVideoCapturer() throws InterruptedException { + fixtures.createFrontFacingCapturerAndRender(); + } + + // This test that the camera can be started and that the frames are forwarded + // to a Java video renderer using the back facing video capturer. + // It tests both the Java and the C++ layer. + @MediumTest + public void testStartBackFacingVideoCapturer() throws InterruptedException { + fixtures.createBackFacingCapturerAndRender(); + } + + // This test that the default camera can be started and that the camera can + // later be switched to another camera. + // It tests both the Java and the C++ layer. + @MediumTest + public void testSwitchVideoCapturer() throws InterruptedException { + fixtures.switchCamera(); + } + + @MediumTest + public void testCameraEvents() throws InterruptedException { + fixtures.cameraEventsInvoked(); + } + + // Test what happens when attempting to call e.g. switchCamera() after camera has been stopped. + @MediumTest + public void testCameraCallsAfterStop() throws InterruptedException { + fixtures.cameraCallsAfterStop(); + } + + // This test that the VideoSource that the CameraVideoCapturer is connected to can + // be stopped and restarted. It tests both the Java and the C++ layer. + @LargeTest + public void testStopRestartVideoSource() throws InterruptedException { + fixtures.stopRestartVideoSource(); + } + + // This test that the camera can be started at different resolutions. + // It does not test or use the C++ layer. + @LargeTest + public void testStartStopWithDifferentResolutions() throws InterruptedException { + fixtures.startStopWithDifferentResolutions(); + } + + // This test what happens if buffers are returned after the capturer have + // been stopped and restarted. It does not test or use the C++ layer. + @LargeTest + public void testReturnBufferLate() throws InterruptedException { + fixtures.returnBufferLateEndToEnd(); + } + + // This test that we can capture frames, keep the frames in a local renderer, stop capturing, + // and then return the frames. The difference between the test testReturnBufferLate() is that we + // also test the JNI and C++ AndroidVideoCapturer parts. + @MediumTest + public void testReturnBufferLateEndToEnd() throws InterruptedException { + fixtures.returnBufferLateEndToEnd(); + } + + // This test that CameraEventsHandler.onError is triggered if video buffers are not returned to + // the capturer. + @LargeTest + public void testCameraFreezedEventOnBufferStarvation() throws InterruptedException { + fixtures.cameraFreezedEventOnBufferStarvation(); + } + + // This test that frames forwarded to a renderer is scaled if onOutputFormatRequest is + // called. This test both Java and C++ parts of of the stack. + @MediumTest + public void testScaleCameraOutput() throws InterruptedException { + fixtures.scaleCameraOutput(); + } + + // This test that an error is reported if the camera is already opened + // when CameraVideoCapturer is started. + @LargeTest + public void testStartWhileCameraIsAlreadyOpen() throws InterruptedException { + fixtures.startWhileCameraIsAlreadyOpen(); + } + + // This test that CameraVideoCapturer can be started, even if the camera is already opened + // if the camera is closed while CameraVideoCapturer is re-trying to start. + @LargeTest + public void testStartWhileCameraIsAlreadyOpenAndCloseCamera() throws InterruptedException { + fixtures.startWhileCameraIsAlreadyOpenAndCloseCamera(); + } + + // This test that CameraVideoCapturer.stop can be called while CameraVideoCapturer is + // re-trying to start. + @MediumTest + public void testStartWhileCameraIsAlreadyOpenAndStop() throws InterruptedException { + fixtures.startWhileCameraIsAlreadyOpenAndStop(); + } +} diff --git a/webrtc/api/androidtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java b/webrtc/api/androidtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java new file mode 100644 index 0000000000..30119e5a1f --- /dev/null +++ b/webrtc/api/androidtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java @@ -0,0 +1,667 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +package org.webrtc; + +import static junit.framework.Assert.*; + +import org.webrtc.CameraEnumerationAndroid.CaptureFormat; +import org.webrtc.VideoRenderer.I420Frame; + +import android.content.Context; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +class CameraVideoCapturerTestFixtures { + static final String TAG = "CameraVideoCapturerTestFixtures"; + + static private 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 { + Logging.d(TAG, "Waiting for the next frame to render"); + synchronized (frameLock) { + frameLock.wait(); + return framesRendered; + } + } + } + + static private class FakeAsyncRenderer implements VideoRenderer.Callbacks { + private final List pendingFrames = new ArrayList(); + + @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 waitForPendingFrames() throws InterruptedException { + Logging.d(TAG, "Waiting for pending frames"); + synchronized (pendingFrames) { + while (pendingFrames.isEmpty()) { + pendingFrames.wait(); + } + return new ArrayList(pendingFrames); + } + } + } + + static private class FakeCapturerObserver implements CameraVideoCapturer.CapturerObserver { + private int framesCaptured = 0; + private int frameSize = 0; + private int frameWidth = 0; + private int frameHeight = 0; + final private Object frameLock = new Object(); + final private Object capturerStartLock = new Object(); + private boolean capturerStartResult = false; + final private List timestamps = new ArrayList(); + + @Override + public void onCapturerStarted(boolean success) { + Logging.d(TAG, "onCapturerStarted: " + success); + + synchronized (capturerStartLock) { + capturerStartResult = success; + capturerStartLock.notifyAll(); + } + } + + @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 { + Logging.d(TAG, "Waiting for the capturer to start"); + synchronized (capturerStartLock) { + capturerStartLock.wait(); + return capturerStartResult; + } + } + + public int waitForNextCapturedFrame() throws InterruptedException { + Logging.d(TAG, "Waiting for the next captured frame"); + 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 getCopyAndResetListOftimeStamps() { + synchronized (frameLock) { + ArrayList list = new ArrayList(timestamps); + timestamps.clear(); + return list; + } + } + } + + static class CameraEvents implements + CameraVideoCapturer.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 { + Logging.d(TAG, "Waiting for the camera to freeze"); + synchronized (onCameraFreezedLock) { + onCameraFreezedLock.wait(); + return onCameraFreezedDescription; + } + } + } + + /** + * Class to collect all classes related to single capturer instance. + */ + static private class CapturerInstance { + public CameraVideoCapturer capturer; + public CameraEvents cameraEvents; + public SurfaceTextureHelper surfaceTextureHelper; + public FakeCapturerObserver observer; + public List supportedFormats; + public CaptureFormat format; + } + + /** + * Class used for collecting a VideoSource, a VideoTrack and a renderer. The class + * is used for testing local rendering from a capturer. + */ + static private class VideoTrackWithRenderer { + public VideoSource source; + public VideoTrack track; + public RendererCallbacks rendererCallbacks; + public FakeAsyncRenderer fakeAsyncRenderer; + } + + public interface TestObjectFactory { + CameraVideoCapturer createCapturer( + String name, CameraVideoCapturer.CameraEventsHandler eventsHandler); + String getNameOfFrontFacingDevice(); + String getNameOfBackFacingDevice(); + boolean haveTwoCameras(); + boolean isCapturingToTexture(); + Context getAppContext(); + + // CameraVideoCapturer API is too slow for some of our tests where we need to open a competing + // camera. These methods are used instead. + Object rawOpenCamera(String cameraName); + void rawCloseCamera(Object camera); + } + + private PeerConnectionFactory peerConnectionFactory; + private TestObjectFactory testObjectFactory; + + CameraVideoCapturerTestFixtures(TestObjectFactory testObjectFactory) { + PeerConnectionFactory.initializeAndroidGlobals( + testObjectFactory.getAppContext(), true, true, true); + + this.peerConnectionFactory = new PeerConnectionFactory(null /* options */); + this.testObjectFactory = testObjectFactory; + } + + public void dispose() { + this.peerConnectionFactory.dispose(); + } + + // Internal helper methods + private CapturerInstance createCapturer(String name) { + CapturerInstance instance = new CapturerInstance(); + instance.cameraEvents = new CameraEvents(); + instance.capturer = testObjectFactory.createCapturer(name, instance.cameraEvents); + instance.surfaceTextureHelper = SurfaceTextureHelper.create( + "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); + instance.observer = new FakeCapturerObserver(); + instance.supportedFormats = instance.capturer.getSupportedFormats(); + return instance; + } + + private CapturerInstance createCapturer() { + return createCapturer(""); + } + + private void startCapture(CapturerInstance instance) { + startCapture(instance, 0); + } + + private void startCapture(CapturerInstance instance, int formatIndex) { + final CameraEnumerationAndroid.CaptureFormat format = + instance.supportedFormats.get(formatIndex); + + instance.capturer.startCapture(format.width, format.height, format.framerate.max, + instance.surfaceTextureHelper, testObjectFactory.getAppContext(), instance.observer); + instance.format = format; + } + + private void disposeCapturer(CapturerInstance instance) { + instance.capturer.dispose(); + instance.surfaceTextureHelper.returnTextureFrame(); + instance.surfaceTextureHelper.dispose(); + } + + private VideoTrackWithRenderer createVideoTrackWithRenderer(CameraVideoCapturer capturer, + VideoRenderer.Callbacks rendererCallbacks) { + VideoTrackWithRenderer videoTrackWithRenderer = new VideoTrackWithRenderer(); + videoTrackWithRenderer.source = + peerConnectionFactory.createVideoSource(capturer, new MediaConstraints()); + videoTrackWithRenderer.track = + peerConnectionFactory.createVideoTrack("dummy", videoTrackWithRenderer.source); + videoTrackWithRenderer.track.addRenderer(new VideoRenderer(rendererCallbacks)); + return videoTrackWithRenderer; + } + + private VideoTrackWithRenderer createVideoTrackWithRenderer(CameraVideoCapturer capturer) { + RendererCallbacks rendererCallbacks = new RendererCallbacks(); + VideoTrackWithRenderer videoTrackWithRenderer = + createVideoTrackWithRenderer(capturer, rendererCallbacks); + videoTrackWithRenderer.rendererCallbacks = rendererCallbacks; + return videoTrackWithRenderer; + } + + private VideoTrackWithRenderer createVideoTrackWithFakeAsyncRenderer( + CameraVideoCapturer capturer) { + FakeAsyncRenderer fakeAsyncRenderer = new FakeAsyncRenderer(); + VideoTrackWithRenderer videoTrackWithRenderer = + createVideoTrackWithRenderer(capturer, fakeAsyncRenderer); + videoTrackWithRenderer.fakeAsyncRenderer = fakeAsyncRenderer; + return videoTrackWithRenderer; + } + + private void disposeVideoTrackWithRenderer(VideoTrackWithRenderer videoTrackWithRenderer) { + videoTrackWithRenderer.track.dispose(); + videoTrackWithRenderer.source.dispose(); + } + + private void waitUntilIdle(CapturerInstance capturerInstance) throws InterruptedException { + final CountDownLatch barrier = new CountDownLatch(1); + capturerInstance.surfaceTextureHelper.getHandler().post(new Runnable() { + @Override public void run() { + barrier.countDown(); + } + }); + barrier.await(); + } + + private void createCapturerAndRender(String name) throws InterruptedException { + if (name == null) { + Logging.w(TAG, "Skipping video capturer test because device name is null."); + return; + } + + final CapturerInstance capturerInstance = createCapturer(name); + final VideoTrackWithRenderer videoTrackWithRenderer = + createVideoTrackWithRenderer(capturerInstance.capturer); + assertTrue(videoTrackWithRenderer.rendererCallbacks.waitForNextFrameToRender() > 0); + disposeVideoTrackWithRenderer(videoTrackWithRenderer); + disposeCapturer(capturerInstance); + } + + // Test methods + public void createCapturerAndDispose() { + disposeCapturer(createCapturer()); + } + + public void createNonExistingCamera() { + try { + disposeCapturer(createCapturer("non-existing camera")); + } catch (IllegalArgumentException e) { + return; + } + + fail("Expected illegal argument exception when creating non-existing camera."); + } + + public void createCapturerAndRender() throws InterruptedException { + createCapturerAndRender(""); + } + + public void createFrontFacingCapturerAndRender() throws InterruptedException { + createCapturerAndRender(testObjectFactory.getNameOfFrontFacingDevice()); + } + + public void createBackFacingCapturerAndRender() throws InterruptedException { + createCapturerAndRender(testObjectFactory.getNameOfBackFacingDevice()); + } + + public void switchCamera() throws InterruptedException { + if (!testObjectFactory.haveTwoCameras()) { + Logging.w(TAG, + "Skipping test switch video capturer because the device doesn't have two cameras."); + return; + } + + final CapturerInstance capturerInstance = createCapturer(); + final VideoTrackWithRenderer videoTrackWithRenderer = + createVideoTrackWithRenderer(capturerInstance.capturer); + + // Array with one element to avoid final problem in nested classes. + final boolean[] cameraSwitchSuccessful = new boolean[1]; + final CountDownLatch barrier = new CountDownLatch(1); + capturerInstance.capturer.switchCamera(new CameraVideoCapturer.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. + assertTrue(cameraSwitchSuccessful[0]); + // Ensure that frames are received. + assertTrue(videoTrackWithRenderer.rendererCallbacks.waitForNextFrameToRender() > 0); + disposeVideoTrackWithRenderer(videoTrackWithRenderer); + disposeCapturer(capturerInstance); + } + + public void cameraEventsInvoked() throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(); + startCapture(capturerInstance); + // Make sure camera is started and first frame is received and then stop it. + assertTrue(capturerInstance.observer.waitForCapturerToStart()); + capturerInstance.observer.waitForNextCapturedFrame(); + capturerInstance.capturer.stopCapture(); + disposeCapturer(capturerInstance); + + assertTrue(capturerInstance.cameraEvents.onCameraOpeningCalled); + assertTrue(capturerInstance.cameraEvents.onFirstFrameAvailableCalled); + } + + public void cameraCallsAfterStop() throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(); + startCapture(capturerInstance); + // Make sure camera is started and then stop it. + assertTrue(capturerInstance.observer.waitForCapturerToStart()); + capturerInstance.capturer.stopCapture(); + capturerInstance.surfaceTextureHelper.returnTextureFrame(); + + // We can't change |capturer| at this point, but we should not crash. + capturerInstance.capturer.switchCamera(null /* switchEventsHandler */); + capturerInstance.capturer.onOutputFormatRequest(640, 480, 15); + capturerInstance.capturer.changeCaptureFormat(640, 480, 15); + + disposeCapturer(capturerInstance); + } + + public void stopRestartVideoSource() throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(); + final VideoTrackWithRenderer videoTrackWithRenderer = + createVideoTrackWithRenderer(capturerInstance.capturer); + + assertTrue(videoTrackWithRenderer.rendererCallbacks.waitForNextFrameToRender() > 0); + assertEquals(MediaSource.State.LIVE, videoTrackWithRenderer.source.state()); + + videoTrackWithRenderer.source.stop(); + assertEquals(MediaSource.State.ENDED, videoTrackWithRenderer.source.state()); + + videoTrackWithRenderer.source.restart(); + assertTrue(videoTrackWithRenderer.rendererCallbacks.waitForNextFrameToRender() > 0); + assertEquals(MediaSource.State.LIVE, videoTrackWithRenderer.source.state()); + + disposeVideoTrackWithRenderer(videoTrackWithRenderer); + disposeCapturer(capturerInstance); + } + + public void startStopWithDifferentResolutions() throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(); + + for(int i = 0; i < 3 ; ++i) { + startCapture(capturerInstance, i); + assertTrue(capturerInstance.observer.waitForCapturerToStart()); + capturerInstance.observer.waitForNextCapturedFrame(); + + // Check the frame size. The actual width and height depend on how the capturer is mounted. + final boolean identicalResolution = ( + capturerInstance.observer.frameWidth() == capturerInstance.format.width + && capturerInstance.observer.frameHeight() == capturerInstance.format.height); + final boolean flippedResolution = ( + capturerInstance.observer.frameWidth() == capturerInstance.format.height + && capturerInstance.observer.frameHeight() == capturerInstance.format.width); + if (!identicalResolution && !flippedResolution) { + fail("Wrong resolution, got: " + + capturerInstance.observer.frameWidth() + "x" + capturerInstance.observer.frameHeight() + + " expected: "+ capturerInstance.format.width + "x" + capturerInstance.format.height + + " or " + capturerInstance.format.height + "x" + capturerInstance.format.width); + } + + if (testObjectFactory.isCapturingToTexture()) { + assertEquals(0, capturerInstance.observer.frameSize()); + } else { + assertTrue(capturerInstance.format.frameSize() <= capturerInstance.observer.frameSize()); + } + capturerInstance.capturer.stopCapture(); + capturerInstance.surfaceTextureHelper.returnTextureFrame(); + } + disposeCapturer(capturerInstance); + } + + public void returnBufferLate() throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(); + startCapture(capturerInstance); + assertTrue(capturerInstance.observer.waitForCapturerToStart()); + + capturerInstance.observer.waitForNextCapturedFrame(); + capturerInstance.capturer.stopCapture(); + List listOftimestamps = capturerInstance.observer.getCopyAndResetListOftimeStamps(); + assertTrue(listOftimestamps.size() >= 1); + + startCapture(capturerInstance, 1); + capturerInstance.observer.waitForCapturerToStart(); + capturerInstance.surfaceTextureHelper.returnTextureFrame(); + + capturerInstance.observer.waitForNextCapturedFrame(); + capturerInstance.capturer.stopCapture(); + + listOftimestamps = capturerInstance.observer.getCopyAndResetListOftimeStamps(); + assertTrue(listOftimestamps.size() >= 1); + + disposeCapturer(capturerInstance); + } + + public void returnBufferLateEndToEnd() + throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(); + final VideoTrackWithRenderer videoTrackWithRenderer = + createVideoTrackWithFakeAsyncRenderer(capturerInstance.capturer); + // Wait for at least one frame that has not been returned. + assertFalse(videoTrackWithRenderer.fakeAsyncRenderer.waitForPendingFrames().isEmpty()); + + capturerInstance.capturer.stopCapture(); + + // Dispose everything. + disposeVideoTrackWithRenderer(videoTrackWithRenderer); + disposeCapturer(capturerInstance); + + // Return the frame(s), on a different thread out of spite. + final List pendingFrames = + videoTrackWithRenderer.fakeAsyncRenderer.waitForPendingFrames(); + final Thread returnThread = new Thread(new Runnable() { + @Override + public void run() { + for (I420Frame frame : pendingFrames) { + VideoRenderer.renderFrameDone(frame); + } + } + }); + returnThread.start(); + returnThread.join(); + } + + public void cameraFreezedEventOnBufferStarvation() throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(); + startCapture(capturerInstance); + // Make sure camera is started. + assertTrue(capturerInstance.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.", + capturerInstance.cameraEvents.waitForCameraFreezed()); + + capturerInstance.capturer.stopCapture(); + disposeCapturer(capturerInstance); + } + + public void scaleCameraOutput() throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(); + final VideoTrackWithRenderer videoTrackWithRenderer = + createVideoTrackWithRenderer(capturerInstance.capturer); + assertTrue(videoTrackWithRenderer.rendererCallbacks.waitForNextFrameToRender() > 0); + + final int startWidth = videoTrackWithRenderer.rendererCallbacks.frameWidth(); + final int startHeight = videoTrackWithRenderer.rendererCallbacks.frameHeight(); + final int frameRate = 30; + final int scaledWidth = startWidth / 2; + final int scaledHeight = startHeight / 2; + + // Request the captured frames to be scaled. + capturerInstance.capturer.onOutputFormatRequest(scaledWidth, scaledHeight, frameRate); + + boolean gotExpectedResolution = false; + int numberOfInspectedFrames = 0; + + do { + videoTrackWithRenderer.rendererCallbacks.waitForNextFrameToRender(); + ++numberOfInspectedFrames; + + gotExpectedResolution = (videoTrackWithRenderer.rendererCallbacks.frameWidth() == scaledWidth + && videoTrackWithRenderer.rendererCallbacks.frameHeight() == scaledHeight); + } while (!gotExpectedResolution && numberOfInspectedFrames < 30); + + disposeVideoTrackWithRenderer(videoTrackWithRenderer); + disposeCapturer(capturerInstance); + + assertTrue(gotExpectedResolution); + } + + public void startWhileCameraIsAlreadyOpen() throws InterruptedException { + final String cameraName = testObjectFactory.getNameOfBackFacingDevice(); + // At this point camera is not actually opened. + final CapturerInstance capturerInstance = createCapturer(cameraName); + + final Object competingCamera = testObjectFactory.rawOpenCamera(cameraName); + + startCapture(capturerInstance); + + if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) { + // The first opened camera client will be evicted. + assertTrue(capturerInstance.observer.waitForCapturerToStart()); + capturerInstance.capturer.stopCapture(); + } else { + assertFalse(capturerInstance.observer.waitForCapturerToStart()); + } + + testObjectFactory.rawCloseCamera(competingCamera); + disposeCapturer(capturerInstance); + } + + public void startWhileCameraIsAlreadyOpenAndCloseCamera() throws InterruptedException { + final String cameraName = testObjectFactory.getNameOfBackFacingDevice(); + // At this point camera is not actually opened. + final CapturerInstance capturerInstance = createCapturer(cameraName); + + Logging.d(TAG, "startWhileCameraIsAlreadyOpenAndCloseCamera: Opening competing camera."); + final Object competingCamera = testObjectFactory.rawOpenCamera(cameraName); + + Logging.d(TAG, "startWhileCameraIsAlreadyOpenAndCloseCamera: Opening camera."); + final VideoTrackWithRenderer videoTrackWithRenderer = + createVideoTrackWithRenderer(capturerInstance.capturer); + waitUntilIdle(capturerInstance); + + Logging.d(TAG, "startWhileCameraIsAlreadyOpenAndCloseCamera: Closing competing camera."); + testObjectFactory.rawCloseCamera(competingCamera); + + // Make sure camera is started and first frame is received and then stop it. + Logging.d(TAG, "startWhileCameraIsAlreadyOpenAndCloseCamera: Waiting for capture to start."); + videoTrackWithRenderer.rendererCallbacks.waitForNextFrameToRender(); + Logging.d(TAG, "startWhileCameraIsAlreadyOpenAndCloseCamera: Stopping capture."); + capturerInstance.capturer.stopCapture(); + disposeCapturer(capturerInstance); + } + + public void startWhileCameraIsAlreadyOpenAndStop() throws InterruptedException { + final String cameraName = testObjectFactory.getNameOfBackFacingDevice(); + // At this point camera is not actually opened. + final CapturerInstance capturerInstance = createCapturer(cameraName); + + final Object competingCamera = testObjectFactory.rawOpenCamera(cameraName); + + startCapture(capturerInstance); + + capturerInstance.capturer.stopCapture(); + disposeCapturer(capturerInstance); + + testObjectFactory.rawCloseCamera(competingCamera); + } +} diff --git a/webrtc/api/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java b/webrtc/api/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java deleted file mode 100644 index 17bbd13305..0000000000 --- a/webrtc/api/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2015 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -package org.webrtc; - -import android.content.Context; -import android.test.ActivityTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Size; - -import org.webrtc.CameraEnumerationAndroid.CaptureFormat; - -import java.util.HashSet; -import java.util.Set; - -@SuppressWarnings("deprecation") -public class VideoCapturerAndroidTest extends ActivityTestCase { - static final String TAG = "VideoCapturerAndroidTest"; - - @Override - protected void setUp() { - assertTrue(PeerConnectionFactory.initializeAndroidGlobals( - getInstrumentation().getContext(), true, true, true)); - } - - @SmallTest - // Test that enumerating formats using android.hardware.camera2 will give the same formats as - // android.hardware.camera in the range 320x240 to 1280x720. Often the camera2 API may contain - // some high resolutions that are not supported in camera1, but it may also be the other way - // around in some cases. Supported framerates may also differ, so don't compare those. - public void testCamera2Enumerator() { - if (!Camera2Enumerator.isSupported()) { - return; - } - final Context context = getInstrumentation().getContext(); - - for (int i = 0; i < CameraEnumerationAndroid.getDeviceCount(); ++i) { - final Set resolutions1 = new HashSet(); - for (CaptureFormat format : CameraEnumerator.getSupportedFormats(i)) { - resolutions1.add(new Size(format.width, format.height)); - } - final Set resolutions2 = new HashSet(); - for (CaptureFormat format : - Camera2Enumerator.getSupportedFormats(context, Integer.toString(i))) { - resolutions2.add(new Size(format.width, format.height)); - } - for (Size size : resolutions1) { - if (size.getWidth() >= 320 && size.getHeight() >= 240 - && size.getWidth() <= 1280 && size.getHeight() <= 720) { - assertTrue(resolutions2.contains(size)); - } - } - } - } - - @SmallTest - public void testCreateAndRelease() { - VideoCapturerAndroidTestFixtures.release(VideoCapturerAndroid.create("", null)); - } - - @SmallTest - public void testCreateAndReleaseUsingTextures() { - VideoCapturerAndroidTestFixtures.release( - VideoCapturerAndroid.create("", null, true /* captureToTexture */)); - } - - @SmallTest - public void testCreateNonExistingCamera() { - VideoCapturerAndroid capturer = VideoCapturerAndroid.create( - "non-existing camera", null); - assertNull(capturer); - } - - @SmallTest - // This test that the camera can be started and that the frames are forwarded - // to a Java video renderer using a "default" capturer. - // It tests both the Java and the C++ layer. - public void testStartVideoCapturer() throws InterruptedException { - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create("", null); - VideoCapturerAndroidTestFixtures.startCapturerAndRender(capturer); - } - - @SmallTest - public void testStartVideoCapturerUsingTextures() throws InterruptedException { - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create("", null, true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.startCapturerAndRender(capturer); - } - - @SmallTest - // This test that the camera can be started and that the frames are forwarded - // to a Java video renderer using the front facing video capturer. - // It tests both the Java and the C++ layer. - public void testStartFrontFacingVideoCapturer() throws InterruptedException { - String deviceName = CameraEnumerationAndroid.getNameOfFrontFacingDevice(); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null); - VideoCapturerAndroidTestFixtures.startCapturerAndRender(capturer); - } - - @SmallTest - // This test that the camera can be started and that the frames are forwarded - // to a Java video renderer using the back facing video capturer. - // It tests both the Java and the C++ layer. - public void testStartBackFacingVideoCapturer() throws InterruptedException { - if (!VideoCapturerAndroidTestFixtures.HaveTwoCameras()) { - return; - } - - String deviceName = CameraEnumerationAndroid.getNameOfBackFacingDevice(); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null); - VideoCapturerAndroidTestFixtures.startCapturerAndRender(capturer); - } - - @SmallTest - // This test that the default camera can be started and that the camera can - // later be switched to another camera. - // It tests both the Java and the C++ layer. - public void testSwitchVideoCapturer() throws InterruptedException { - VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null); - VideoCapturerAndroidTestFixtures.switchCamera(capturer); - } - - @SmallTest - public void testSwitchVideoCapturerUsingTextures() throws InterruptedException { - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create("", null, true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.switchCamera(capturer); - } - - @MediumTest - public void testCameraEvents() throws InterruptedException { - VideoCapturerAndroidTestFixtures.CameraEvents cameraEvents = - VideoCapturerAndroidTestFixtures.createCameraEvents(); - VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", cameraEvents); - VideoCapturerAndroidTestFixtures.cameraEventsInvoked( - capturer, cameraEvents, getInstrumentation().getContext()); - } - - @MediumTest - public void testCameraEventsUsingTextures() throws InterruptedException { - VideoCapturerAndroidTestFixtures.CameraEvents cameraEvents = - VideoCapturerAndroidTestFixtures.createCameraEvents(); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create("", cameraEvents, true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.cameraEventsInvoked( - capturer, cameraEvents, getInstrumentation().getContext()); - } - - @MediumTest - // Test what happens when attempting to call e.g. switchCamera() after camera has been stopped. - public void testCameraCallsAfterStop() throws InterruptedException { - final String deviceName = CameraEnumerationAndroid.getDeviceName(0); - final VideoCapturerAndroid capturer = VideoCapturerAndroid.create(deviceName, null); - - VideoCapturerAndroidTestFixtures.cameraCallsAfterStop(capturer, - getInstrumentation().getContext()); - } - - @MediumTest - public void testCameraCallsAfterStopUsingTextures() throws InterruptedException { - final String deviceName = CameraEnumerationAndroid.getDeviceName(0); - final VideoCapturerAndroid capturer = VideoCapturerAndroid.create(deviceName, null, - true /* captureToTexture */); - - VideoCapturerAndroidTestFixtures.cameraCallsAfterStop(capturer, - getInstrumentation().getContext()); - } - - @SmallTest - // This test that the VideoSource that the VideoCapturer is connected to can - // be stopped and restarted. It tests both the Java and the C++ layer. - public void testStopRestartVideoSource() throws InterruptedException { - VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null); - VideoCapturerAndroidTestFixtures.stopRestartVideoSource(capturer); - } - - @SmallTest - public void testStopRestartVideoSourceUsingTextures() throws InterruptedException { - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create("", null, true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.stopRestartVideoSource(capturer); - } - - @SmallTest - // This test that the camera can be started at different resolutions. - // It does not test or use the C++ layer. - public void testStartStopWithDifferentResolutions() throws InterruptedException { - String deviceName = CameraEnumerationAndroid.getDeviceName(0); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null); - VideoCapturerAndroidTestFixtures.startStopWithDifferentResolutions(capturer, - getInstrumentation().getContext()); - } - - @SmallTest - public void testStartStopWithDifferentResolutionsUsingTextures() throws InterruptedException { - String deviceName = CameraEnumerationAndroid.getDeviceName(0); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null, true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.startStopWithDifferentResolutions(capturer, - getInstrumentation().getContext()); - } - - @SmallTest - // This test that an error is reported if the camera is already opened - // when VideoCapturerAndroid is started. - public void testStartWhileCameraAlreadyOpened() throws InterruptedException { - String deviceName = CameraEnumerationAndroid.getDeviceName(0); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null); - VideoCapturerAndroidTestFixtures.startWhileCameraIsAlreadyOpen( - capturer, getInstrumentation().getContext()); - } - - @SmallTest - // This test that VideoCapturerAndroid can be started, even if the camera is already opened - // if the camera is closed while VideoCapturerAndroid is re-trying to start. - public void testStartWhileCameraIsAlreadyOpenAndCloseCamera() throws InterruptedException { - String deviceName = CameraEnumerationAndroid.getDeviceName(0); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null); - VideoCapturerAndroidTestFixtures.startWhileCameraIsAlreadyOpenAndCloseCamera( - capturer, getInstrumentation().getContext()); - } - - @SmallTest - // This test that VideoCapturerAndroid.stop can be called while VideoCapturerAndroid is - // re-trying to start. - public void startWhileCameraIsAlreadyOpenAndStop() throws InterruptedException { - String deviceName = CameraEnumerationAndroid.getDeviceName(0); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null); - VideoCapturerAndroidTestFixtures.startWhileCameraIsAlreadyOpenAndStop( - capturer, getInstrumentation().getContext()); - } - - - - @SmallTest - // This test what happens if buffers are returned after the capturer have - // been stopped and restarted. It does not test or use the C++ layer. - public void testReturnBufferLate() throws InterruptedException { - String deviceName = CameraEnumerationAndroid.getDeviceName(0); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null); - VideoCapturerAndroidTestFixtures.returnBufferLate(capturer, - getInstrumentation().getContext()); - } - - @SmallTest - public void testReturnBufferLateUsingTextures() throws InterruptedException { - String deviceName = CameraEnumerationAndroid.getDeviceName(0); - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create(deviceName, null, true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.returnBufferLate(capturer, - getInstrumentation().getContext()); - } - - @MediumTest - // This test that we can capture frames, keep the frames in a local renderer, stop capturing, - // and then return the frames. The difference between the test testReturnBufferLate() is that we - // also test the JNI and C++ AndroidVideoCapturer parts. - public void testReturnBufferLateEndToEnd() throws InterruptedException { - final VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null); - VideoCapturerAndroidTestFixtures.returnBufferLateEndToEnd(capturer); - } - - @MediumTest - public void testReturnBufferLateEndToEndUsingTextures() throws InterruptedException { - final VideoCapturerAndroid capturer = - VideoCapturerAndroid.create("", null, true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.returnBufferLateEndToEnd(capturer); - } - - @MediumTest - // This test that CameraEventsHandler.onError is triggered if video buffers are not returned to - // the capturer. - public void testCameraFreezedEventOnBufferStarvationUsingTextures() throws InterruptedException { - VideoCapturerAndroidTestFixtures.CameraEvents cameraEvents = - VideoCapturerAndroidTestFixtures.createCameraEvents(); - VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", cameraEvents, - true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.cameraFreezedEventOnBufferStarvationUsingTextures(capturer, - cameraEvents, getInstrumentation().getContext()); - } - - @MediumTest - // This test that frames forwarded to a renderer is scaled if onOutputFormatRequest is - // called. This test both Java and C++ parts of of the stack. - public void testScaleCameraOutput() throws InterruptedException { - VideoCapturerAndroid capturer = VideoCapturerAndroid.create("", null); - VideoCapturerAndroidTestFixtures.scaleCameraOutput(capturer); - } - - @MediumTest - // This test that frames forwarded to a renderer is scaled if onOutputFormatRequest is - // called. This test both Java and C++ parts of of the stack. - public void testScaleCameraOutputUsingTextures() throws InterruptedException { - VideoCapturerAndroid capturer = - VideoCapturerAndroid.create("", null, true /* captureToTexture */); - VideoCapturerAndroidTestFixtures.scaleCameraOutput(capturer); - } -} diff --git a/webrtc/api/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java b/webrtc/api/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java deleted file mode 100644 index 4c96b8cff0..0000000000 --- a/webrtc/api/androidtests/src/org/webrtc/VideoCapturerAndroidTestFixtures.java +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright 2015 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -package org.webrtc; - -import android.content.Context; - -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.*; - -@SuppressWarnings("deprecation") -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 pendingFrames = new ArrayList(); - - @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 waitForPendingFrames() throws InterruptedException { - synchronized (pendingFrames) { - while (pendingFrames.isEmpty()) { - pendingFrames.wait(); - } - return new ArrayList(pendingFrames); - } - } - } - - static class FakeCapturerObserver implements VideoCapturer.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 timestamps = new ArrayList(); - - @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 getCopyAndResetListOftimeStamps() { - synchronized (frameLock) { - ArrayList list = new ArrayList(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 (android.hardware.Camera.getNumberOfCameras() >= 2); - } - - static public void release(VideoCapturerAndroid capturer) { - assertNotNull(capturer); - capturer.dispose(); - } - - static public void startCapturerAndRender(VideoCapturerAndroid capturer) - throws InterruptedException { - PeerConnectionFactory factory = new PeerConnectionFactory(null /* options */); - 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(); - } - - static public void switchCamera(VideoCapturerAndroid capturer) throws InterruptedException { - PeerConnectionFactory factory = new PeerConnectionFactory(null /* options */); - 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(); - } - - static public void cameraEventsInvoked(VideoCapturerAndroid capturer, CameraEvents events, - Context appContext) throws InterruptedException { - final List formats = capturer.getSupportedFormats(); - final CameraEnumerationAndroid.CaptureFormat format = formats.get(0); - - final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( - "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); - final FakeCapturerObserver observer = new FakeCapturerObserver(); - capturer.startCapture(format.width, format.height, format.framerate.max, - surfaceTextureHelper, 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()) { - surfaceTextureHelper.returnTextureFrame(); - } - release(capturer); - surfaceTextureHelper.dispose(); - - assertTrue(events.onCameraOpeningCalled); - assertTrue(events.onFirstFrameAvailableCalled); - } - - static public void cameraCallsAfterStop( - VideoCapturerAndroid capturer, Context appContext) throws InterruptedException { - final List formats = capturer.getSupportedFormats(); - final CameraEnumerationAndroid.CaptureFormat format = formats.get(0); - - final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( - "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); - final FakeCapturerObserver observer = new FakeCapturerObserver(); - capturer.startCapture(format.width, format.height, format.framerate.max, - surfaceTextureHelper, appContext, observer); - // Make sure camera is started and then stop it. - assertTrue(observer.WaitForCapturerToStart()); - capturer.stopCapture(); - if (capturer.isCapturingToTexture()) { - surfaceTextureHelper.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); - - release(capturer); - surfaceTextureHelper.dispose(); - } - - static public void stopRestartVideoSource(VideoCapturerAndroid capturer) - throws InterruptedException { - PeerConnectionFactory factory = new PeerConnectionFactory(null /* options */); - 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(); - } - - static public void startStopWithDifferentResolutions(VideoCapturerAndroid capturer, - Context appContext) throws InterruptedException { - final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( - "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); - FakeCapturerObserver observer = new FakeCapturerObserver(); - List formats = capturer.getSupportedFormats(); - - for(int i = 0; i < 3 ; ++i) { - CameraEnumerationAndroid.CaptureFormat format = formats.get(i); - capturer.startCapture(format.width, format.height, format.framerate.max, - surfaceTextureHelper, 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()) { - surfaceTextureHelper.returnTextureFrame(); - } - } - release(capturer); - surfaceTextureHelper.dispose(); - } - - 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 { - final List formats = capturer.getSupportedFormats(); - final CameraEnumerationAndroid.CaptureFormat format = formats.get(0); - android.hardware.Camera camera = android.hardware.Camera.open(capturer.getCurrentCameraId()); - - final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( - "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); - final FakeCapturerObserver observer = new FakeCapturerObserver(); - capturer.startCapture(format.width, format.height, format.framerate.max, - surfaceTextureHelper, 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()); - } - - release(capturer); - camera.release(); - surfaceTextureHelper.dispose(); - } - - static public void startWhileCameraIsAlreadyOpenAndCloseCamera( - VideoCapturerAndroid capturer, Context appContext) throws InterruptedException { - final PeerConnectionFactory factory = new PeerConnectionFactory(null /* options */); - final List formats = capturer.getSupportedFormats(); - final CameraEnumerationAndroid.CaptureFormat format = formats.get(0); - android.hardware.Camera camera = android.hardware.Camera.open(capturer.getCurrentCameraId()); - - final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( - "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); - final VideoSource source = factory.createVideoSource(capturer, new MediaConstraints()); - final VideoTrack track = factory.createVideoTrack("dummy", source); - final RendererCallbacks callbacks = new RendererCallbacks(); - track.addRenderer(new VideoRenderer(callbacks)); - waitUntilIdle(capturer); - - camera.release(); - - // Make sure camera is started and first frame is received and then stop it. - callbacks.WaitForNextFrameToRender(); - capturer.stopCapture(); - release(capturer); - surfaceTextureHelper.dispose(); - } - - static public void startWhileCameraIsAlreadyOpenAndStop( - VideoCapturerAndroid capturer, Context appContext) throws InterruptedException { - final List formats = capturer.getSupportedFormats(); - final CameraEnumerationAndroid.CaptureFormat format = formats.get(0); - android.hardware.Camera camera = android.hardware.Camera.open(capturer.getCurrentCameraId()); - - final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( - "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); - final FakeCapturerObserver observer = new FakeCapturerObserver(); - capturer.startCapture(format.width, format.height, format.framerate.max, - surfaceTextureHelper, appContext, observer); - capturer.stopCapture(); - release(capturer); - camera.release(); - surfaceTextureHelper.dispose(); - } - - static public void returnBufferLate(VideoCapturerAndroid capturer, - Context appContext) throws InterruptedException { - final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( - "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); - FakeCapturerObserver observer = new FakeCapturerObserver(); - - List formats = capturer.getSupportedFormats(); - CameraEnumerationAndroid.CaptureFormat format = formats.get(0); - capturer.startCapture(format.width, format.height, format.framerate.max, - surfaceTextureHelper, appContext, observer); - assertTrue(observer.WaitForCapturerToStart()); - - observer.WaitForNextCapturedFrame(); - capturer.stopCapture(); - List listOftimestamps = observer.getCopyAndResetListOftimeStamps(); - assertTrue(listOftimestamps.size() >= 1); - - format = formats.get(1); - capturer.startCapture(format.width, format.height, format.framerate.max, - surfaceTextureHelper, appContext, observer); - observer.WaitForCapturerToStart(); - if (capturer.isCapturingToTexture()) { - surfaceTextureHelper.returnTextureFrame(); - } - - observer.WaitForNextCapturedFrame(); - capturer.stopCapture(); - - listOftimestamps = observer.getCopyAndResetListOftimeStamps(); - assertTrue(listOftimestamps.size() >= 1); - if (capturer.isCapturingToTexture()) { - surfaceTextureHelper.returnTextureFrame(); - } - - release(capturer); - surfaceTextureHelper.dispose(); - } - - static public void returnBufferLateEndToEnd(VideoCapturerAndroid capturer) - throws InterruptedException { - final PeerConnectionFactory factory = new PeerConnectionFactory(null /* options */); - 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(); - - // Return the frame(s), on a different thread out of spite. - final List 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 formats = capturer.getSupportedFormats(); - final CameraEnumerationAndroid.CaptureFormat format = formats.get(0); - - final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create( - "SurfaceTextureHelper test" /* threadName */, null /* sharedContext */); - final FakeCapturerObserver observer = new FakeCapturerObserver(); - capturer.startCapture(format.width, format.height, format.framerate.max, - surfaceTextureHelper, 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()) { - surfaceTextureHelper.returnTextureFrame(); - } - - release(capturer); - surfaceTextureHelper.dispose(); - } - - static public void scaleCameraOutput(VideoCapturerAndroid capturer) throws InterruptedException { - PeerConnectionFactory factory = new PeerConnectionFactory(null /* options */); - 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(gotExpectedResolution); - } - -} diff --git a/webrtc/api/java/android/org/webrtc/CameraEnumerationAndroid.java b/webrtc/api/java/android/org/webrtc/CameraEnumerationAndroid.java index 44c850538e..ed93bd12a0 100644 --- a/webrtc/api/java/android/org/webrtc/CameraEnumerationAndroid.java +++ b/webrtc/api/java/android/org/webrtc/CameraEnumerationAndroid.java @@ -11,10 +11,11 @@ package org.webrtc; import static java.lang.Math.abs; -import android.graphics.ImageFormat; import org.webrtc.Logging; +import android.graphics.ImageFormat; + import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -137,6 +138,18 @@ public class CameraEnumerationAndroid { + ", Orientation " + info.orientation; } + // Returns the camera index for camera with name |deviceName|, or throws IllegalArgumentException + // if no such camera can be found. + public static int getCameraIndex(String deviceName) { + Logging.d(TAG, "getCameraIndex: " + deviceName); + for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) { + if (deviceName.equals(CameraEnumerationAndroid.getDeviceName(i))) { + return i; + } + } + throw new IllegalArgumentException("No such camera: " + deviceName); + } + // Returns the name of the front facing camera. Returns null if the // camera can not be used or does not exist. public static String getNameOfFrontFacingDevice() { diff --git a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java b/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java index 4a5ae15341..f40f7e6728 100644 --- a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java +++ b/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java @@ -10,15 +10,14 @@ package org.webrtc; +import org.webrtc.CameraEnumerationAndroid.CaptureFormat; + import android.content.Context; import android.os.Handler; import android.os.SystemClock; import android.view.Surface; import android.view.WindowManager; -import org.webrtc.CameraEnumerationAndroid.CaptureFormat; -import org.webrtc.Logging; - import java.io.IOException; import java.nio.ByteBuffer; import java.util.HashSet; @@ -199,7 +198,7 @@ public class VideoCapturerAndroid implements // Helper function to retrieve the current camera id synchronously. Note that the camera id might // change at any point by switchCamera() calls. - int getCurrentCameraId() { + private int getCurrentCameraId() { synchronized (cameraIdLock) { return id; } @@ -223,7 +222,7 @@ public class VideoCapturerAndroid implements if (cameraName == null || cameraName == "") { this.id = 0; } else { - this.id = getCameraIndex(cameraName); + this.id = CameraEnumerationAndroid.getCameraIndex(cameraName); } this.eventsHandler = eventsHandler; isCapturingToTexture = captureToTexture; @@ -240,18 +239,6 @@ public class VideoCapturerAndroid implements } } - // Returns the camera index for camera with name |deviceName|, or throws IllegalArgumentException - // if no such camera can be found. - private static int getCameraIndex(String deviceName) { - Logging.d(TAG, "getCameraIndex: " + deviceName); - for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) { - if (deviceName.equals(CameraEnumerationAndroid.getDeviceName(i))) { - return i; - } - } - throw new IllegalArgumentException("No such camera: " + deviceName); - } - private boolean maybePostOnCameraThread(Runnable runnable) { return maybePostDelayedOnCameraThread(0 /* delayMs */, runnable); } diff --git a/webrtc/api/java/src/org/webrtc/VideoCapturer.java b/webrtc/api/java/src/org/webrtc/VideoCapturer.java index 5a515d2261..452009b3ac 100644 --- a/webrtc/api/java/src/org/webrtc/VideoCapturer.java +++ b/webrtc/api/java/src/org/webrtc/VideoCapturer.java @@ -15,7 +15,6 @@ import android.content.Context; import java.util.List; // Base interface for all VideoCapturers to implement. -// TODO(magjed): Simplify and improve this interface. public interface VideoCapturer { // Interface used for providing callbacks to an observer. public interface CapturerObserver { @@ -83,7 +82,6 @@ public interface VideoCapturer { int width, int height, int framerate); } - /** * Returns a list with all the formats this VideoCapturer supports. */ @@ -104,6 +102,10 @@ public interface VideoCapturer { */ void stopCapture() throws InterruptedException; + void onOutputFormatRequest(int width, int height, int framerate); + + void changeCaptureFormat(int width, int height, int framerate); + /** * Perform any final cleanup here. No more capturing will be done after this call. */