feat(moq-ffi)!: defer dynamic track accept so media tracks declare a timescale#1761
Merged
Conversation
…timescale A track from `requested_track()` was accepted eagerly with no timescale, which is correct for raw tracks but wrong for media: a media track should declare the container timescale so per-frame timestamps ride the moq-net wire for the relay. The accept freezes the timescale, and a subscribe blocks until it, so the choice must be made at accept and can't be amended later. Defer the accept to the first producer operation, which knows the right timescale: - `requested_track()` returns a not-yet-accepted handle (a `TrackRequest`). - `publish_media_on_track` accepts at `hang::container::TIMESCALE` (timed media). - raw `write_frame`/`append_group`/`consume`/`used`/`unused`/`finish` accept untimed. - `abort` rejects a still-pending request, or aborts an accepted one. No FFI signatures change, only behavior: the requesting subscriber now stays pending until that first op instead of unblocking at `requested_track()`. Tests run the subscribe concurrently to match. py: broaden `subscribe_media` to accept a bare `Container` (the consumer side of the dynamic flow) and fix the dynamic tests, which never actually ran before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3 tasks
…edia test uniffi's generated Container variant classes aren't seen as Container subtypes statically, so passing Container.LEGACY() to subscribe_media (typed Video | Audio | Container) tripped pyright in CI. Restore the cast that the original test used. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng defaults (#1763) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
A track handed out by
MoqBroadcastDynamic.requested_track()was accepted eagerly with no timescale. That's right for raw tracks (moq-boy status/command), but wrong for media: a media track should declare the container timescale (hang::container::TIMESCALE) so per-frame timestamps ride the moq-net wire and the relay gets timing without parsing the payload.The constraint is that the accept freezes the timescale into
TrackInfoand a subscribe blocks until that accept, so the timescale must be chosen at the accept and can't be amended afterward. One genericrequested_track()can't satisfy both raw (untimed) and media (timed) with a single hardcoded accept.This PR defers the accept to the first producer operation, which knows the right timescale:
requested_track()returns a not-yet-accepted handle (wraps aTrackRequest).publish_media_on_trackaccepts at the media timescale → the requested media track is now properly timed.write_frame/append_group/consume/used/unused/finishaccept untimed.abortrejects a still-pending request (the subscriber's subscribe fails), or aborts an already-accepted one.This is the follow-up to #1750's quick fix (which only made media tolerate an untimed requested track); the track is now timed at the source, as the maintainer's timestamp-on-wire convention wants.
Breaking change (behavior, not signature)
No FFI signatures change. The observable difference: the requesting subscriber now stays pending until the first producer op on the requested track, instead of unblocking at
requested_track(). This is why it targetsdev. swift/kt/go auto-bindings andlibmoqare unaffected (no signature change; verifiedlibmoqhas no refs and builds clean). The kt/swift dynamic-broadcast doc examples (writeFrame/abortin arequestedTracksloop) remain valid.Python
subscribe_mediato also accept a bareContainer— the consumer-side counterpart of the dynamic flow, where you subscribe before the catalog exists.test_dynamic_track_request_can_publish_media, which never actually ran before (it was missingawaits and passed aContainerwhere the wrapper wanted a catalog record).Test plan
moq-ffitests pass (incl. all threedynamic_track_request*); clippy + fmt clean via nix.libmoq+moq-ffibuild clean; rebased onto currentdev(post per-frame-duration removal) and re-verified.🤖 Generated with Claude Code
(Written by Claude)