WebRTC Loopback
Two RTCPeerConnection instances in a single process exchange an SDP offer and answer, trickle ICE candidates at each other, open an RTCDataChannel, and echo a string and an ArrayBuffer back and forth. No signaling server and no remote peer.
The W3C WebRTC API works on GJS, backed by GStreamer’s webrtcbin. If your app needs peer-to-peer data or media, you write the code you would write for a browser.
Live demo
Section titled “Live demo”Press Restart to run the handshake again from scratch.
Install the GStreamer plugins first
Section titled “Install the GStreamer plugins first”WebRTC on GJS needs GStreamer 1.20 or newer with the WebRTC plugin and libnice:
# Fedorasudo dnf install gstreamer1-plugins-bad-free gstreamer1-plugins-bad-free-extras libnice-gstreamer1
# Ubuntu / Debiansudo apt install gstreamer1.0-plugins-bad gstreamer1.0-niceCheck that both are found:
gst-inspect-1.0 webrtcbingst-inspect-1.0 nicesrcgjsify system-check # reports GJS plus the optional GStreamer piecesRun it on GJS
Section titled “Run it on GJS”This is one of two showcases that declare ["gjs"] rather than all four runtimes. Its native side is a GStreamer pipeline reached through a typelib, so it ships no --app node bundle and --runtime node tells you so instead of crashing:
gjsify showcase webrtc-loopbackThis one is a console program rather than a window. It prints the handshake as it happens and exits when the round trip completes:
[A] createOffer[A] signalingState → have-local-offer[B] signalingState → have-remote-offer[B] createAnswer[B] signalingState → stable[A] iceConnectionState → checking[A] iceConnectionState → connected[B] ondatachannel "chat"[A] received: echo: hello from peer A[A] received ArrayBuffer(4): [4, 3, 2, 1]If it stops at “checking” and never reaches “connected”, one of the two GStreamer plugins above is missing.
Run it in the browser
Section titled “Run it in the browser”import { mount } from '@gjsify/example-dom-webrtc-loopback/browser';
const handle = mount(document.getElementById('app')!, { title: 'WebRTC Data-Channel Loopback',});
await handle.restart();mount() renders a heading, a log and a Restart button, then runs the same runLoopback(log) against the browser’s own WebRTC stack. Comparing the two logs line by line is how you check that the GJS side behaves the same.
What it uses
Section titled “What it uses”| Package | What it needs |
|---|---|
@gjsify/webrtc | RTCPeerConnection, createOffer, createAnswer, setLocalDescription, setRemoteDescription, addIceCandidate, and RTCDataChannel with string and ArrayBuffer payloads |
@gjsify/webrtc-native | The Vala bridge that moves webrtcbin’s callbacks off GStreamer’s streaming threads onto the GLib main context, so your handlers run on the main loop |
gst-1.0, gst-webrtc-1.0, libnice | DTLS, SCTP and ICE, the actual WebRTC implementation |
The shared demo module imports nothing from @gjsify/*. It reaches for RTCPeerConnection, RTCSessionDescription and RTCIceCandidate as globals, exactly as browser code does, and the build supplies them.
Copy the structure
Section titled “Copy the structure”showcases/dom/webrtc-loopback/
src/loopback-demo.tsis the whole handshake and echo, behind onerunLoopback(log)function.src/gjs/gjs.tsruns it on aGLib.MainLoopand prints to stdout.src/browser/browser.tsexportsmount();browser-main.tsruns it full-page.
The GJS entry is a useful template on its own: WebRTC needs no GTK window, only a running main loop.
Related
Section titled “Related”webrtc-videois the media half of the same stack, withgetUserMediaand a GTK video sink.- Web API packages lists what
@gjsify/webrtccovers and which GNOME libraries back it.