phrame-webrtc-session — one viewer, one stream
phrame-webrtc-session — one viewer, one stream
Section titled “phrame-webrtc-session — one viewer, one stream”Where output-processor fans one production out to many destinations, this
does the opposite: one process serves one browser, directly, with the shortest
path the technology allows.
- Serves: exactly one authenticated viewer, over WHEP
- Reads: a video and an audio stream from the Phrame bus
- Returns: the viewer’s microphone (talkback) into a Phrame bus audio stream
Why a second WebRTC path exists
Section titled “Why a second WebRTC path exists”The egress page describes output-processor publishing WebRTC over
WHIP. That is the right tool for distribution: encode once, hand it to something
that fans it out.
A monitor is a different problem. There is one viewer, watching one compositor, and what matters is how far behind the glass is — not how many people can watch. Sending that through a relay adds a hop, a buffer and a re-packetisation to a path whose entire purpose is to be short. The relay’s fan-out and muxing buy nothing when the audience is one.
So this service is a direct WHEP server: the browser connects to it, and there is one media hop between the compositor and the picture. It replaced an earlier stack — a C++ sender, Flask signalling, an MQTT SDP bus and a relay — that did the same job with four moving parts instead of one.
The isolation boundary
Section titled “The isolation boundary”One process, one stream, one viewer. That is a security boundary, not a performance tuning choice.
A second authorised offer arriving while a viewer is connected is refused with
409 Conflict, protecting the viewer already watching — unless it comes from
the same user, which is treated as a genuine reconnect replacing its own stale
session.
When a viewer leaves — an explicit WHEP DELETE, a dropped ICE connection, or
an unconnected offer whose token expired — the lock is released and the WebRTC
state is reset, but the process stays up, still bound to its long-lived
compositor. Reconnects are therefore warm rather than a cold start.
Authentication
Section titled “Authentication”The service verifies a bearer token before it will accept an SDP offer.
Tokens are signed asymmetrically, and the service holds only the public key — so it can verify a token and cannot mint one. A compromised media node cannot issue itself access to anything.
The token is minted by the layer that already knows who the user is, and it is deliberately narrow: short-lived, single-use, scoped to one audience and bound to the specific stream id this process serves. The claims are all checked — audience, expiry, not-before, subject, stream id, and a nonce cached for the token’s lifetime so a captured token cannot be replayed.
No token, or an invalid one, is a 401. A valid token for a different stream
is a 403.
The reasoning behind that shape is worth stating, because it is a trade: an opaque token checked against a central service on every connect would cost a round trip and a hard runtime dependency at exactly the moment a viewer is waiting. A token that lives seconds is effectively revocable anyway. Forwarding the session cookie instead would mean this media service had to understand the session store — a much bigger thing to hold, and a much bigger thing to leak.
Locking on quickly
Section titled “Locking on quickly”A viewer joining should see a picture immediately, and the honest obstacle is that a decoder cannot start on an arbitrary frame.
Three things address it. The GOP is short — about a second by default — so a natural key frame is never far away. When the browser asks for one, on connect or after loss, the encoder forces the next frame to be a key frame rather than waiting. And a low playout-delay hint is advertised so the browser is discouraged from buffering more than it needs.
The middle one does most of the work: it gives near-instant lock on connect without paying the bitrate cost of a permanently tiny GOP.
Talkback
Section titled “Talkback”The path runs both ways. The service accepts the browser’s microphone and writes it back into a Phrame bus audio stream, so a monitor is also a talkback position without a second system to configure.
Where it is used
Section titled “Where it is used”Any surface that needs one person watching one compositor with as little delay as possible: an operator preview, a confidence monitor, an editing monitor. Two instances can serve two monitors of the same production independently, each on its own port.
The service is keyed on a Phrame bus stream id rather than on anything compositor-specific, so adding a viewer to a new stream is configuration, not code.
