Repository navigation
fix(publish): mint a new track name when a rendition's decoder identity changes - #5552
Conversation
Co-Authored-By: GPT-6 <noreply@openai.com>
Co-Authored-By: GPT-6 <noreply@openai.com>
|
Quest outcome: Served identity/ceiling changes now end the old catalogs and replace the broadcast epoch; exact-config Opus probing happens before advertisement, and encoder maxima reset per network producer. No new public API or wire fields; catalog decreases are refused and custom-track handles follow Validation: complete Follow-up: Rust track identity enforcement remains its own quest. Preserve the exact Opus probe lifecycle when combining the independent audio-warmup change. (Written by GPT-6) |
A served rendition whose codec, description, container, audio format, or coded-size ceiling changes keeps its catalog ID and moves to a fresh `track` name in the same epoch, ending the old track on its old identity. This replaces restarting the whole broadcast under a new epoch, per the 2026-10-15 maintainer decision. Names are fixed once the catalog is first served, so pre-announce churn mints nothing. Drops the epoch handoff (per-epoch encoder limits, catalog termination, announce gating) that only the restart needed. Keeps the Opus description probe and the refusal of falling bitrate/framerate, now keyed by track name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A producer mutating a container it shares rewrote the stored identity, so an init change kept the same track name. Found by Codex review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Same-named tracks referenced in different broadcasts are different tracks, so removing one compared the other against it. Found by Codex review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oval A rendition re-registered on the same track name at a lower bitrate lowered the advertised maximum, though the track had carried more. Broadcast now advertises the most each served name has carried. Found by Codex review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before the first serve, a codec change with a lower bitrate was refused by the catalog, leaving the old codec advertised. Unserved folds now carry the previous fold's maxima. Found by Codex review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per the 2026-10-15 maintainer decision, only fields that force a decoder reinitialization (codec, description, container, display aspect, sample rate, channel count) mint a new track. Coded size, bitrate, and framerate update in place in either direction, so the falling-maxima refusal and per-name maxima are gone, and the hang draft, concept doc, and Rust quest drop the ceiling and maxima classes. Minted names count up per catalog ID (<id>/1, <id>/2), skipping every name the broadcast already uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Application tracks on net share the namespace, so names under a rendition's ID are reserved for its replacements rather than tracked through the network broadcast's private state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A rendition removed before it had a config keeps its network track, so the counter handed its name to a replacement and createTrack threw. Found by Codex review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Registering a minted name as a new rendition pointed two renditions at one track, and the network loop stopped at the duplicate. Found by Codex review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A replacement minted before its rendition had a config was never served, so a later mint could reuse it and a rendition could register under it. Record every minted name for the network broadcast's life. The estimate check keyed entries by track alone, so two catalog IDs aliasing one track collapsed and a falling estimate on one slipped through. Key each entry by section, ID, broadcast, and track. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Follow-up planned in #5584: (Written by Claude Opus 5.5) |
A rendition's media is the broadcast's own track, so a config naming another broadcast or track sent viewers to media this publisher never writes, and an identity change overwrote the target with a local replacement name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge summary
Follow-ups are in the description: Rust enforcement, encoders advertising current values, the (Written by Claude Opus 5.5) |
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 14 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f562ae6677
ℹ️ 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".
| // The network loop ends each renamed rendition's old track and serves the new name. | ||
| if (Object.keys(renamed).length > 0) this.#names.update((names) => ({ ...names, ...renamed })); |
There was a problem hiding this comment.
Swap tracks before accepting the new media identity
When a demanded rendition changes codec, description, or another identity field while frames continue arriving, catalog.mutate publishes the new config before this update schedules the network effect that closes the old track and hands the encoder its replacement. The encoder can already be configured for the new identity during that microtask gap, so an output frame may be written under the old track name, breaking existing subscribers and the track-name invariant. Gate encoding or swap the producer before the new identity becomes active.
AGENTS.md reference: AGENTS.md:L64-L64
Useful? React with 👍 / 👎.
Problem
Catalog edits could redefine served audio/video tracks in place. A codec knob, an audio capture changing its rate or channels, or the Opus decoder description arriving with the first subscriber all rewrote a track's decoder config under the same name. The hang draft's Track Identity section says a name always means the same content.
Approach
Broadcastrecords each track name's decoder identity from the catalog's first served snapshot. Identity means only the fields a consumer cannot change without reinitializing its decoder or demuxer:codec,description,container,sampleRate,numberOfChannels,displayAspectWidth, anddisplayAspectHeight. When a later fold changes the identity, the rendition keeps its catalog ID and itstrackpoints at a fresh name. The old track ends cleanly on its old identity. The broadcast, its epoch, and every sibling rendition carry on, and viewers follow the catalog to the new name (js/watchalready rebuilds its pipeline whentrackchanges). Every other field, including bitrate, frame rate, and coded size, updates the served track in place, in either direction.Fresh names are a per-broadcast counter,
<id>/1,<id>/2, .... Closed-track replacement now uses the same counter. A minted name skips every name the broadcast has registered, minted, or served, and every key and track name in the catalog, including an application's own sections. Names under a rendition's ID are reserved for its replacements: registering one throws, anddoc/lib/js/publish.mddocuments the reservation for application tracks onnet.Identities are cloned, so a producer mutating a shared
containercannot rewrite one. They persist across a rendition's removal, so a name that returns with different media gets a fresh name too. Nothing is recorded before the first serve, so pre-announce churn (a camera settling its resolution) mints nothing.The Opus description is probed with a throwaway encoder that uses the exact live config before advertising, so the first subscriber no longer changes the identity.
Decisions
<id>/<n>, collision-free since this publisher owns the namespace. This replaces<id>/<uuid>.codec,description: decoder config fields; changing either reconfigures the decoder.container: the consumer picks its demuxer once per track.sampleRate,numberOfChannels: requiredAudioDecoderConfigfields.displayAspectWidth/Height: part of watch's decoder config, and the draft already says changing them requires a decoder reinit.codedWidth/Height. These are optional hints, and decoders follow in-band resolution changes at keyframes. A size that needs a new level also changes the codec string, and adescriptionpins the size where one exists.bitrate,framerate,optimizeForLatency,jitter,delay. These are live state or consumer hints.broadcastortrack(agent):netnamed likevideo/1(agent):<id>/for replacements and document it.uniqueNameAPI to@moq/net. This would be new public surface.quest/m1/catalog-identity-js.mdis completed and removed.quest/m1/catalog-identity-rs.mdrecords the decisions for the Rust side.Impact
Rendition.configdocuments which fields mint a new track. A config that setsbroadcastortrackis refused.Broadcast.net; the first revision of this PR did.CatalogProducer.mutatechecksjitter/delayper section, ID, broadcast, and track name. This keeps aliases of one track apart and lets a renamed rendition's new track start over.trackfor an identity change, as it already did for replacements.doc/concept/hang.mdnow define identity as the decoder-reinit fields and say every other field is live state.bitrate/framerateare removed from the JS and Rust catalog types.Validation
just checkpasses (253 publish tests). New regressions, each confirmed to fail without its fix:Video.Encodergrowing its ceiling updates in place.Review
Codex adversarial review rounds found and fixed:
containerThe final round on the head approved with no material findings.
Follow-ups
quest/m1/catalog-identity-rs.md.CatalogProducer'sjitter/delaynever-fall check compares adjacent snapshots only, so a rendition removed and restored loses its floor. This is unchanged frommain.🤖 Generated with Claude Code
(Written by Claude Opus 5.5)