• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 package org.webrtc;
12 
13 import static org.junit.Assert.fail;
14 
15 import android.content.Context;
16 import android.hardware.camera2.CameraAccessException;
17 import android.hardware.camera2.CameraDevice;
18 import android.hardware.camera2.CameraManager;
19 import android.os.Handler;
20 import android.os.Looper;
21 import android.support.test.InstrumentationRegistry;
22 import androidx.annotation.Nullable;
23 import androidx.test.filters.LargeTest;
24 import androidx.test.filters.MediumTest;
25 import androidx.test.filters.SmallTest;
26 import java.util.concurrent.CountDownLatch;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Test;
30 
31 public class Camera2CapturerTest {
32   static final String TAG = "Camera2CapturerTest";
33 
34   /**
35    * Simple camera2 implementation that only knows how to open the camera and close it.
36    */
37   private class SimpleCamera2 {
38     final CameraManager cameraManager;
39     final LooperThread looperThread;
40     final CountDownLatch openDoneSignal;
41     final Object cameraDeviceLock;
42     @Nullable CameraDevice cameraDevice; // Guarded by cameraDeviceLock
43     boolean openSucceeded; // Guarded by cameraDeviceLock
44 
45     private class LooperThread extends Thread {
46       final CountDownLatch startedSignal = new CountDownLatch(1);
47       private Handler handler;
48 
49       @Override
run()50       public void run() {
51         Looper.prepare();
52         handler = new Handler();
53         startedSignal.countDown();
54         Looper.loop();
55       }
56 
waitToStart()57       public void waitToStart() {
58         ThreadUtils.awaitUninterruptibly(startedSignal);
59       }
60 
requestStop()61       public void requestStop() {
62         handler.getLooper().quit();
63       }
64 
getHandler()65       public Handler getHandler() {
66         return handler;
67       }
68     }
69 
70     private class CameraStateCallback extends CameraDevice.StateCallback {
71       @Override
onClosed(CameraDevice cameraDevice)72       public void onClosed(CameraDevice cameraDevice) {
73         Logging.d(TAG, "Simple camera2 closed.");
74 
75         synchronized (cameraDeviceLock) {
76           SimpleCamera2.this.cameraDevice = null;
77         }
78       }
79 
80       @Override
onDisconnected(CameraDevice cameraDevice)81       public void onDisconnected(CameraDevice cameraDevice) {
82         Logging.d(TAG, "Simple camera2 disconnected.");
83 
84         synchronized (cameraDeviceLock) {
85           SimpleCamera2.this.cameraDevice = null;
86         }
87       }
88 
89       @Override
onError(CameraDevice cameraDevice, int errorCode)90       public void onError(CameraDevice cameraDevice, int errorCode) {
91         Logging.w(TAG, "Simple camera2 error: " + errorCode);
92 
93         synchronized (cameraDeviceLock) {
94           SimpleCamera2.this.cameraDevice = cameraDevice;
95           openSucceeded = false;
96         }
97 
98         openDoneSignal.countDown();
99       }
100 
101       @Override
onOpened(CameraDevice cameraDevice)102       public void onOpened(CameraDevice cameraDevice) {
103         Logging.d(TAG, "Simple camera2 opened.");
104 
105         synchronized (cameraDeviceLock) {
106           SimpleCamera2.this.cameraDevice = cameraDevice;
107           openSucceeded = true;
108         }
109 
110         openDoneSignal.countDown();
111       }
112     }
113 
SimpleCamera2(Context context, String deviceName)114     SimpleCamera2(Context context, String deviceName) {
115       cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
116       looperThread = new LooperThread();
117       looperThread.start();
118       looperThread.waitToStart();
119       cameraDeviceLock = new Object();
120       openDoneSignal = new CountDownLatch(1);
121       cameraDevice = null;
122       Logging.d(TAG, "Opening simple camera2.");
123       try {
124         cameraManager.openCamera(deviceName, new CameraStateCallback(), looperThread.getHandler());
125       } catch (CameraAccessException e) {
126         fail("Simple camera2 CameraAccessException: " + e.getMessage());
127       }
128 
129       Logging.d(TAG, "Waiting for simple camera2 to open.");
130       ThreadUtils.awaitUninterruptibly(openDoneSignal);
131       synchronized (cameraDeviceLock) {
132         if (!openSucceeded) {
133           fail("Opening simple camera2 failed.");
134         }
135       }
136     }
137 
close()138     public void close() {
139       Logging.d(TAG, "Closing simple camera2.");
140       synchronized (cameraDeviceLock) {
141         if (cameraDevice != null) {
142           cameraDevice.close();
143         }
144       }
145 
146       looperThread.requestStop();
147       ThreadUtils.joinUninterruptibly(looperThread);
148     }
149   }
150 
151   private class TestObjectFactory extends CameraVideoCapturerTestFixtures.TestObjectFactory {
152     @Override
getCameraEnumerator()153     public CameraEnumerator getCameraEnumerator() {
154       return new Camera2Enumerator(getAppContext());
155     }
156 
157     @Override
getAppContext()158     public Context getAppContext() {
159       return InstrumentationRegistry.getTargetContext();
160     }
161 
162     @SuppressWarnings("deprecation")
163     @Override
rawOpenCamera(String cameraName)164     public Object rawOpenCamera(String cameraName) {
165       return new SimpleCamera2(getAppContext(), cameraName);
166     }
167 
168     @SuppressWarnings("deprecation")
169     @Override
rawCloseCamera(Object camera)170     public void rawCloseCamera(Object camera) {
171       ((SimpleCamera2) camera).close();
172     }
173   }
174 
175   private CameraVideoCapturerTestFixtures fixtures;
176 
177   @Before
setUp()178   public void setUp() {
179     fixtures = new CameraVideoCapturerTestFixtures(new TestObjectFactory());
180   }
181 
182   @After
tearDown()183   public void tearDown() {
184     fixtures.dispose();
185   }
186 
187   @Test
188   @SmallTest
testCreateAndDispose()189   public void testCreateAndDispose() throws InterruptedException {
190     fixtures.createCapturerAndDispose();
191   }
192 
193   @Test
194   @SmallTest
testCreateNonExistingCamera()195   public void testCreateNonExistingCamera() throws InterruptedException {
196     fixtures.createNonExistingCamera();
197   }
198 
199   // This test that the camera can be started and that the frames are forwarded
200   // to a Java video renderer using a "default" capturer.
201   // It tests both the Java and the C++ layer.
202   @Test
203   @MediumTest
testCreateCapturerAndRender()204   public void testCreateCapturerAndRender() throws InterruptedException {
205     fixtures.createCapturerAndRender();
206   }
207 
208   // This test that the camera can be started and that the frames are forwarded
209   // to a Java video renderer using the front facing video capturer.
210   // It tests both the Java and the C++ layer.
211   @Test
212   @MediumTest
testStartFrontFacingVideoCapturer()213   public void testStartFrontFacingVideoCapturer() throws InterruptedException {
214     fixtures.createFrontFacingCapturerAndRender();
215   }
216 
217   // This test that the camera can be started and that the frames are forwarded
218   // to a Java video renderer using the back facing video capturer.
219   // It tests both the Java and the C++ layer.
220   @Test
221   @MediumTest
testStartBackFacingVideoCapturer()222   public void testStartBackFacingVideoCapturer() throws InterruptedException {
223     fixtures.createBackFacingCapturerAndRender();
224   }
225 
226   // This test that the default camera can be started and that the camera can
227   // later be switched to another camera.
228   // It tests both the Java and the C++ layer.
229   @Test
230   @MediumTest
testSwitchVideoCapturer()231   public void testSwitchVideoCapturer() throws InterruptedException {
232     fixtures.switchCamera();
233   }
234 
235   @Test
236   @MediumTest
testSwitchVideoCapturerToSpecificCameraName()237   public void testSwitchVideoCapturerToSpecificCameraName() throws InterruptedException {
238     fixtures.switchCamera(true /* specifyCameraName */);
239   }
240 
241   @Test
242   @MediumTest
testCameraEvents()243   public void testCameraEvents() throws InterruptedException {
244     fixtures.cameraEventsInvoked();
245   }
246 
247   // Test what happens when attempting to call e.g. switchCamera() after camera has been stopped.
248   @Test
249   @MediumTest
testCameraCallsAfterStop()250   public void testCameraCallsAfterStop() throws InterruptedException {
251     fixtures.cameraCallsAfterStop();
252   }
253 
254   // This test that the VideoSource that the CameraVideoCapturer is connected to can
255   // be stopped and restarted. It tests both the Java and the C++ layer.
256   @Test
257   @LargeTest
testStopRestartVideoSource()258   public void testStopRestartVideoSource() throws InterruptedException {
259     fixtures.stopRestartVideoSource();
260   }
261 
262   // This test that the camera can be started at different resolutions.
263   // It does not test or use the C++ layer.
264   @Test
265   @LargeTest
testStartStopWithDifferentResolutions()266   public void testStartStopWithDifferentResolutions() throws InterruptedException {
267     fixtures.startStopWithDifferentResolutions();
268   }
269 
270   // This test what happens if buffers are returned after the capturer have
271   // been stopped and restarted. It does not test or use the C++ layer.
272   @Test
273   @LargeTest
testReturnBufferLate()274   public void testReturnBufferLate() throws InterruptedException {
275     fixtures.returnBufferLate();
276   }
277 
278   // This test that we can capture frames, keep the frames in a local renderer, stop capturing,
279   // and then return the frames. The difference between the test testReturnBufferLate() is that we
280   // also test the JNI and C++ AndroidVideoCapturer parts.
281   @Test
282   @MediumTest
testReturnBufferLateEndToEnd()283   public void testReturnBufferLateEndToEnd() throws InterruptedException {
284     fixtures.returnBufferLateEndToEnd();
285   }
286 
287   // This test that CameraEventsHandler.onError is triggered if video buffers are not returned to
288   // the capturer.
289   @Test
290   @LargeTest
testCameraFreezedEventOnBufferStarvation()291   public void testCameraFreezedEventOnBufferStarvation() throws InterruptedException {
292     fixtures.cameraFreezedEventOnBufferStarvation();
293   }
294 
295   // This test that frames forwarded to a renderer is scaled if adaptOutputFormat is
296   // called. This test both Java and C++ parts of of the stack.
297   @Test
298   @MediumTest
testScaleCameraOutput()299   public void testScaleCameraOutput() throws InterruptedException {
300     fixtures.scaleCameraOutput();
301   }
302 
303   // This test that frames forwarded to a renderer is cropped to a new orientation if
304   // adaptOutputFormat is called in such a way. This test both Java and C++ parts of of the stack.
305   @Test
306   @MediumTest
testCropCameraOutput()307   public void testCropCameraOutput() throws InterruptedException {
308     fixtures.cropCameraOutput();
309   }
310 
311   // This test that an error is reported if the camera is already opened
312   // when CameraVideoCapturer is started.
313   @Test
314   @LargeTest
testStartWhileCameraIsAlreadyOpen()315   public void testStartWhileCameraIsAlreadyOpen() throws InterruptedException {
316     fixtures.startWhileCameraIsAlreadyOpen();
317   }
318 
319   // This test that CameraVideoCapturer can be started, even if the camera is already opened
320   // if the camera is closed while CameraVideoCapturer is re-trying to start.
321   @Test
322   @LargeTest
testStartWhileCameraIsAlreadyOpenAndCloseCamera()323   public void testStartWhileCameraIsAlreadyOpenAndCloseCamera() throws InterruptedException {
324     fixtures.startWhileCameraIsAlreadyOpenAndCloseCamera();
325   }
326 
327   // This test that CameraVideoCapturer.stop can be called while CameraVideoCapturer is
328   // re-trying to start.
329   @Test
330   @MediumTest
testStartWhileCameraIsAlreadyOpenAndStop()331   public void testStartWhileCameraIsAlreadyOpenAndStop() throws InterruptedException {
332     fixtures.startWhileCameraIsAlreadyOpenAndStop();
333   }
334 }
335