laravel-webrtc/rust
Blax Software e675531bb0 test(sfu): in-process media-forwarding integration test (rust/tests/forwarding.rs)
Closes the coverage gap the README flagged as "Next": two real str0m clients
connect to the sidecar over loopback UDP (full ICE/DTLS/SRTP + the data-channel
renegotiation the browser does), peer A sends Opus RTP, and we assert peer B
RECEIVES the forwarded media. Pure `cargo test`, ~0.8s, no browser — proves the
SFU data plane (accept offer → forward without decode) end to end.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 10:53:50 +02:00
..
dist feat(sfu): ship the prebuilt sidecar binary in the package (true plug-and-play) 2026-07-22 10:08:14 +02:00
src feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
tests test(sfu): in-process media-forwarding integration test (rust/tests/forwarding.rs) 2026-07-22 10:53:50 +02:00
Cargo.lock feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
Cargo.toml feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
README.md feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00
build-release.sh feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary 2026-07-10 11:29:26 +02:00

README.md

blax-webrtc-sfu — the Rust data plane

The sidecar binary behind Blax\WebRtc\Media\RustMediaEngine: backend-hosted rooms. PHP/Laravel owns the control plane (rooms, auth, signaling orchestration, billing hooks); this process owns everything per-packet.

  • ICE (Interactive Connectivity Establishment — finds a working network path to each browser), DTLS (the UDP TLS handshake that negotiates keys) and SRTP (the encrypted media packets) are terminated here via str0m, a sans-IO Rust WebRTC stack.
  • SFU forwarding (Selective Forwarding Unit): each peer's Opus audio is received once and forwarded to the other peers of its room without decoding — one loop thread, one shared UDP socket, rtc.accepts() demultiplexing.
  • Recording: per-peer Ogg/Opus written straight from the depayloaded stream — playable by ffmpeg/browsers, no transcoding, no client cooperation.
  • Negotiation: the initial offer/answer arrives via the control socket (from PHP signaling). Renegotiation as peers join rides each browser's WebRTC data channel directly — PHP never relays it (see str0m's chat example, which this follows).

Control protocol (PHP ⇄ sidecar)

JSON-lines over a Unix socket. Requests carry an id echoed by the reply; notices are pushed without one.

→ {"id":1,"cmd":"add_peer","room":"lobby","peer":"alice","offer":"v=0…","record":"/rec/alice.ogg"}
← {"id":1,"ok":true,"answer":"v=0…"}
→ {"id":2,"cmd":"mute_peer","room":"lobby","peer":"alice","muted":true}
→ {"id":3,"cmd":"record_start","room":"lobby","peer":"alice","path":"/rec/a.ogg"}   (record_stop too)
→ {"id":4,"cmd":"remove_peer","room":"lobby","peer":"alice"}
→ {"id":5,"cmd":"stats"}      ← rooms → peers → {connected, muted, seconds}
→ {"id":6,"cmd":"ping"} / {"id":7,"cmd":"shutdown"}
← {"event":"peer_connected","room":"lobby","peer":"alice"}
← {"event":"peer_left","room":"lobby","peer":"alice","seconds":42.5}

Build & test

cargo build && cargo test          # 7 tests incl. an end-to-end control-socket run
./build-release.sh                 # static musl release + .sha256 into dist/

On NixOS: nix-shell -p gcc pkg-config openssl perl gnumake musl --run ./build-release.sh.

Releasing

BinaryManager::VERSION (PHP) pins the release tag it downloads from. To ship: build dist/ artifacts (linux-x86_64 at minimum), attach both files to the Forgejo release with that tag, and bump VERSION when the protocol changes. PHP resolves binaries in this order: webrtc.sfu.binary override → a local target/{release,debug} build (dev) → the downloaded install.

CLI

blax-webrtc-sfu --socket /run/sfu.sock --udp-ip 0.0.0.0 --udp-port 41000 --public-ip 203.0.113.9

--udp-port 0 (default) picks an ephemeral port — fine on one host, pin it in production and publish it (UDP) on the container. --public-ip is what browsers are told to reach in the ICE host candidate; it defaults to the default-route interface address.