From f8ffb25c562c15cdcf194142fa10ff2a7f1ca36a Mon Sep 17 00:00:00 2001 From: Alex Andres Date: Sun, 25 Oct 2026 14:23:00 +0100 Subject: [PATCH] test: start recording a client API call once its remote video decodes A signaled track need not carry video yet, and on a slow runner the recorder then waited past its header timeout for a key frame. Warnings are now part of the failure message. --- .../media/recorder/ClientApiRecorderTest.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/webrtc-media/src/test/java/dev/onvoid/webrtc/media/recorder/ClientApiRecorderTest.java b/webrtc-media/src/test/java/dev/onvoid/webrtc/media/recorder/ClientApiRecorderTest.java index d663c6f3..d90598cd 100644 --- a/webrtc-media/src/test/java/dev/onvoid/webrtc/media/recorder/ClientApiRecorderTest.java +++ b/webrtc-media/src/test/java/dev/onvoid/webrtc/media/recorder/ClientApiRecorderTest.java @@ -42,6 +42,7 @@ import dev.onvoid.webrtc.client.Sender; import dev.onvoid.webrtc.client.Signaling; import dev.onvoid.webrtc.client.SignalingPair; +import dev.onvoid.webrtc.client.Subscription; import dev.onvoid.webrtc.client.WebRtc; import dev.onvoid.webrtc.client.internal.EncodedFrames; import dev.onvoid.webrtc.media.player.MediaInfo; @@ -124,6 +125,16 @@ void startCall() throws Exception { remoteVideo.get(15, TimeUnit.SECONDS); remoteAudio.get(15, TimeUnit.SECONDS); + // A track that is signaled need not carry video yet: on a slow machine + // the receiver may still wait for its first key frame, and a recorder + // started then waits for one too, longer than it holds the file back + // for it. The call is up once the remote video decodes. + CountDownLatch decoded = new CountDownLatch(15); + + try (Subscription frames = remoteVideo.join().asVideo().onFrame(frame -> decoded.countDown())) { + assertTrue(decoded.await(15, TimeUnit.SECONDS), "no remote video decoded"); + } + // Well into the call, so that the recorder has to ask for a key frame. Thread.sleep(1500); } @@ -141,14 +152,14 @@ void endCall() throws Exception { void recordsRemoteTracks() throws Exception { Path file = tempDir.resolve("received.mkv"); - record(file, recorder -> { + List warnings = record(file, recorder -> { recorder.addTrack(remoteVideo.join()); recorder.addTrack(remoteAudio.join()); }); MediaInfo info = readInfo(file); - assertTrue(info.hasVideo()); + assertTrue(info.hasVideo(), () -> "No video, warnings: " + warnings); assertEquals(WIDTH, info.getVideoWidth()); assertEquals(HEIGHT, info.getVideoHeight()); assertEquals("opus", info.getAudioCodec()); @@ -242,9 +253,14 @@ private interface Tracks { } - private static void record(Path file, Tracks tracks) throws Exception { + /** + * Records the given tracks into the file, and returns what the recorder + * warned of, such as a track it had to leave out. + */ + private static List record(Path file, Tracks tracks) throws Exception { CountDownLatch started = new CountDownLatch(1); AtomicReference error = new AtomicReference<>(); + List warnings = new CopyOnWriteArrayList<>(); try (MediaRecorder recorder = new MediaRecorder(file)) { recorder.setListener(new MediaRecorderListener() { @@ -254,6 +270,11 @@ public void onStarted() { started.countDown(); } + @Override + public void onWarning(String message) { + warnings.add(message); + } + @Override public void onError(String message) { error.set(message); @@ -269,6 +290,8 @@ public void onError(String message) { } assertNull(error.get()); + + return warnings; } private static MediaInfo readInfo(Path file) throws Exception {