11---
22title : moq-rtmp
3- description : RTMP / enhanced-RTMP -> MoQ contribution ingest gateway
3+ description : RTMP / enhanced-RTMP < -> MoQ gateway ( ingest and egress)
44---
55
66# moq-rtmp
77
8- ` moq-rtmp ` ingests [ RTMP] ( https://en.wikipedia.org/wiki/Real-Time_Messaging_Protocol )
9- (the protocol OBS, ffmpeg, and most hardware encoders speak) and publishes each
10- stream into Media over QUIC as an ordinary broadcast.
8+ ` moq-rtmp ` bridges [ RTMP] ( https://en.wikipedia.org/wiki/Real-Time_Messaging_Protocol )
9+ (the protocol OBS, ffmpeg, and most hardware encoders and players speak) and
10+ Media over QUIC, in ** both directions** :
11+
12+ - ** Publish (ingest):** an encoder pushes a stream in, and ` moq-rtmp ` publishes it
13+ into MoQ as an ordinary broadcast.
14+ - ** Play (egress):** a player pulls ` rtmp://host/<app>/<key> ` , and ` moq-rtmp `
15+ subscribes to that broadcast from MoQ and streams it back down. VLC, ffplay, and
16+ mpv can play it (browsers can't -- Flash is dead).
1117
1218RTMP carries media as FLV-format audio/video messages. ` moq-rtmp ` runs the RTMP
1319handshake and chunk/AMF session (via the pure-Rust
14- [ ` rml_rtmp ` ] ( https://crates.io/crates/rml_rtmp ) , no librtmp), re-wraps each
15- message as an FLV tag, and feeds it to ` moq-mux ` 's FLV demuxer, which routes the
16- media onto MoQ tracks. It's the contribution-ingest sibling of ` moq-srt `
17- (SRT/MPEG-TS) and ` moq-rtc ` (WHIP).
20+ [ ` rml_rtmp ` ] ( https://crates.io/crates/rml_rtmp ) , no librtmp). On ingest it re-wraps
21+ each message as an FLV tag and feeds it to ` moq-mux ` 's FLV demuxer; on egress it
22+ muxes the broadcast back to FLV with ` moq-mux ` and sends the tags out as RTMP
23+ messages. It's the sibling of ` moq-srt ` (SRT/MPEG-TS) and ` moq-rtc ` (WHIP/WHEP ).
1824
1925Both ** legacy RTMP** (H.264 + AAC) and ** enhanced RTMP** (E-RTMP: the HEVC, AV1,
20- VP9, Opus, and AC-3 FourCC payloads) are supported, because all codec handling
21- lives in the ` moq-mux ` FLV demuxer.
26+ VP9, Opus, and AC-3 FourCC payloads) are supported in each direction, because all
27+ codec handling lives in the ` moq-mux ` FLV demuxer/muxer. Legacy players that speak
28+ only H.264 + AAC will reject the E-RTMP codecs on the play path.
2229
2330## CLI shape
2431
@@ -42,6 +49,15 @@ and the stream key to `cam0`; with ffmpeg:
4249ffmpeg -re -i input.mp4 -c copy -f flv rtmp://127.0.0.1:1935/live/cam0
4350```
4451
52+ Then play the same broadcast back out over RTMP from any player:
53+
54+ ``` bash
55+ # Pulls broadcast `live/cam0` (the same URL it was pushed to).
56+ ffplay rtmp://127.0.0.1:1935/live/cam0
57+ mpv rtmp://127.0.0.1:1935/live/cam0
58+ vlc rtmp://127.0.0.1:1935/live/cam0
59+ ```
60+
4561### ` serve ` flags
4662
4763- ` --server-bind ` : QUIC/WebTransport bind address (default ` [::]:443 ` ). Also
@@ -81,23 +97,32 @@ moq-rtmp serve --server-bind [::]:443 --tls-generate localhost \
8197
8298Each connection's broadcast path is ` <app>/<key> ` from the RTMP app and stream
8399key (` rtmp://host/<app>/<key> ` ), falling back to just the app when the key is
84- empty, with ` --rtmp-prefix ` prepended. First publisher on a path wins; a second
85- connection to the same path is rejected.
100+ empty, with ` --rtmp-prefix ` prepended. The same routing applies to both
101+ directions, so the URL round-trips: push to ` rtmp://host/live/cam0 ` , then pull it
102+ back from ` rtmp://host/live/cam0 ` .
103+
104+ A play waits for the broadcast to be announced, so a player can connect slightly
105+ before the publisher. First ** publisher** on a path wins (a second publish to a
106+ live path is rejected); ** plays** don't claim a path, so any number of players can
107+ pull the same broadcast at once. In ` serve ` mode plays are served from the same
108+ origin the server exposes, so anything in it -- RTMP ingests and otherwise -- can
109+ be pulled back out over RTMP.
86110
87111## Notes and limitations
88112
89113- ** Auth.** The binary (and the ` moq_rtmp::run ` convenience) is unauthenticated:
90- anyone who can reach the TCP port can publish. Gate it with a host firewall or
91- a private network. To authenticate, embed the library and drive its
92- ` Server ` / ` Request ` API: ` Server::accept ` yields a pending publish, and you
93- verify the app / stream key (e.g. the stream key as a moq-token JWT) before
94- calling ` request.accept(origin, path) ` or ` request.reject(reason) ` -- no
95- callback, the policy lives in your loop.
96- - ** Embedding.** A relay can run ingest in-process by depending on the ` moq-rtmp `
97- library (` default-features = false ` ). Call ` moq_rtmp::run ` against its own
98- origin for the unauthenticated case, or use ` Server ` / ` Request ` to plug in the
99- relay's existing JWT/path auth and scope the origin per token. Either way the
100- media is published locally with no extra hop.
114+ anyone who can reach the TCP port can publish or play. Gate it with a host
115+ firewall or a private network. To authenticate, embed the library and drive its
116+ ` Server ` / ` Request ` API: ` Server::accept ` yields a ` Request ` that is either a
117+ ` Publish ` or a ` Play ` , and you verify the app / stream key (e.g. the stream key
118+ as a moq-token JWT) before accepting it into / out of an origin at a path of your
119+ choosing, or rejecting it -- no callback, the policy lives in your loop.
120+ - ** Embedding.** A relay can run the gateway in-process by depending on the
121+ ` moq-rtmp ` library (` default-features = false ` ). Call ` moq_rtmp::run ` against its
122+ own origin for the unauthenticated case (publishers ingest into it, players are
123+ served out of it), or use ` Server ` / ` Request ` to plug in the relay's existing
124+ JWT/path auth and scope the origin per token. Either way the media stays local
125+ with no extra hop.
101126- ** RTMPS.** Embedders can terminate TLS themselves: set ` Config::tls ` (or
102127 ` Server::with_tls ` ) with a ` rustls::ServerConfig ` , or accept the connection and
103128 finish the TLS handshake by hand and hand the stream to ` moq_rtmp::accept_stream `
0 commit comments