Get Started
This guide will help you get started with webrtc-java quickly. We'll cover installation, basic setup, and simple examples to demonstrate core functionality.
Supported Platforms
Maven Central artifacts contain native libraries that can be loaded on the following platforms:
| x64 | arm | arm64 | |
|---|---|---|---|
| Linux | ✔ | ✔ | ✔ |
| macOS | ✔ | - | ✔ |
| Windows | ✔ | - | ✔ |
INFO
Linux runtime dependencies
The Linux native library links the X11 client libraries and the GLib, GBM and libdrm libraries that libwebrtc's desktop capturers use. A minimal installation, for example a container image, needs them installed before the library can load. On Debian and Ubuntu:
apt-get install libx11-6 libxext6 libxfixes3 libxdamage1 libxtst6 libxrandr2 libxcomposite1 libglib2.0-0 libgbm1 libdrm2Since 0.17.0, PulseAudio, udev and D-Bus are opened at runtime only when audio devices, cameras or the screen saver inhibition are used, so the library loads without libpulse0, libudev1 and libdbus-1-3. Earlier versions link them and need those three packages as well.
Installation
Maven
Add the following dependency to your pom.xml:
<dependency>
<groupId>dev.onvoid.webrtc</groupId>
<artifactId>webrtc-java</artifactId>
<version>0.20.0</version>
</dependency>INFO
Native library classifiers
The webrtc-java artifact will automatically add the dependency for the native library artifact matching the currently running operating system and architecture at runtime.
If you encounter issues loading the native library "webrtc-java", you can declare the platform-specific classifier explicitly. For example, for Windows 64-Bit:
<dependency>
<groupId>dev.onvoid.webrtc</groupId>
<artifactId>webrtc-java</artifactId>
<version>0.20.0</version>
<classifier>windows-x86_64</classifier>
</dependency>Complete list of available classifiers:
- windows-x86_64
- windows-aarch64
- macos-x86_64
- macos-aarch64
- linux-x86_64
- linux-aarch64
- linux-aarch32
Using SNAPSHOT Versions
If you want to use the latest development version, you can use a SNAPSHOT release:
<dependency>
<groupId>dev.onvoid.webrtc</groupId>
<artifactId>webrtc-java</artifactId>
<version>0.21.0-SNAPSHOT</version>
</dependency>To use SNAPSHOT versions, you need to add the following repository configuration to your pom.xml:
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>Gradle
Add the following to your build.gradle:
implementation "dev.onvoid.webrtc:webrtc-java:0.20.0"For specific platforms, add the appropriate classifier:
implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "0.20.0", classifier: "windows-x86_64"
implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "0.20.0", classifier: "windows-aarch64"
implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "0.20.0", classifier: "macos-x86_64"
implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "0.20.0", classifier: "macos-aarch64"
implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "0.20.0", classifier: "linux-x86_64"
implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "0.20.0", classifier: "linux-aarch64"
implementation group: "dev.onvoid.webrtc", name: "webrtc-java", version: "0.20.0", classifier: "linux-aarch32"Basic Setup
Initialize the WebRTC Library
First, create a PeerConnectionFactory which is the entry point for most WebRTC operations:
import dev.onvoid.webrtc.PeerConnectionFactory;
// Create a peer connection factory
PeerConnectionFactory factory = new PeerConnectionFactory();Create a Peer Connection
To establish communication with another peer, you need to create a RTCPeerConnection:
import dev.onvoid.webrtc.RTCConfiguration;
import dev.onvoid.webrtc.RTCIceServer;
import dev.onvoid.webrtc.RTCPeerConnection;
import dev.onvoid.webrtc.PeerConnectionObserver;
// Configure ICE servers (STUN/TURN)
RTCConfiguration config = new RTCConfiguration();
RTCIceServer iceServer = new RTCIceServer();
iceServer.urls.add("stun:stun.l.google.com:19302");
config.iceServers.add(iceServer);
// Create a peer connection with an observer to handle events
RTCPeerConnection peerConnection = factory.createPeerConnection(config, new PeerConnectionObserver() {
// Implement required methods
});Signaling
WebRTC requires a signaling mechanism to exchange connection information between peers. The library doesn't provide this, so you'll need to implement it using your preferred method (WebSockets, HTTP, etc.).
Here's a simplified example of the signaling process:
// Create an offer
RTCOfferOptions options = new RTCOfferOptions();
peerConnection.createOffer(options, new CreateSessionDescriptionObserver() {
@Override
public void onSuccess(RTCSessionDescription description) {
// Set local description
peerConnection.setLocalDescription(description, new SetSessionDescriptionObserver() {
@Override
public void onSuccess() {
// Send the offer to the remote peer via your signaling channel
signalingChannel.send(description);
}
@Override
public void onFailure(String error) {
System.err.println("Failed to set local description: " + error);
}
});
}
@Override
public void onFailure(String error) {
System.err.println("Failed to create offer: " + error);
}
});
// When you receive an answer from the remote peer via your signaling channel
signalingChannel.onMessage(message -> {
RTCSessionDescription remoteDescription = parseSessionDescription(message);
peerConnection.setRemoteDescription(remoteDescription, new SetSessionDescriptionObserver() {
@Override
public void onSuccess() {
System.out.println("Remote description set successfully");
}
@Override
public void onFailure(String error) {
System.err.println("Failed to set remote description: " + error);
}
});
});
// Handle ICE candidates
PeerConnectionObserver connectionObserver = new PeerConnectionObserver() {
@Override
public void onIceCandidate(RTCIceCandidate candidate) {
// Send the ICE candidate to the remote peer via your signaling channel
signalingChannel.send(candidate);
}
};
// When you receive an ICE candidate from the remote peer
signalingChannel.onIceCandidate(candidateMessage -> {
RTCIceCandidate candidate = parseIceCandidate(candidateMessage);
peerConnection.addIceCandidate(candidate);
});Letting the Peer Connection Create the Description
setLocalDescription can also be called with only an observer. It then creates the offer or the answer, whichever the signaling state calls for, and applies it in one step, so nothing can change in between. That makes it the simpler way to implement "perfect negotiation". Read the result with getLocalDescription() once the observer reports success:
peerConnection.setLocalDescription(new SetSessionDescriptionObserver() {
@Override
public void onSuccess() {
signalingChannel.send(peerConnection.getLocalDescription());
}
@Override
public void onFailure(String error) {
System.err.println("Failed to set local description: " + error);
}
});Adding Candidates and Knowing the Outcome
addIceCandidate(candidate) reports nothing and fails silently when the remote description is not set yet. The form that takes an AddIceCandidateObserver is queued behind any pending offer/answer operation, as the standard requires, so a candidate can be added right after setRemoteDescription() without waiting for it, and the observer says whether it was accepted:
peerConnection.addIceCandidate(candidate, new AddIceCandidateObserver() {
@Override
public void onSuccess() {
}
@Override
public void onFailure(String error) {
// e.g. "[INVALID_STATE] The remote description was null"
System.err.println("Candidate rejected: " + error);
}
});A local candidate can also go away, for example when its network interface does. The observer's onIceCandidateRemoved(RTCIceCandidate) reports it; send it to the remote peer, which passes it to removeIceCandidate(candidate).
Once ICE has picked the candidate pair it uses, onSelectedCandidatePairChanged(RTCCandidatePairChangeEvent) reports both candidates of the pair and why it was chosen. It fires again whenever ICE switches pairs:
@Override
public void onSelectedCandidatePairChanged(RTCCandidatePairChangeEvent event) {
System.out.println("Using " + event.getLocal().sdp + " <-> " + event.getRemote().sdp
+ " (" + event.getReason() + ")");
}Media Streams
Accessing Media Devices
To query media devices (cameras and microphones):
import dev.onvoid.webrtc.media.MediaDevices;
import dev.onvoid.webrtc.media.video.VideoDevice;
import dev.onvoid.webrtc.media.audio.AudioDevice;
// Get available video devices
List<VideoDevice> videoDevices = MediaDevices.getVideoCaptureDevices();
for (VideoDevice device : videoDevices) {
System.out.println("Video device: " + device.getName());
}
// Get available audio devices
List<AudioDevice> audioDevices = MediaDevices.getAudioCaptureDevices();
for (AudioDevice device : audioDevices) {
System.out.println("Audio device: " + device.getName());
}Creating Media Tracks
To create audio and video tracks:
import dev.onvoid.webrtc.media.video.VideoTrack;
import dev.onvoid.webrtc.media.audio.AudioTrack;
import dev.onvoid.webrtc.media.video.VideoDeviceSource;
import dev.onvoid.webrtc.media.audio.AudioDeviceSource;
// Create a video source and track
VideoDeviceSource videoSource = new VideoDeviceSource();
videoSource.setVideoCaptureDevice(videoDevices.get(0)); // Use the first available camera
VideoTrack videoTrack = factory.createVideoTrack("video0", videoSource);
// Create an audio source and track
AudioOptions audioOptions = new AudioOptions();
audioOptions.echoCancellation = true;
audioOptions.autoGainControl = true;
audioOptions.noiseSuppression = true;
// Create an audio source using the default audio device
AudioTrackSource audioSource = factory.createAudioSource(audioOptions);
AudioTrack audioTrack = factory.createAudioTrack("audio0", audioSource);INFO
Audio devices can be specified via the AudioDeviceModule, see the guide on Audio Devices for more details.
Adding Tracks to Peer Connection
import java.util.ArrayList;
import java.util.List;
import dev.onvoid.webrtc.RTCRtpSender;
// Add tracks to the peer connection
List<String> streamIds = new ArrayList<>();
streamIds.add("stream1");
RTCRtpSender videoSender = peerConnection.addTrack(videoTrack, streamIds);
RTCRtpSender audioSender = peerConnection.addTrack(audioTrack, streamIds);Receiving Tracks
A remote track arrives in onTrack with the transceiver that carries it. To learn when its media actually starts flowing, for example to show a video only once there is something to show, set an RTCRtpReceiverObserver; an RTCRtpSenderObserver does the same for the first packet a sender sends. Either is called right away when that has already happened:
@Override
public void onTrack(RTCRtpTransceiver transceiver) {
RTCRtpReceiver receiver = transceiver.getReceiver();
receiver.setObserver(mediaType -> System.out.println("First " + mediaType + " packet received"));
MediaStreamTrack track = receiver.getTrack();
if (track instanceof AudioTrack) {
// Playout volume of this remote track, 0 to 10; 1 plays it as received.
((AudioTrack) track).setVolume(0.5);
}
}An observer belongs to the RTCRtpReceiver or RTCRtpSender object it was set through, and is removed when that object is disposed. getId() identifies a sender or receiver, and RTCRtpSender.getStreams() returns the stream IDs its track was added with.
Cleanup
Always properly dispose of resources when you're done:
// Dispose of senders (not owned by the peer connection)
videoSender.dispose();
audioSender.dispose();
// Dispose of tracks
videoTrack.dispose();
audioTrack.dispose();
// Dispose of sources
videoSource.dispose();
audioSource.dispose();
// Close peer connection and release resources
peerConnection.close();
// Dispose of factory
factory.dispose();Next Steps
Now that you have a basic understanding of webrtc-java, you can explore more advanced features:
- Data Channels - Learn more about data channels
- RTC Stats - Monitor connection quality
- Desktop Capture - Share screens and windows
- All Guides - Complete list of guides
For a complete API reference, check the JavaDoc.
