Media over QUIC¶
media.play.moq
Sapsan delivers live media over Media over QUIC: a client connects to the server, selects a stream, and subscribes to its tracks. Sapsan acts as the media publisher. The input source can use a different protocol, such as RTSP.
The current implementation uses MoQT transport draft-19. This is a specific Internet-Draft version; client compatibility must be checked against that version. The catalog follows MSF draft-01, and media is carried in CMAF.
How delivery works¶
For each active stream, Sapsan publishes a broadcast whose namespace equals the stream name. For example, cam becomes the name the client selects when subscribing.
- Discovery — subscribing to an empty namespace prefix provides the list of published streams.
- Catalog — the
catalogtrack contains JSON describing the media and its initialization data. - Tracks — video and audio are published under names such as
v1anda1. - Objects — CMAF parts are sent as they become available. A segment boundary starts a new group, and the parts within it become consecutive objects.
A client joining in the middle of a segment waits for a suitable group start. An established subscription receives subsequent objects as they are produced. Overall latency depends on the source, encoding, segment and part structure, network, and player buffer.
Two connection options share one UDP listener:
| Client | Transport |
|---|---|
| Native MoQT client | QUIC with ALPN moqt-19. |
| Browser | WebTransport over HTTP/3, ALPN h3; the client uses a compatible MoQT draft-19 variant. |
This path provides live video and audio delivery. It does not provide DVR/VOD playback, MoQ camera ingest, or subtitle delivery. A MoQ subscription does not replace source or transcoder configuration.
Listener setup¶
Set listeners.moq in the streamer settings. In a deployed system, configuration is managed through central. For a local check, use the following server configuration fragment:
listeners:
http:
- port: 8080
moq:
- bind: "127.0.0.1:4443"
certificate: /etc/sapsan/tls/moq-cert.pem
private_key: /etc/sapsan/tls/moq-key.pem
streams:
- name: cam
inputs:
- rtsp:
url: rtsp://camera.example/live
Replace the source and the certificate and key paths. bind is a numeric IP address and UDP port; port 4443 is chosen for this example. Allowing only TCP on that port does not enable QUIC connections.
For the first browser check, use a source with H.264 and AAC supported by the chosen browser. The catalog describes existing tracks: MoQ alone does not convert a codec unsupported by the browser into a compatible one. Configure transcoding in advance if needed.
Listener access¶
The current MoQ listener provides access to the server's published live streams without integration with HTTP authorization or playback sessions. JWT ticket validation or HLS ?token= handling does not carry over to MoQ. TLS encrypts the transport but does not determine who may subscribe to a particular stream.
When choosing the listener address, consider which clients can reach it. In this example, it is accessible only on the local machine. For a remote test setup, restrict network access to the UDP port to the test participants.
Verifying media reception¶
The Sapsan source tree includes the moq-probe utility. It establishes a MoQT connection, discovers the stream, and reads the catalog. With a track parameter, it also verifies receipt of a media object.
After starting Sapsan with an active cam stream and a v1 video track, run the following from the source tree root:
MOQ_PROBE_SERVER=127.0.0.1:4443 \
MOQ_PROBE_BROADCAST=cam \
MOQ_PROBE_TRACK=v1 \
cargo run -p moq --bin moq-probe
Building the utility requires the project's Rust environment. MOQ_PROBE_SERVER accepts an IP address and port without an https:// prefix; the broadcast and track names must match the running stream.
Expected result:
MOQT probe OK: SETUP + SUBSCRIBE_NAMESPACE + catalog + media object
The MOQ_PROBE_TRACK parameter matters: without it, success means the catalog was checked, but no video object was received. The utility checks CMAF object structure rather than decoding video and audio. This diagnostic utility also skips server certificate verification, so its success does not establish TLS trust for a regular client.
An ordinary HTTPS GET or opening the MoQ listener address as a web page does not subscribe to media and cannot replace this check.
Checking browser playback¶
The source tree includes a test setup using the stock @moq/watch player with a pinned compatible client library version. Run the following commands from the Sapsan repository root.
- Create a test certificate:
bash
crates/moq/tests/gen-browser-cert.sh
The script creates crates/moq/tests/wt-cert.pem and wt-key.pem. Set their absolute paths in your listener's certificate and private_key. This is a 14-day ECDSA certificate suitable for the browser test setup's certificate hash pinning.
- Start Sapsan with this configuration and a compatible source. For example, for a local source build:
bash
CONFIG_PATH=/absolute/path/to/moq.yaml \
cargo run --no-default-features --bin sapsan
This command is intended for local testing. The additional capabilities available in this launch depend on the build; choose an already working compatible source for the check.
- Start the browser component:
bash
crates/moq/tests/watch-sapsan/serve.sh
It requires bun, git, and openssl; on the first launch, the script fetches dependencies. It starts the client test setup, not Sapsan itself.
- Open
http://localhost:5173/sapsan.html?name=camin a WebTransport-capable browser. By default, the test setup connects tohttps://127.0.0.1:4443. Start playback and click to enable audio if the browser requires it.
Successful browser verification means video and audio play, not merely that the stream name appears in a list. localhost is intentional: WebTransport requires a secure context, and an ordinary HTTP page accessed by a LAN IP is not equivalent. The player's buffer indicator is also not a measurement of total camera-to-screen latency.