feat(moq-msf): draft-01 support behind a version-agnostic snapshot API#1834
Open
kixelated wants to merge 3 commits into
Open
feat(moq-msf): draft-01 support behind a version-agnostic snapshot API#1834kixelated wants to merge 3 commits into
kixelated wants to merge 3 commits into
Conversation
draft-ietf-moq-msf-01 changed the catalog `version` field from a JSON number (`1`) to a `"draft-XX"` string. Introduce a `Version` enum that serializes the newest draft (`"draft-01"`) by default while still decoding draft-00's numeric `1`, so older publishers stay compatible. `Catalog::version` changes type from `u32` to `Version`. moq-mux now emits `"draft-01"` and its MSF consumer accepts both encodings. The JS `@moq/msf` schema accepts the number or any `"draft-XX"` string and exports a `VERSION` constant. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R2ruH25xAPfNhydKJ9Q67n
Verifying backwards compatibility against the draft-ietf-moq-msf-00 examples surfaced a real gap: the spec marks `isLive` required but its own mediatimeline/eventtimeline track examples omit it. With `isLive` as a required field, serde and zod rejected the entire catalog. Default `is_live` to false when absent (Rust `#[serde(default)]`, JS `z.optional`) so draft-00 catalogs decode. Add tests built from the draft-00 example JSON (AV, timeline tracks, completion) in both languages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R2ruH25xAPfNhydKJ9Q67n
draft-01 changes more than the version field: init data moved out of the
track (inline `initData`) into a root `initDataList` referenced per-track
by `initRef`, and `version` became a `"draft-XX"` string. Rather than leak
any of that, make `Catalog` a version-agnostic snapshot (`{ tracks }`) and
push the wire details into (de)serialization.
- Drop the public `version` field and `Version` enum. Parsing accepts
draft-00 (numeric version, inline initData) and draft-01 (string
version, initDataList + initRef); serializing always emits draft-01.
- Init data is always presented inline via `Track::init_data`: initRef is
resolved against initDataList on parse, and on serialize identical
payloads are hoisted into a deduplicated initDataList with initRef
pointers. Callers never see the version or the indirection.
- moq-mux producer/consumer are unchanged except dropping the version
field; they keep using inline `init_data` and the abstraction handles
the wire form.
- Mirror the same abstraction in `@moq/msf` (encode/decode hide version,
initDataList, initRef).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R2ruH25xAPfNhydKJ9Q67n
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for draft-ietf-moq-msf-01 while staying backwards-compatible with draft-00 catalogs. The design goal: the wire format is an internal detail. Callers work with a version-agnostic snapshot and never touch the catalog
versionor the init-data indirection.What changed between draft-00 and draft-01 (the parts that matter to us)
version: JSON number1→ JSON string"draft-XX".initData) into a rootinitDataListof{ id, type:"inline", data:<base64> }, referenced per-track byinitRef.isLive: still nominally required, but draft-00's ownmediatimeline/eventtimelineexamples omit it.deltaUpdateboolean→array,addTracks/removeTracks/cloneTracks→deltaUpdateops, new optional track fields likebuffers/avgBitrate/encryptionScheme/template, newpackaging/roleenum values) is additive and already tolerated — unknown fields are ignored and unknown enum values fall intoUnknown.Design
moq_msf::Catalogis now a snapshot:{ tracks: Vec<Track> }. No publicversion, noVersionenum, noinitRef/initDataList. The wire complexity lives in custom (de)serialization:versionis parsed permissively (number must be1; any"draft-XX"string is accepted).initRefis resolved againstinitDataListso every track ends up with inlineTrack::init_data. MissingisLivedefaults tofalse.version: "draft-01", and inlineinit_datais hoisted into a deduplicatedinitDataListwithinitRefpointers (identical payloads across tracks collapse to one entry).Net effect:
moq-mux's producer/consumer didn't need to learn any of this — they keep setting/reading inlineinit_data, and the abstraction handles the wire form.@moq/msfmirrors the sameencode/decodebehavior.This also shrinks the public API versus the previous commits on this branch (the
Versionenum andCatalog.versionfield are gone).Files
rs/moq-msf— snapshotCatalog, custom serde, init-data hoist/resolve + dedup,isLivedefault.rs/moq-mux— producer emits the snapshot (drops the version field); consumer unchanged.js/msf—encode/decodehide version + initDataList/initRef; publicCatalog/Trackare version-agnostic.moq-msf,moq-mux),doc/concept/standard/msf.md.Out of scope
Full delta-update application (
deltaUpdateops / draft-00addTracksetc.) — we don't consume deltas. The snapshot API is the right shape to add it behind later without a breaking change.Branch targeting
dev: MSF catalog-format change + breaking public-API change to publishedrs/moq-msfandjs/msf.Test plan
cargo test -p moq-msf -p moq-mux— draft-00 numeric version, draft-01 string version, unsupported numeric version rejected, draft-00 example catalogs (AV / timeline-without-isLive/ completion),initRef→inline resolution, serialize hoist+dedup round-tripcargo clippy -p moq-msf -p moq-mux --all-targets+cargo fmt --checkcleanbun test js/msf(5 tests incl. initRef resolve + hoist/dedup round-trip) +tsc --noEmit+biome check(Written by Claude)