Skip to content

Commit 5ec6cac

Browse files
committed
Port the web example to the client API
The Java client is a peer of the client API, with SignalingManager as its Signaling over the example's own message format. A new peer calls the browser whenever it joins. WebSocket sends are queued, since a WebSocket takes one at a time, and the browser reuses its connection when the Java side renegotiates.
1 parent 02c1713 commit 5ec6cac

11 files changed

Lines changed: 368 additions & 1144 deletions

File tree

‎docs/guide/examples.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,15 @@ mvn exec:java -D"exec.mainClass=dev.onvoid.webrtc.examples.EncryptedRecordingExa
136136

137137
## Web Client
138138

139-
The [`WebClientExample`](https://github.com/devopvoid/webrtc-java/blob/master/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/web/WebClientExample.java) demonstrates how to combine WebSocket signaling with WebRTC peer connections for real-time communication between web and Java clients.
139+
The [`WebClientExample`](https://github.com/devopvoid/webrtc-java/blob/master/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/web/WebClientExample.java) calls a browser from Java over WebSocket signaling, with the [Client API](/guide/client-api). The browser client is served by the example's `WebServer`.
140140

141141
**Key features demonstrated:**
142142
- Connecting to a signaling server using WebSockets
143-
- Setting up audio and video media tracks
144-
- Creating and managing WebRTC peer connections
145-
- Handling signaling messages (offers, answers, ICE candidates)
146-
- Joining and leaving rooms for multi-user communication
147-
- Using custom audio sources for audio streaming
143+
- Implementing a `Signaling` over the application's own message format, which also names the sender and receiver of each message
144+
- Sending generated audio and video, pushed into tracks of the runtime
145+
- Letting the peer negotiate by itself, and starting a new peer whenever the browser joins the room
146+
- Joining and leaving rooms
147+
- Logging the audio and video the browser sends
148148

149149
This example is valuable for applications that need to establish WebRTC connections between Java clients and web browsers, enabling cross-platform real-time communication with audio and video.
150150

‎webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/util/MediaFrameLogger.java‎

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,98 +16,100 @@
1616

1717
package dev.onvoid.webrtc.examples.util;
1818

19-
import dev.onvoid.webrtc.media.audio.AudioTrackSink;
19+
import java.util.function.Consumer;
20+
21+
import dev.onvoid.webrtc.client.AudioChunk;
2022
import dev.onvoid.webrtc.media.video.VideoFrame;
21-
import dev.onvoid.webrtc.media.video.VideoTrackSink;
2223

2324
import org.slf4j.Logger;
2425
import org.slf4j.LoggerFactory;
2526

2627
/**
27-
* Utility class for logging audio and video frame information.
28+
* Utility class for logging audio and video frame information, as handlers of
29+
* the client API's {@code RemoteAudio.onAudio} and {@code RemoteVideo.onFrame}.
30+
* Each logger writes a summary once a second.
2831
*
2932
* @author Alex Andres
3033
*/
3134
public class MediaFrameLogger {
3235

3336
private static final Logger LOG = LoggerFactory.getLogger(MediaFrameLogger.class);
3437

38+
private static final long LOG_INTERVAL_MS = 1000;
39+
3540

3641
/**
37-
* Creates a new audio track sink that logs information about received audio data.
42+
* Creates a handler that logs information about received audio.
3843
*
39-
* @return An AudioTrackSink that logs audio frame information.
44+
* @return A handler for {@code RemoteAudio.onAudio}.
4045
*/
41-
public static AudioTrackSink createAudioLogger() {
46+
public static Consumer<AudioChunk> createAudioLogger() {
4247
return new AudioFrameLogger();
4348
}
4449

4550
/**
46-
* Creates a new video track sink that logs information about received video frames.
51+
* Creates a handler that logs information about received video frames.
4752
*
48-
* @return A VideoTrackSink that logs video frame information.
53+
* @return A handler for {@code RemoteVideo.onFrame}.
4954
*/
50-
public static VideoTrackSink createVideoLogger() {
55+
public static Consumer<VideoFrame> createVideoLogger() {
5156
return new VideoFrameLogger();
5257
}
5358

5459

5560

5661
/**
57-
* A simple implementation of AudioTrackSink that logs information about received audio data.
62+
* Logs the number of audio chunks received per second and the format of the last one.
63+
* The client API calls it on one thread, in order.
5864
*/
59-
private static class AudioFrameLogger implements AudioTrackSink {
60-
61-
private static final long LOG_INTERVAL_MS = 1000; // Log every second
62-
private int frameCount = 0;
65+
private static class AudioFrameLogger implements Consumer<AudioChunk> {
66+
67+
private int chunkCount = 0;
68+
6369
private long lastLogTime = System.currentTimeMillis();
64-
70+
71+
6572
@Override
66-
public void onData(byte[] data, int bitsPerSample, int sampleRate, int channels, int frames) {
67-
frameCount++;
68-
73+
public void accept(AudioChunk chunk) {
74+
chunkCount++;
75+
6976
long now = System.currentTimeMillis();
77+
7078
if (now - lastLogTime >= LOG_INTERVAL_MS) {
71-
LOG.info(String.format("Received %d audio frames in the last %.1f seconds",
72-
frameCount, (now - lastLogTime) / 1000.0));
73-
LOG.info(String.format("Last audio data: %d bytes, %d bits/sample, %d Hz, %d channels, %d frames",
74-
data.length, bitsPerSample, sampleRate, channels, frames));
75-
76-
frameCount = 0;
79+
LOG.info(String.format("Received %d audio chunks in the last %.1f seconds, the last one %s",
80+
chunkCount, (now - lastLogTime) / 1000.0, chunk));
81+
82+
chunkCount = 0;
7783
lastLogTime = now;
7884
}
7985
}
8086
}
8187

82-
83-
8488
/**
85-
* A simple implementation of VideoTrackSink that logs information about received frames.
89+
* Logs the number of video frames received per second and the size of the last one.
90+
* The frame is released by the client API once the handler returned.
8691
*/
87-
private static class VideoFrameLogger implements VideoTrackSink {
88-
89-
private static final long LOG_INTERVAL_MS = 1000; // Log every second
92+
private static class VideoFrameLogger implements Consumer<VideoFrame> {
93+
9094
private int frameCount = 0;
95+
9196
private long lastLogTime = System.currentTimeMillis();
92-
97+
98+
9399
@Override
94-
public void onVideoFrame(VideoFrame frame) {
100+
public void accept(VideoFrame frame) {
95101
frameCount++;
96-
102+
97103
long now = System.currentTimeMillis();
104+
98105
if (now - lastLogTime >= LOG_INTERVAL_MS) {
99-
LOG.info(String.format("Received %d video frames in the last %.1f seconds",
100-
frameCount, (now - lastLogTime) / 1000.0));
101-
LOG.info(String.format("Last frame: %dx%d, rotation: %d, timestamp: %dms",
102-
frame.buffer.getWidth(), frame.buffer.getHeight(), frame.rotation,
103-
frame.timestampNs / 1000000));
104-
106+
LOG.info(String.format("Received %d video frames in the last %.1f seconds, the last one %dx%d, rotation %d",
107+
frameCount, (now - lastLogTime) / 1000.0, frame.buffer.getWidth(),
108+
frame.buffer.getHeight(), frame.rotation));
109+
105110
frameCount = 0;
106111
lastLogTime = now;
107112
}
108-
109-
// Release the native resources associated with this frame to prevent memory leaks.
110-
frame.release();
111113
}
112114
}
113-
}
115+
}

0 commit comments

Comments
 (0)