Android: Expose VideoCapturer.isCapturing() and add tests Add a boolean isCapturing() method to the VideoCapturer interface and implement it in FileVideoCapturer, ScreenCapturerAndroid, and CameraCapturer. Allows callers to query capture state without tracking it externally. Tests: - FileVideoCapturerTest: lifecycle assertion (start -> stop). - CameraVideoCapturerTestFixtures: shared start/stop/restart helper, wired into Camera1 (byte buffer + texture) and Camera2 tests. Bug: webrtc:512514764 Change-Id: I576fec9721b69e328fdd8d2cd91c819cf67c19f4 Co-authored-by: Danilo Bargen <danilo.bargen@threema.ch> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/472200 Commit-Queue: Philipp Hancke <philipp.hancke@googlemail.com> Reviewed-by: Magnus Jedvert <magjed@webrtc.org> Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org> Cr-Commit-Position: refs/heads/main@{#48054}
diff --git a/AUTHORS b/AUTHORS index 7c4ea2c..77a7557 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -209,6 +209,7 @@ RingCentral, Inc. <*@ringcentral.com> Signal Messenger, LLC <*@signal.org> Sinch AB <*@sinch.com> +SoftAvail, Inc. <*@softavail.com> struktur AG <*@struktur.de> Telenor Digital AS <*@telenor.com> Temasys Communications <*@temasys.io>
diff --git a/sdk/android/api/org/webrtc/FileVideoCapturer.java b/sdk/android/api/org/webrtc/FileVideoCapturer.java index 408d8b5..2cddd30 100644 --- a/sdk/android/api/org/webrtc/FileVideoCapturer.java +++ b/sdk/android/api/org/webrtc/FileVideoCapturer.java
@@ -141,6 +141,7 @@ private final VideoReader videoReader; private CapturerObserver capturerObserver; private final Timer timer = new Timer(); + private boolean isCapturing; private final TimerTask tickTask = new TimerTask() { @Override @@ -173,11 +174,13 @@ @Override public void startCapture(int width, int height, int framerate) { timer.schedule(tickTask, 0, 1000 / framerate); + isCapturing = true; } @Override public void stopCapture() throws InterruptedException { timer.cancel(); + isCapturing = false; } @Override @@ -186,6 +189,11 @@ } @Override + public boolean isCapturing() { + return isCapturing; + } + + @Override public void dispose() { videoReader.close(); }
diff --git a/sdk/android/api/org/webrtc/ScreenCapturerAndroid.java b/sdk/android/api/org/webrtc/ScreenCapturerAndroid.java index 08b03bd..2855b17 100644 --- a/sdk/android/api/org/webrtc/ScreenCapturerAndroid.java +++ b/sdk/android/api/org/webrtc/ScreenCapturerAndroid.java
@@ -217,6 +217,12 @@ return true; } + @Override + public boolean isCapturing() { + // The virtual display is created in startCapture() and set to null in stopCapture() + return virtualDisplay != null; + } + public long getNumCapturedFrames() { return numCapturedFrames; }
diff --git a/sdk/android/api/org/webrtc/VideoCapturer.java b/sdk/android/api/org/webrtc/VideoCapturer.java index 67eb7ab..39a7607 100644 --- a/sdk/android/api/org/webrtc/VideoCapturer.java +++ b/sdk/android/api/org/webrtc/VideoCapturer.java
@@ -42,6 +42,11 @@ void changeCaptureFormat(int width, int height, int framerate); /** + * @return true if capturing is currently active. + */ + boolean isCapturing(); + + /** * Perform any final cleanup here. No more capturing will be done after this call. */ void dispose();
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/Camera1CapturerUsingByteBufferTest.java b/sdk/android/instrumentationtests/src/org/webrtc/Camera1CapturerUsingByteBufferTest.java index b83a77d..a0ad59f 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/Camera1CapturerUsingByteBufferTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/Camera1CapturerUsingByteBufferTest.java
@@ -202,4 +202,12 @@ public void testStartWhileCameraIsAlreadyOpenAndStop() throws InterruptedException { fixtures.startWhileCameraIsAlreadyOpenAndStop(); } + + // This test that VideoCapturer.isCapturing() returns the correct value across the + // start -> stop -> restart -> stop lifecycle. + @Test + @MediumTest + public void testIsCapturing() throws InterruptedException { + fixtures.capturerReportsCapturingState(); + } }
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/Camera1CapturerUsingTextureTest.java b/sdk/android/instrumentationtests/src/org/webrtc/Camera1CapturerUsingTextureTest.java index 582c3b8..3714dc6 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/Camera1CapturerUsingTextureTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/Camera1CapturerUsingTextureTest.java
@@ -205,4 +205,12 @@ public void testStartWhileCameraIsAlreadyOpenAndStop() throws InterruptedException { fixtures.startWhileCameraIsAlreadyOpenAndStop(); } + + // This test that VideoCapturer.isCapturing() returns the correct value across the + // start -> stop -> restart -> stop lifecycle. + @Test + @MediumTest + public void testIsCapturing() throws InterruptedException { + fixtures.capturerReportsCapturingState(); + } }
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/Camera2CapturerTest.java b/sdk/android/instrumentationtests/src/org/webrtc/Camera2CapturerTest.java index 5851146..781ec4e 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/Camera2CapturerTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/Camera2CapturerTest.java
@@ -332,4 +332,12 @@ public void testStartWhileCameraIsAlreadyOpenAndStop() throws InterruptedException { fixtures.startWhileCameraIsAlreadyOpenAndStop(); } + + // This test that VideoCapturer.isCapturing() returns the correct value across the + // start -> stop -> restart -> stop lifecycle. + @Test + @MediumTest + public void testIsCapturing() throws InterruptedException { + fixtures.capturerReportsCapturingState(); + } }
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java b/sdk/android/instrumentationtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java index 551d75f..b42e287 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java
@@ -790,4 +790,40 @@ testObjectFactory.rawCloseCamera(competingCamera); } + + // Verifies that VideoCapturer.isCapturing() reflects the lifecycle of + // startCapture()/stopCapture() across a full start/stop/restart cycle. + public void capturerReportsCapturingState() throws InterruptedException { + final CapturerInstance capturerInstance = createCapturer(true /* initialize */); + + assertFalse("Should not be capturing before startCapture()", + capturerInstance.capturer.isCapturing()); + + startCapture(capturerInstance); + assertTrue(capturerInstance.observer.waitForCapturerToStart()); + capturerInstance.observer.waitForNextCapturedFrame(); + assertTrue("Should be capturing after the capturer has started", + capturerInstance.capturer.isCapturing()); + + capturerInstance.capturer.stopCapture(); + capturerInstance.observer.releaseFrame(); + capturerInstance.cameraEvents.waitForCameraClosed(); + assertFalse("Should not be capturing after stopCapture()", + capturerInstance.capturer.isCapturing()); + + // Restart to verify the flag flips back to true on a subsequent start. + startCapture(capturerInstance); + assertTrue(capturerInstance.observer.waitForCapturerToStart()); + capturerInstance.observer.waitForNextCapturedFrame(); + assertTrue("Should be capturing again after restart", + capturerInstance.capturer.isCapturing()); + + capturerInstance.capturer.stopCapture(); + capturerInstance.observer.releaseFrame(); + capturerInstance.cameraEvents.waitForCameraClosed(); + assertFalse(capturerInstance.capturer.isCapturing()); + + capturerInstance.capturer.dispose(); + capturerInstance.surfaceTextureHelper.dispose(); + } }
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java b/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java index 8584ddf..c09f458 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java
@@ -11,6 +11,7 @@ package org.webrtc; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Environment; @@ -120,6 +121,33 @@ } } + // This test that FileVideoCapturer.isCapturing() returns the correct value across the + // initialize -> start -> stop lifecycle. + @Test + @SmallTest + public void testIsCapturing() throws InterruptedException, IOException { + final FileVideoCapturer fileVideoCapturer = + new FileVideoCapturer(Environment.getExternalStorageDirectory().getPath() + + "/chromium_tests_root/sdk/android/instrumentationtests/src/org/webrtc/" + + "capturetestvideo.y4m"); + final MockCapturerObserver capturerObserver = new MockCapturerObserver(); + fileVideoCapturer.initialize( + null /* surfaceTextureHelper */, null /* applicationContext */, capturerObserver); + + assertFalse( + "Should not be capturing before startCapture()", fileVideoCapturer.isCapturing()); + + fileVideoCapturer.startCapture(4 /* width */, 4 /* height */, 30 /* fps */); + assertTrue( + "Should be capturing after startCapture()", fileVideoCapturer.isCapturing()); + + fileVideoCapturer.stopCapture(); + assertFalse( + "Should not be capturing after stopCapture()", fileVideoCapturer.isCapturing()); + + fileVideoCapturer.dispose(); + } + private static void assertByteBufferContents(byte[] expected, ByteBuffer actual) { assertEquals("Unexpected ByteBuffer size.", expected.length, actual.remaining()); for (int i = 0; i < expected.length; i++) {
diff --git a/sdk/android/src/java/org/webrtc/CameraCapturer.java b/sdk/android/src/java/org/webrtc/CameraCapturer.java index 1922a52..2375fba 100644 --- a/sdk/android/src/java/org/webrtc/CameraCapturer.java +++ b/sdk/android/src/java/org/webrtc/CameraCapturer.java
@@ -313,6 +313,13 @@ } @Override + public boolean isCapturing() { + synchronized (stateLock) { + return sessionOpening || currentSession != null; + } + } + + @Override public void changeCaptureFormat(int width, int height, int framerate) { Logging.d(TAG, "changeCaptureFormat: " + width + "x" + height + "@" + framerate); synchronized (stateLock) {