# Details

> Broadcast live video to any number of viewers and serve it on demand — WHIP/RTMP/SRT/TUS2 ingest, CMAF, signed playback.

<img src="/images/diagrams/streams.png" alt="streams data flow" />

Broadcast live video to any number of viewers, and serve the same content on
demand. Push once from any encoder or the browser. The stream fans out to
thousands of viewers with sub-second latency. Browsers play it natively over
WebTransport. Native SDKs subscribe to the same stream. To let a person
watch, give them a short-lived signed playback URL. That URL *is* the auth,
and the relay enforces it for you.

You work with two surfaces:

- `Streams` manages your live inputs, signing keys, API keys, assets, and
  analytics over plain HTTPS. It is stateless request/response.
- `BroadcastPublisher` + `BroadcastViewer` move CMAF segments over a persistent
  MoQT (Media over QUIC Transport) connection.

  - **Live broadcast** — Push once and fan out to thousands. Glass-to-glass is sub-second on a
    warm path.
  - **VOD** — Resumable uploads via TUS2, packaged to CMAF, and played from the same
    catalog that the live path uses.
  - **Four ingest protocols** — RTMP, SRT, WHIP (WebRTC-HTTP ingest), and TUS2. Pick the protocol that
    your encoder or browser already uses.
  - **Signed playback** — Mint a JWT-bearing playback URL. The relay checks it inside the subscribe
    handler. The URL *is* the auth.

## When to use it

Use streams when one producer feeds many consumers and you want adaptive,
buffered media rather than a tight interactive loop:

- Live events, sports, town halls, product launches, IRL streaming.
- Low-latency "watch parties" where every viewer needs the same timeline.
- VOD libraries built from the same packaging pipeline as your live streams.
- Embedded playback in a browser without a separate plugin. WebTransport is
  the playback path.

If you need a bidirectional, per-participant audio loop (a call), use
[Voice](/modalities/voice/overview). If you need typed pub/sub of small
messages, use [Data](/modalities/data/details).

## Ingest

The publish side accepts four ingest protocols. Each live input declares its
`ingest` kind (`rtmp`, `srt`, `whip`, or `fmp4`). VOD uses TUS2.

  
    Classic encoder ingest (OBS, hardware encoders, mobile broadcasters).
    Point the encoder at the RTMP endpoint and authenticate with the
    per-input stream key. The engine ingests, transcodes, and CMAF-packages
    on the fly.
  
  
    Secure Reliable Transport for lossy/long-haul contribution links. It uses
    the same stream-key auth. Prefer SRT when the contribution path crosses
    the public internet and RTMP-over-TCP head-of-line blocking hurts.
  
  
    WebRTC-HTTP Ingestion Protocol: the browser-native, sub-second
    contribution path. A page captures camera/screen and WHIP-publishes with
    a single HTTP POST of the SDP offer. The engine answers and pulls the
    WebRTC media. It transcodes the media into the same CMAF pipeline. This
    path is good for "go live from the browser" without an external encoder.
  
  
    Resumable upload (the TUS2 protocol) for VOD. Large files upload in
    chunks and survive network drops. A paused upload resumes from the last
    confirmed offset. When the upload completes, the engine packages the file
    to CMAF. It then publishes an asset with a playback id that you can
    stream immediately.
  

> **NOTE:**
> The browser `BroadcastPublisher` in the SDK is a WHIP-style alternative. It
> fragments a `MediaStream` to CMAF in the page and sends each fragment as a
> MoQT object. For server-class contribution, prefer RTMP/SRT/WHIP into the
> engine. Let the transcoder do the packaging.

## Wire model

The SDK connects over one of two **URL paths**, both of which resolve to the
same MoQT **namespace** on the relay:

```
URL path (client → relay):
  /publish/<input_id>?sk=<stream_key>   broadcaster ingest session
  /playback/<input_id>?tok=<jwt>        viewer session (carries a playback token)

MoQT namespace (what the relay fans out, live + VOD uniform):
  stream/<org>/<input_id>               live  (input_id = lv_…)
  stream/<org>/<asset_id>               VOD   (asset_id  = as_…, playback id pb_…)

Tracks within the namespace:
  .catalog        in-band catalog track (draft-ietf-moq-catalog)
  <rendition>     one media track per ABR rendition (e.g. video/720p, audio)
```

`/publish/…` and `/playback/…` are URL paths, not namespaces. The relay's
stream resolver maps them to `stream/<org>/<id>`. VOD reuses the same
`stream/<org>/…` namespace, so live and on-demand playback are uniform.

