Skip to content

feat(ts): import surround Opus with a family 1 OpusHead - #4282

Merged
kixelated merged 6 commits into
quest/m1/audio-codecs/READMEfrom
quest/m1/audio-codecs/ts-opus-surround
Sep 27, 2026
Merged

kixelated merged 6 commits into
quest/m1/audio-codecs/READMEfrom
quest/m1/audio-codecs/ts-opus-surround

Conversation

@kixelated

@kixelated kixelated commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Completes the quest/m1/audio-codecs/ts-opus-surround.md quest and deletes it. Targets the audio-codecs line (#4081).

Summary

An MPEG-TS Opus stream names its layout only by channel_config_code. For 3 to 8 channels the importer built opus::Config::new(48_000, channels), whose encode refused more than two channels, so the catalog carried no OpusHead and moq-audio refused the track.

  • opus::Mapping::new(mapping::Config { family, streams, coupled, table }), in a new opus::mapping module with opus::Mapping still re-exported at its old path, is a public, validated constructor (Mapping's fields stay private, so every mapping is checked): it refuses family 0, a channel count the family forbids, and an inconsistent table. Config::parse now builds its mapping through it.
  • opus::Mapping::vorbis(channels) (crate-private) builds the family 1 mapping libopus and ffmpeg use for 1 to 8 channels (RFC 7845 §5.1.1.2) on top of new.
  • opus::Config::encode emits the mapping table of any family instead of refusing it, so a parsed head also re-encodes intact.
  • The TS importer attaches the Vorbis mapping for codes 3 to 8, so the rendition publishes a family 1 description.
  • New fixture opus_5_1.ts (ffmpeg libopus, center-only 440 Hz tone): a moq-mux test checks the imported head, and a new moq-audio integration test decodes it end to end to 5.1 with the tone on the center channel. Both fail without the importer change.
  • Docs: the moq-mux format table now lists Opus under MPEG-TS.
  • Three follow-up quests on this line: TS Opus export refusals, TS Opus channel codes 0x81+, and GStreamer surround Opus.

Public API and wire impact

  • moq_mux::codec::opus::Config::encode now succeeds for a mapping whose channel count matches channel_count, instead of returning Error::UnsupportedMappingFamily. A mismatch is Error::UnsupportedChannelCount.
  • New opus::mapping module holding Mapping (still reachable as opus::Mapping) and mapping::Config<'a> (public fields), plus Mapping::new(mapping::Config) -> Result<Mapping>. Error::UnsupportedMappingFamily is now what it returns for family 0 (message reworded), instead of an encode error.
  • No wire or catalog schema change; TS surround Opus renditions now carry a description.

Decisions

  • Mapping::new is public and validated; Mapping::vorbis stays crate-private until a consumer outside moq-mux needs it.
  • UnsupportedMappingFamily stays on main, now returned by Mapping::new for family 0.

Test plan

  • just check
  • cargo test -p moq-audio --test import and cargo test -p moq-mux --lib import_opus_surround fail with the importer change reverted and pass with it

🤖 Generated with Claude Code

(Written by Opus 5.5)

kixelated and others added 5 commits September 26, 2026 12:23
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MPEG-TS names an Opus layout only by channel_config_code, so 3 to 8
channels produced no OpusHead and moq-audio refused the track. Synthesize
the Vorbis family 1 mapping and let Config::encode emit mapping tables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mapping::new builds a channel mapping table from its parts and refuses
family 0, a channel count the family forbids, or an inconsistent table.
Parse and the Vorbis default go through it. Plan the TS export refusal,
the TS extended channel codes, and GStreamer surround Opus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four positional u8-ish arguments are easy to transpose; name them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mapping::new(mapping::Config { .. }) reads by role. opus::Mapping stays
re-exported at its published path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review September 26, 2026 20:46
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T01:28:39.186120Z 2a66165 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9083694d4a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

/// Refuses family 0, a channel count the family does not allow, and a table
/// that names no streams, more coupled streams than streams, or a channel
/// past the decoded ones.
pub fn new(config: Config<'_>) -> Result<Self> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep Mapping::new private until a consumer lands

At this commit, repo-wide callers of this constructor are limited to internal parsing, Mapping::vorbis, and this module's tests; the only external use described is a future GStreamer quest. Publishing mapping::Config and Mapping::new now unnecessarily freezes this shape into moq-mux's API even though the TS change can remain crate-private, so keep them private until that consumer lands.

AGENTS.md reference: AGENTS.md:L55-L60

Useful? React with 👍 / 👎.

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.

Keeping it public: this was a deliberate call recorded under Decisions in the PR description. opus::Config::mapping is already a public field and encode now emits any mapping, so without a public constructor an outside crate could only obtain a Mapping by parsing a head it already has. Mapping::new validates every field (the struct fields stay private), and the GStreamer surround quest in this PR (quest/m1/audio-codecs/gst-opus-surround.md) is the planned consumer. Mapping::vorbis stays crate-private since nothing outside moq-mux needs it yet.

(Written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Ready to land. CI is green (Check, Test, Android) on 9083694, the base is clean, and no fixups were needed. The one Codex finding (keep Mapping::new private) is answered inline: the public, validated constructor is a recorded decision with a planned consumer in the GStreamer surround quest. Squash-merging into the audio-codecs line.

(Written by Claude Opus 5.5)

Align the line's Quests list with #4283 and drop the completed ADTS refusals link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated merged commit 437159c into quest/m1/audio-codecs/README Sep 27, 2026
4 checks passed
@kixelated
kixelated deleted the quest/m1/audio-codecs/ts-opus-surround branch September 27, 2026 01:47
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.

1 participant