multimux — multi-input, multi-output just-in-time repackaging origin
multimux is a hub, not a single pipe. It pulls/receives live media from any of several ingest transports, and serves each ingested stream as any combination of low-latency delivery protocols, from one in-process tokio + axum HTTP origin. Muxing only: samples are opaque and are never transcoded. Every route (one ingest → its served outputs) is independent — a single instance can serve dozens of unrelated cameras/feeds side by side.
RTSP ─┐ ┌─▶ LL-HLS (media.m3u8 + parts)
RTP ─┤ │
TS/UDP─┼─▶ ingest ─▶ transmux (depay/segment) ──┼─▶ DASH (manifest.mpd)
TS/HTTP┤ one route = │
HLS-pull┘ one ingest, N outputs └─▶ LL-DASH (manifest-ll.mpd)
Inputs
Each route names one ingest transport (InputSpec):
type |
Transport | Notes |
|---|---|---|
rtsp |
RTSP pull (DESCRIBE/SETUP/PLAY, interleaved TCP), via rtsp-runtime |
optional auth |
rtp |
Raw RTP over UDP (uni/multicast) | needs an out-of-band SDP (inline or @path) for codec/fmtp |
ts_udp |
MPEG-2 TS over UDP (uni/multicast) | track set comes from the in-band PMT — no SDP needed |
ts_http |
MPEG-2 TS over a streaming HTTP GET (chunked/progressive) | optional auth |
hls_pull |
Pull a remote (LL-)HLS Media Playlist, via ll-hls-runtime's client |
optional auth |
srt |
MPEG-2 TS over SRT, caller (dial out) or listener (bind + accept), via srt-runtime |
track set from the in-band PMT; payload encryption out of scope |
dash_pull |
Pull a remote DASH MPD and its segments | optional auth |
smooth_pull |
Pull a remote Smooth Streaming manifest and its fragments | optional auth |
rtmp |
Push ingest: binds a listen port and accepts RTMP publishers (FLV), via rtmp-runtime |
app/stream_key filters; concurrent publishers |
rtsp accepts rtsps:// for RTSP over TLS. rtmp is the only push input —
nothing is dialled out to; it binds and accepts, and one stalled publisher does
not block another.
rtsp/ts_http/hls_pull/dash_pull/smooth_pull each accept an optional auth — either
{ "username": "...", "password": "..." } (answered as Basic or Digest,
whichever the upstream's own challenge asks for) or { "bearer_token": "..." } (RFC 6750; the only way to supply a bearer token, since it has no
URL-userinfo form). A username/password may instead ride the route's own
URL userinfo (rtsp://user:pass@host/...); an explicit auth always wins
over that.
Codecs: H.264 video + AAC audio (whatever transmux's depayload/demux
supports — any missing codec/transport capability is a library gap fixed
upstream, in transmux or rtsp-runtime, never in this crate).
Outputs
Each route selects which delivery protocol(s) to serve its ingested media
as (outputs, defaulting to ["llhls"] — every pre-existing config is
unaffected), all reading the exact same segmented CMAF — ingest-once,
many-outputs, no per-output re-mux:
outputs token |
Served as | Manifest |
|---|---|---|
"llhls" |
Low-Latency HLS (RFC 8216bis) | master.m3u8 + media.m3u8 (or the configured playlist_name) |
"dash" |
MPEG-DASH, $Number$-addressed |
manifest.mpd |
"ll_dash" |
Low-latency DASH, true chunked-transfer CMAF (whole-segment $Number$, served over HTTP chunked transfer while in progress) |
manifest-ll.mpd |
A route can enable more than one (e.g. ["llhls", "dash"]), and different
routes may enable different sets.
Served endpoints
One route ("stream") is served per configured name, under /{stream}/...:
| Endpoint | Description |
|---|---|
GET /{stream}/master.m3u8 |
LL-HLS master playlist (if llhls is enabled). |
GET /{stream}/media.m3u8[?_HLS_msn=&_HLS_part=] |
LL-HLS media playlist (or the configured playlist_name). Blocking Playlist Reload (RFC 8216bis §6.2.5.2) when _HLS_msn/_HLS_part are present. |
GET /{stream}/manifest.mpd |
DASH manifest (if dash is enabled). |
GET /{stream}/manifest-ll.mpd |
Low-latency DASH manifest (if ll_dash is enabled). |
GET /{stream}/init-{track}.mp4 |
fMP4 init segment (moov) — shared across every enabled output. |
GET /{stream}/seg-{track}-{seq}.m4s |
A full media segment: served whole (Content-Length) once closed, or streamed over HTTP chunked transfer-encoding while still in progress (issue #721 — ll_dash's low-latency delivery). |
GET /{stream}/part-{track}-{seq}.{part}.m4s |
An LL-HLS partial segment of the in-progress segment (also how ll_dash's chunked-transfer path internally sources the bytes it streams — never addressed directly by the LL-DASH MPD itself). |
GET /healthz |
Liveness — always 200. Never gated by output_auth. |
GET /readyz |
Readiness — 200 once at least one route is live, 503 otherwise. Never gated by output_auth. |
GET /metrics |
Prometheus metrics. Never gated by output_auth. |
An unknown stream name, or a filename multimux doesn't recognize, returns
404.
Shared output auth
One credential can gate every media output route (manifests and
init/segment/part bytes alike) across every configured route — e.g. 40
cameras under /camN/media.m3u8, one shared login — via Config::output_auth.
Independent of, and unrelated to, each route's own ingest auth. None
(the default) leaves every route open.
Schemes (scheme tag): "basic" / "digest" (username + password),
"bearer" (token), and "forwarded" — see below.
Reverse-proxy deployment (forwarded)
When multimux sits behind a reverse proxy that already terminates TLS and
authenticates the caller (its own login, mTLS, an SSO gateway, ...), the
forwarded scheme trusts the proxy's own X-Forwarded-User (configurable)
header instead of checking a credential itself — no second login,
no WWW-Authenticate round-trip a direct client could answer:
Safe ONLY when the origin is reachable exclusively through a reverse
proxy that strips any client-supplied copies of user_header (and
forwarded_for_header, if set) before forwarding. multimux performs no
such stripping and trusts every inbound header completely — if the origin
is also reachable directly, any client can set these headers itself and
bypass authentication entirely.
Config shape
Every field except routes has a default (Config::default()); every
route's outputs defaults to ["llhls"]. request_timeout_secs must
exceed 5.0 (the LL-HLS blocking-reload cap) or a legitimate long-poll
request would be cut off by the HTTP layer before the LL-HLS engine gets a
chance to resolve it.
40-camera scenario
Many routes, one shared output credential, one process:
Each camera is served at its own /camN/media.m3u8, independently
reconnected/supervised, all gated by the one output_auth credential.
External scheme plugin registry
A third-party crate can add a new input, output, or output-auth
scheme without editing multimux at all, wired purely via config JSON:
InputSpec::Custom { type_tag, params }, OutputKind::Custom { type_tag, params }, and OutputAuthSpec::Custom { type_tag, params }, resolved at
serve_with_registry(config, registry) time against a registry::SchemeRegistry
the embedding application builds (register_input/register_output/
register_auth). origin::serve(config) is serve_with_registry(config, SchemeRegistry::new()) — the empty registry, for the built-in schemes only.
See examples/custom_scheme.rs for a
complete, runnable example.
Quick start
Single-route quick start (one camera, no config file):
Multi-route JSON config file:
Every flag/field has a default (see multimux --help or
multimux::config::Config::default()); --rtsp/--name and --config are
mutually exclusive — pass one or the other.
Production hardening
- Supervised route lifecycle — each route reconnects with capped exponential backoff on connect failure, pipeline error, or source EOF, rather than dying on the first failure.
- HTTP resource limits + ingest timeouts — per-request timeout,
concurrency bound, and request-body cap on the listener
(
request_timeout_secs/max_concurrent_requests/max_request_body_bytes); connect/read timeouts on every ingest source (ingest_connect_timeout_secs/ingest_read_timeout_secs). - Structured errors + secret redaction +
tracing— no credential ever reaches a log line, error message, orDebugoutput. - Prometheus metrics + health/readiness — see the served-endpoints table above.
- Graceful shutdown — Ctrl-C /
SIGTERMdrains in-flight requests and every route's ingest task before exiting.
v1 limits (still out of scope)
- Per-viewer sessions, server-side ad insertion, manifest rewrites.
- DVR / VOD / disk spill (the window is RAM-only and rolls forward).
- Trick-play.
Additional documented limits inherited from the underlying streaming
depayloader (transmux's RtpStreamDepacketiser, issue #700): low-delay
H.264 only (no B-frame reordering), one AAC access unit per RTP packet, and
packets must arrive in order.
See
docs/superpowers/specs/2026-07-18-multimux-hub-design.md
in the workspace root for the full hub design, and
docs/superpowers/specs/2026-07-14-multimux-design.md
for the original v1 (RTSP→LL-HLS-only) design this hub replaced.
Examples
# Serve one real RTSP source.
# Register a custom input scheme with zero multimux edits (drives a real
# synthetic Dialer/IngestSession through supervise_driver end to end).
Example configs
JSON files under examples/ — each a realistic, valid
multimux::config::Config for multimux-cli --config <file> (deserialize +
validate() are guarded by tests/example_configs.rs, so they can't drift
from the config schema):
webcam-fleet-40.json— 40 routes (cam1..cam40) spanning all five ingest protocols (RTSP with per-camera Password/Bearerauth, RTP, TS/UDP multicast, TS/HTTP, HLS-pull), all served under one sharedoutput_auth(Basic) — heterogeneous ingest, one uniform LL-HLS(+DASH) output surface.reverse-proxy.json—output_authusing theforwardedscheme: TLS terminates at a fronting reverse proxy, and the origin trusts itsX-Forwarded-Userheader instead of challenging clients itself (seeOutputAuthSpec::Forwarded's trust-assumption docs before using this in production).multi-output.json— one RTSP ingest packaged to all three outputs (llhls,dash,ll_dash) from the same CMAF segments (issue #663 P4's "ingest-once, many-outputs").custom-scheme.json— anInputSpec::Customroute naming the"demo"schemeexamples/custom_scheme.rsregisters.
Spec
RFC 8216bis (HTTP Live Streaming, 2nd edition) — Low-Latency HLS:
#EXT-X-PART (§4.4.4.9), #EXT-X-PART-INF/#EXT-X-SERVER-CONTROL
(§4.4.3.7/§4.4.3.8), and Blocking Playlist Reload (§6.2.5.2). ISO/IEC
23009-1 (DASH) for the dash/ll_dash outputs. RTSP 1.0 (RFC 2326) for
RTSP ingest, via rtsp-runtime. RFC 7617/7616/6750 (Basic/Digest/Bearer)
for auth, via broadcast-auth.
License
MIT OR Apache-2.0