The broadcast capability tag is `media.broadcast`. The relay writes a
per-broadcast analytics row keyed on that tag.

### The catalog

Every stream carries a **catalog** (a subset of `draft-ietf-moq-catalog`)
that declares its tracks: name, RFC 6381 codec string, and (for VOD) the
CMAF init segment. A **live** input publishes the catalog *in-band* on the
`.catalog` track. Thus the catalog can update mid-stream (a rendition added,
a codec switched). **VOD** ships the catalog as `catalog.json` over HTTPS.
The viewer parses the catalog. It opens a `MediaSource` buffer per track and
feeds CMAF segments into it.

```jsonc
{
  "version": 1,
  "live": true,
  "tracks": [
    { "name": "video/720p", "kind": "video", "codec": "avc1.42E01F",
      "width": 1280, "height": 720, "bitrate": 2500000 },
    { "name": "audio",      "kind": "audio", "codec": "opus",
      "sampleRate": 48000, "channels": 2 }
  ]
}
```

## Lanes & QoS

Streams use MoQT's group/object model. Each CMAF fragment is one MoQT
object. An init/keyframe fragment opens a new **group**, so a late joiner
can start at a group boundary. Objects carry a **priority**. The init
segment is highest priority, because it must arrive before media decodes.
Media segments follow. Under loss, the relay drops by priority and group
age. It does not block the whole stream. This is buffered, adaptive
delivery, not a frame-perfect interactive lane.

| Property | Streams behavior |
| -------- | ---------------- |
| Delivery | Per-group, priority-ordered objects over MoQT |
| Late join | Starts at the most recent group boundary (keyframe) |
| ABR | One media track per rendition; the catalog enumerates them |
| Fallback | LL-HLS over HTTPS where WebTransport/MoQT is unavailable |

## Codecs & packaging

- **Video:** H.264/AVC (`avc1.*`) on the broad-compatibility path. The
  catalog declares the codec string per track.
- **Audio:** Opus or AAC (`mp4a.40.2`).
- **Container:** CMAF / fragmented MP4 (fMP4): `ftyp+moov` init segment,
  then `moof+mdat` media fragments. The viewer feeds them directly into the
  browser's `MediaSource`.
- **Fallback:** **LL-HLS** (Low-Latency HLS) over HTTPS for environments
  where WebTransport is not available. The CMAF segments are the same bytes.
  Only the delivery framing changes.

## Signed playback (JWT)

A **short-lived JWT** authorizes playback. The JWT is minted from one of the
org's **signing keys** (Ed25519 by default, RS256 optional). The control
plane returns a playback URL with the token already attached as `?tok=`. The
relay checks the JWT inside the SUBSCRIBE handler. A bad or expired token
closes the session with `auth_failed`. The server clamps tokens (30 s – 24 h,
default 1 h). Re-mint and re-open before expiry to keep long viewers alive.

> **WARNING:**
> The API returns the cleartext **stream key** for a live input **once**, at
> `create()` / `rotateStreamKey()`. The control plane stores only a hash and
> will never return the key again. Capture and store the key the moment the
> API returns it.

## How it works

A million-viewer fan-out costs you, the publisher, exactly one upload. It
does not cost one copy per viewer. The relay mesh does the fan-out. One QUIC
connection multiplexes every track. The relay replicates each group out to
subscribers at the edge.

```
encoder/browser          engine (shard-per-core)              edge relays
─────────────            ─────────────────────────            ───────────
RTMP / SRT  ─┐           ┌─ ingest + transcode ─┐             ┌─ POP A ─┐
WHIP        ─┼─ ingest ─▶│  CMAF / fMP4 packager │─ MoQT ────▶│  fan-out │─▶ viewers
TUS2 (VOD)  ─┘           │  in-band catalog      │  (QUIC)    └─ POP B ─┘   (browser /
                         └───────────────────────┘                          native SDK)
```

Latency stays low because the data plane skips per-hop copies. It runs on a
**shard-per-core** reactor with a **kernel-bypass** NIC fast path,
**io_uring** for async I/O, and **lock-free mcache / dcache rings** that move
packets between cores without contention.

## Related

- [Realtime Tracks](/concepts/realtime-tracks) — the MoQT publish/subscribe
  primitive underneath every modality.
- [Streams — SDK Methods](/modalities/streams/sdk-methods)
- [Streams — Cookbook](/modalities/streams/cookbook)
- [Streams — Recipes](/modalities/streams/recipes)
