Skip to content

[codex] reject unsupported enhanced RTMP codecs#2017

Draft
kixelated wants to merge 2 commits into
mainfrom
codex/rtmp-codec-capabilities
Draft

[codex] reject unsupported enhanced RTMP codecs#2017
kixelated wants to merge 2 commits into
mainfrom
codex/rtmp-codec-capabilities

Conversation

@kixelated

@kixelated kixelated commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Parse enhanced RTMP codec capability advertisements from the RTMP connect command before rml_rtmp drops the raw AMF properties (it keeps only app, so a second deserializer sniffs the connect object).
  • Reject RTMP play requests with NetStream.Play.Failed when the selected broadcast needs enhanced FourCC codecs the client did not advertise it can decode (CanForward/CanEncode don't let a terminal player render).
  • Add FLV export codec introspection so moq-rtmp can check the muxed audio/video FourCCs before sending NetStream.Play.Start.
  • Update RTMP docs.

Fixes #2007.

Public API Changes

  • Added moq_mux::container::flv::EnhancedCodecs.
  • Added Export::enhanced_codecs (async).

Additive and non-breaking. No wire-protocol changes.

Follow-ups (not in this PR)

  • Server-side advertisement: the server should echo its own codec support (and capsEx) in the connect _result command object so an enhanced encoder will send E-RTMP on ingest. rml_rtmp 0.8 hard-codes the _result and keeps its serializer private, so this needs a small rml_rtmp fork/patch to inject connect-response properties.
  • capsEx / multitrack negotiation: tracked in Support enhanced-RTMP capsEx capability negotiation (multitrack) #2049.

Validation

  • cargo test -p moq-rtmp (20) + cargo test -p moq-mux (337) pass
  • cargo clippy -p moq-rtmp -p moq-mux --all-targets clean
  • cargo fmt clean

History

The second commit simplifies the first: collapses the 6-field client-capability struct to ClientCodecs { video, audio } over a small Support enum, merges the two header-waiting select! blocks in Play::accept into one, renames the connect parser to ConnectSniffer, tightens the gate to CanDecode, drops poll_enhanced_codecs from the public API, and reverts the unrelated just --fmt justfile churn.

(Written by GPT-5; simplification commit and this update written by Claude Opus 4.8)

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@kixelated kixelated force-pushed the codex/rtmp-codec-capabilities branch from e9dbcbe to 8e676bd Compare July 2, 2026 21:16
#[non_exhaustive]
pub struct EnhancedCodecs {
/// The enhanced video FourCC, if the selected video rendition needs one.
pub video: Option<[u8; 4]>,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make a FourCC type?

Co-Authored-By: GPT-5 <codex@openai.com>
@kixelated kixelated force-pushed the codex/rtmp-codec-capabilities branch from 8e676bd to 1fab896 Compare July 3, 2026 02:38
Collapse the client-capability model and play-accept path added for rejecting
unsupported enhanced-RTMP codecs on playback:

- ClientCapabilities (6 fields) -> ClientCodecs { video, audio } backed by a
  small Support enum (None/Any/Only). Drops the any_* flags, the combined
  fourcc_list, push_unique, and the is_empty() special-case.
- Merge the two header-waiting tokio::select! blocks in Play::accept into one:
  pull the first FLV chunk (resolves the header), then read codecs synchronously
  via enhanced_codecs() before Play.Start.
- Rename ConnectCapabilitiesParser -> ConnectSniffer; it owns its parsed result.
- Honor only CanDecode (a terminal player can't render CanForward/CanEncode).
- Drop poll_enhanced_codecs from moq-mux's public API (keep async
  enhanced_codecs).
- Revert the unrelated just --fmt justfile churn.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
kixelated added a commit that referenced this pull request Jul 3, 2026
…a private module)

Advertise the codecs moq-rtmp can ingest in the RTMP connect _result, so a
well-behaved enhanced encoder knows it may send HEVC/AV1/VP9/Opus/etc. This is
the server-side counterpart to the play-side codec gating in #2017.

moq-rtmp is built on rml_rtmp, which is unmaintained (last release 2023-04-29)
and hard-codes the connect _result with no hook to inject properties. No
maintained pure-Rust RTMP library exposes both the connect object and _result
customization, and every alternative is a large tokio-locked rewrite that would
still need a fork for _result injection.

So vendor rml_rtmp 0.8.0 directly into moq-rtmp as a private `rml` module (not a
separate published crate: moq-rtmp is published, so a `[patch.crates-io]` fork
would ship a broken crate to consumers). The vendor carries one additive patch,
`ServerSession::set_connect_response_properties`, which merges caller-supplied
name-value pairs into the connect _result command object (empty by default =
byte-identical to upstream). Porting the edition-2015 source into moq-rtmp's
edition-2024 crate needed three mechanical fixes: `crate::rml::` path prefixes,
`r#gen` for the now-reserved keyword, and dropping explicit `ref` under the new
match ergonomics. The module opts out of the workspace -D warnings gate at its
root; MIT preserved (see src/rml/LICENSE); upstream's 168 tests come along.

On connect, moq-rtmp advertises the FLV importer's accepted FourCCs
(avc1/hvc1/av01/vp09, Opus/mp4a/.mp3/ac-3/ec-3) as videoFourCcInfoMap /
audioFourCcInfoMap, each flagged CanForward (the gateway demuxes FLV into MoQ, it
doesn't decode). A wire-level test drives a raw client through handshake +
connect and deserializes the server's _result to confirm the maps arrive.
kixelated added a commit that referenced this pull request Jul 3, 2026
…a private module)

Advertise the codecs moq-rtmp can ingest in the RTMP connect _result, so a
well-behaved enhanced encoder knows it may send HEVC/AV1/VP9/Opus/etc. This is
the server-side counterpart to the play-side codec gating in #2017.

moq-rtmp is built on rml_rtmp, which is unmaintained (last release 2023-04-29)
and hard-codes the connect _result with no hook to inject properties. No
maintained pure-Rust RTMP library exposes both the connect object and _result
customization, and every alternative is a large tokio-locked rewrite that would
still need a fork for _result injection.

So vendor rml_rtmp 0.8.0 directly into moq-rtmp as a private `rml` module (not a
separate published crate: moq-rtmp is published, so a `[patch.crates-io]` fork
would ship a broken crate to consumers). The vendor carries one additive patch,
`ServerSession::set_connect_response_properties`, which merges caller-supplied
name-value pairs into the connect _result command object (empty by default =
byte-identical to upstream). Porting the edition-2015 source into moq-rtmp's
edition-2024 crate needed three mechanical fixes: `crate::rml::` path prefixes,
`r#gen` for the now-reserved keyword, and dropping explicit `ref` under the new
match ergonomics. The module opts out of the workspace -D warnings gate at its
root; MIT preserved (see src/rml/LICENSE); upstream's 168 tests come along.

On connect, moq-rtmp advertises the FLV importer's accepted FourCCs
(avc1/hvc1/av01/vp09, Opus/mp4a/.mp3/ac-3/ec-3) as videoFourCcInfoMap /
audioFourCcInfoMap, each flagged CanForward (the gateway demuxes FLV into MoQ, it
doesn't decode). A wire-level test drives a raw client through handshake +
connect and deserializes the server's _result to confirm the maps arrive.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

moq-rtmp: E-RTMP codecs sent to legacy clients with no capability negotiation

1 participant