Receives RFC 9828 RTP packets on a UDP socket and fans them out to every connected WebTransport client. No HTJ2K parsing — the browser viewer's WASM decoder owns that.
Each connected viewer gets one server-initiated unidirectional WebTransport
stream. Packets are written length-prefixed: [u16BE len][packet bytes]….
Streams (not datagrams) because Chromium's negotiated WebTransport datagram
size caps at ~1170 B, below typical RFC 9828 packet sizes (~1400 B). On LAN
the head-of-line cost vs datagrams is negligible.
- Go ≥ 1.22 — pinned by
go.mod; older toolchains will refuse to build. - No
cgo, no architecture-specific code — a Go toolchain is the only build-time requirement. - For the end-to-end LAN viewer stack (browser, static server, dev cert),
see
docs/wt_viewer.md.
go build -o wt_bridge .
./wt_bridge --listen-udp 0.0.0.0:6000 --listen-quic 0.0.0.0:4433 --dev--dev generates an ephemeral self-signed ECDSA P-256 certificate (13-day
validity, key usage = digitalSignature, EKU = serverAuth) and prints its
SHA-256 hash to stderr:
[wt_bridge] dev cert SHA-256:
[wt_bridge] ab:cd:…
[wt_bridge] viewer URL hint: ?certHash=ab:cd:…
Paste that hash into the viewer's ?certHash=… URL parameter — Chromium's
serverCertificateHashes API trusts it for that one connection without a
public CA. Cert constraints come from the WebTransport spec: ECDSA-P256,
validity strictly less than two weeks.
Producer side: point any RFC 9828 sender at the bridge's UDP port. Two examples:
# 1. The rpicam-apps fork on a Raspberry Pi:
rpicam-vid --rtp-host <bridge-ip> --rtp-port 6000 …
# 2. Replay a captured .rtp fixture (no live producer needed):
node scripts/udp_replay.mjs <fixture.rtp> --port 6000 --fps 30 --loopAny other RFC 9828 sender works the same way — the bridge is payload-agnostic.
wt_bridge [flags]
--listen-udp <addr> UDP bind for incoming RTP [0.0.0.0:6000]
--listen-quic <addr> QUIC bind for outgoing WebTransport [0.0.0.0:4433]
--max-clients <N> Max concurrent WT sessions [8]
--queue-depth <N> Per-session packet queue depth [8192]
Drop-oldest on overrun.
--cert <path> PEM cert chain (use instead of --dev for a CA cert)
--key <path> PEM private key matching --cert
--dev Ephemeral self-signed cert + hash printout
- UDP in: arbitrary RFC 9828 packets, one packet per UDP datagram. The bridge does not validate the payload.
- WebTransport out, per session: one server-initiated unidirectional stream.
Repeated framing of
[len:u16BE][bytes×len]. Reading client must parse this framing.
go build -o wt_bridge . # native (linux/amd64)
GOOS=linux GOARCH=arm64 go build -o wt_bridge_arm64 . # cross to Pi etc.No cgo, no architecture-specific code. Single static binary; ~10 MB stripped.
- Bump
net.core.rmem_maxto ~8 MiB on the bridge host soSO_RCVBUFisn't kernel-clamped (quic-go logs this if it can't apply the requested size). The relay is intended to share a host with the producer or sit on a small LAN node — host-level tuning is fine. - The bridge listens on QUIC + HTTP/3; CORS is wide open in dev mode.
Tighten
CheckOriginand use a real CA-issued cert for any non-LAN deployment.