Skip to content

feat(ffi): decode a raw group as media, for arrival-order reads - #5356

Merged
kixelated merged 12 commits into
mainfrom
feat/ffi-arrival-media-read
Oct 13, 2026
Merged

kixelated merged 12 commits into
mainfrom
feat/ffi-arrival-media-read

Conversation

@kixelated

@kixelated kixelated commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The bindings have no way to read container-decoded media in arrival order. Latency and jitter measurement needs that: every frame stamped with its media time the moment it lands.

  • media.TrackConsumer.next() decodes the container, but it reads in decode order through a jitter buffer, so a stalled group holds back newer ones.
  • The raw TrackConsumer.recv_group() reads in arrival order, but its frames are raw. Their timestamp_us is the transport's (absent on sessions below moq-lite-05), and the media time is inside the payload, such as the legacy container's varint prefix.

Approach

MoqMediaGroupConsumer gains a constructor, new(group, container), that decodes a raw MoqGroupConsumer with the track's container. It mirrors moq_mux::container::GroupConsumer::new(group, format), and it takes over the raw handle the way the JSON consumers take over a track:

  • The raw handle closes.
  • A group that already read a frame is refused with AlreadyCommitted, since the decoder would start mid-group.
  • A container that fails to parse is refused first, which leaves the raw group readable.

Arrival order then composes from existing pieces:

track = await broadcast.subscribe_track(name, Subscription(max_delay=...))
while (group := await track.recv_group()) is not None:
    spawn(read(moq.media.GroupConsumer(group, container)))

The media consumers also drop their 5-word names: MoqMediaContainerConsumer becomes MoqMediaTrackConsumer and MoqMediaContainerGroupConsumer becomes MoqMediaGroupConsumer, with configs MoqMediaTrackConfig and MoqMediaGroupConfig. The wrappers expose them as media.TrackConsumer and media.GroupConsumer.

container::GroupConsumer no longer waits on its look-ahead. Before, an untimed video frame (a CMAF sample without a duration) was held until a successor or FIN timed it. Now it peeks only at what has already landed: a live group returns each frame as it arrives, and a fetched group still gets its durations filled.

Decisions

Media consumer names (maintainer, 2026-10-13: "5 words is terrible"):

  • ✅ media.TrackConsumer + media.GroupConsumer, with TrackConfig / GroupConfig (generated MoqMediaTrackConsumer, MoqMediaGroupConsumer, MoqMediaTrackConfig, MoqMediaGroupConfig).
    • Generated names go from 5 words to 4, and wrapper names from 3 to 2.
    • The media layer mirrors net one level up, and the pair reads what media.TrackProducer writes.
    • It drops an overloaded "Container": media.ContainerProducer imports a whole multi-track file, while the consumer decodes one track's per-frame container.
    • It follows the media.TrackProducer precedent of reusing a root noun. Kotlin code that star-imports both dev.moq.* and dev.moq.media.* qualifies these names, as the docs already do for TrackProducer.
    • Go: media.NewTrackConsumer, media.NewGroupConsumer(group, container), media.FetchGroup(ctx, broadcast, config). Swift: Media.TrackConsumer, Media.GroupConsumer(group:container:). C++: moq::MediaTrackConsumer, moq::MediaGroupConsumer.
  • Option 2: rename only the group type to media.GroupConsumer. This is a smaller diff, but ContainerConsumer beside GroupConsumer reads as unrelated, and the "Container" overload stays.
  • Option 3: keep the names and rely on plan: quest for one UniFFI crate per binding layer #5405's per-layer crates. The wrappers stay at 3 words, and the crate split is an m2 spike.

API shape (maintainer):

  • ✅ A constructor over a raw group, MoqMediaGroupConsumer::new(group, container).
    • Every moq-ffi verb maps to a Rust one (quest/m1/ffi-shape), and the type is built from the lower-layer handle it wraps.
    • It works for any raw group source: recv_group, next_group, or fetch_group.
    • It needs no new commit mode, no shared cursor, and no Clone on moq-mux types.
  • MoqMediaTrackConsumer::recv_group() (previous revision). One handle, but it adds a verb Rust's container::Consumer lacks, a second commit mode on the media consumer, a generic Cursor<O>, and Clone on catalog::hang::Container and fmp4::Wire.
  • A flat track-level (sequence, frame) stream. One loop, but it needs new merging machinery in moq-mux and adds a third read shape beside the converged container read result of feat(mux): read gaps and declared ends as items in delivery order #5288/feat(watch)!: expose per-frame arrivals through decoder handles #5311.

Go names (maintainer, 2026-10-13: "Rename Go now"):

  • ✅ media.NewGroupConsumer(group, container) is the constructor and media.FetchGroup(ctx, broadcast, config) is the fetch, mirroring Rust. The Go media package has not shipped on release, so no published caller breaks; doc/setup/upgrade.md names both.
  • media.DecodeGroup(group, container), keeping the old constructor name as the fetch (previous revision).

Generated-layer namespaces: the per-layer UniFFI crate split moved to its own quest (#5405, quest/m2/ffi-crate-split.md), which now uses the new names as its example.

Avoiding the dedicated type, for example MoqGroupConsumer::read_media(container), is rejected:

  • It puts a media verb on the root net-layer type, which ffi-shape moved away from.
  • The raw handle would have to hold per-group decoder state: CMAF batch buffering, first-frame keyframe marking, and the look-ahead.

CMAF media time (Codex P1):

  • ✅ Keep the frame timestamp as the CMAF fragment's anchor, as next() and fetch already do and as fmp4::decode documents. The moq-lite frame timestamp is the broadcast timeline, and tfdt only places samples relative to each other. An untimed frame decodes at tfdt.
  • Decode from tfdt. This would put arrival-order reads on a different timeline from decode-order reads of the same track.

Lossless delivery (OpenAI P2):

  • ✅ The subscription's max_delay decides which groups arrive and how long a stalled one stays readable, as moq-net's recv_group documents. The docs now say so instead of promising none is skipped.
  • A budget-exempt observation mode in moq-net. This would be a new subscriber policy for one consumer.

Look-ahead latency (OpenAI P2):

  • ✅ Never wait on look-ahead: peek only at what has already landed.
  • A live/fetched mode flag on GroupConsumer, which is more API for the same result.

Alignment with #5288/#5311: frames stay MoqMediaFrame, the record fetch already returns, so this PR adds no new read shape. When the converged container read result (frame, continuity, arrival) reaches the bindings, media.GroupConsumer.next() picks it up along with fetch. Until then, a caller stamps arrival when next() returns. That is now the moment each frame lands, the same point where JS stamps it.

Impact

Breaking binding API (unreleased on release, which still has MoqMediaConsumer / MoqMediaGroupConsumer); no wire change.

  • rs/moq-ffi:
    • Renamed MoqMediaContainerConsumer to MoqMediaTrackConsumer, MoqMediaContainerGroupConsumer to MoqMediaGroupConsumer, MoqMediaContainerConfig to MoqMediaTrackConfig, and MoqMediaContainerGroupConfig to MoqMediaGroupConfig.
    • New constructor MoqMediaGroupConsumer::new(group: &MoqGroupConsumer, container: MoqContainer) -> Result<Arc<Self>, MoqError>.
    • MoqError::AlreadyCommitted now also covers a group.
    • MoqTrackConsumer::recv_group documents the max_delay skip.
  • rs/moq-mux: behavior only, no signature change.
    • container::GroupConsumer returns an untimed video frame without waiting for its successor.
    • Frame::duration is filled from a duration marker only when that marker has already landed.
  • py/moq-rs: media.TrackConsumer, media.GroupConsumer(group, container), media.GroupConsumer.fetch.
  • go/wrapper: media.NewTrackConsumer / TrackConfig, media.NewGroupConsumer(group, container), media.FetchGroup / GroupConfig.
  • swift: Media.TrackConsumer, Media.GroupConsumer(group:container:).
  • kt, dart: media typealiases/typedefs TrackConsumer, GroupConsumer, TrackConfig, GroupConfig; dart/moq_ffi is regenerated.
  • cpp: moq::MediaTrackConsumer, moq::MediaGroupConsumer, moq::MediaTrackConfig, moq::MediaGroupConfig; cpp/obs and the C++ interop client follow.
  • moq-c has no media consumer handle, so there is nothing to mirror there.
  • Docs: doc/lib/{py,go,swift,kt,dart,cpp}, doc/setup/upgrade.md, py/moq-rs/README.md, go/wrapper/README.md, kt/README.md; quests m1/ffi-shape (records the naming rule) and m2/ffi-crate-split.

Validation

  • just check on the branch (Rust, Python, Kotlin, Go, Dart, C++ as affected).
  • Swift is covered by CI.
  • The new a_live_video_frame_is_not_held_for_its_successor test fails without the look-ahead fix.

Follow-ups

  • MoqMediaContainerStreamProducer is also a 5-word generated name (media.ContainerStreamProducer in the wrappers). The import side's naming is out of scope here.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

…reads

MoqMediaContainerConsumer gains recv_group(), returning container-decoded
groups in arrival order with none skipped. The raw track's lazy
arrival/sequence commitment becomes a generic Cursor that both handles
share, so mixing recv_group() with next() on one consumer fails with
AlreadyCommitted. Mirrored in the Python, Swift, Go, and Kotlin wrappers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 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-10-13T18:40:49.621819Z 8e69e0c Manual request
ℹ️ 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.

@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You'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 22 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b6ce74f1-969e-4b1f-b4c0-79e38a47413d
📥 Commits

Reviewing files that changed from the base of the PR and between 038127f and ac4d115.

📒 Files selected for processing: 43
  • cpp/moq/include/moq/moq.hpp
  • cpp/moq/test/doc-samples.hpp
  • cpp/obs/src/moq-source.cpp
  • cpp/obs/test/moq-output-test.cpp
  • dart/moq/lib/media.dart
  • dart/moq_ffi/lib/src/moq.dart
  • doc/lib/cpp/index.md
  • doc/lib/dart/index.md
  • doc/lib/go/index.md
  • doc/lib/kt/index.md
  • doc/lib/py/index.md
  • doc/lib/swift/index.md
  • doc/setup/upgrade.md
  • go/wrapper/README.md
  • go/wrapper/bridge.go
  • go/wrapper/internal/bridge/handles.go
  • go/wrapper/media/media.go
  • go/wrapper/moq_test.go
  • go/wrapper/subscribe.go
  • kt/README.md
  • kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/media/Media.kt
  • kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/SmokeTest.kt
  • py/moq-rs/README.md
  • py/moq-rs/docs/index.md
  • py/moq-rs/moq/media.py
  • py/moq-rs/moq/subscribe.py
  • py/moq-rs/tests/test_local.py
  • py/moq-rs/tests/test_server.py
  • quest/m1/ffi-crate-split.md
  • quest/m1/ffi-shape/README.md
  • rs/moq-ffi/src/audio.rs
  • rs/moq-ffi/src/consumer.rs
  • rs/moq-ffi/src/error.rs
  • rs/moq-ffi/src/ffi.rs
  • rs/moq-ffi/src/media.rs
  • rs/moq-ffi/src/test.rs
  • rs/moq-mux/src/container/group.rs
  • rs/moq-mux/src/container/mod.rs
  • swift/Sources/Moq/Broadcast.swift
  • swift/Sources/Moq/Media.swift
  • test/interop/clients/cpp/main.cpp
  • test/interop/clients/go/main.go
  • test/interop/clients/python/interop.py
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kixelated kixelated left a comment

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.

Automated review by review (OpenAI)

Reviewed commit: fccfe9e

The shared commitment cursor and per-group API are a sensible direction, but the reused readers do not yet provide the advertised lossless, immediate arrival observation:

  • [P2] Preserve arrivals across the subscriber's drift filter (rs/moq-ffi/src/media.rs:1115–1116). Subscriber::recv_group() still calls poll_stale and attaches GroupExpiry (rs/moq-net/src/model/track.rs:3956–3962); the default subscription budget is zero. On a timed track, queue several stamped groups before the next receive and only the latest is returned. A previously returned stalled group can also expire as newer content advances. Thus measurement silently misses the delayed arrivals it is supposed to observe. Provide an explicit observation path/budget policy that preserves received groups and their pending frames, and test a queued backlog plus a read pending on an older group while a newer group arrives. If ordinary subscription expiry is intentional, narrow the public promise and document the required budget instead of claiming none are skipped.

  • [P2] Avoid fetched-group look-ahead for live CMAF reads (rs/moq-ffi/src/media.rs:1118–1121). The reused GroupConsumer waits for a successor or FIN when a video sample has no duration (rs/moq-mux/src/container/group.rs:48–52). CMAF can legally decode a final sample without a duration, as the existing decode_without_duration_reports_none test demonstrates. A live single-sample fragment therefore remains withheld until another fragment or group completion, inflating measured arrival latency and potentially waiting indefinitely on a stalled group. Add an immediate-delivery mode for live groups while retaining look-ahead for fetched groups, and test an unfinished CMAF group containing one duration-less sample.

Verification: static review of all changed files and relevant cursor, decoder, task/cancellation and binding code. No builds or tests run; CI was still pending/running when checked.

(Written by OpenAI)

@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: fccfe9ec30

ℹ️ 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".

Comment thread rs/moq-ffi/src/media.rs Outdated
Comment on lines +1118 to +1120
Arc::new(MoqMediaContainerGroupConsumer::from_inner(
group,
self.container.clone(),

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 Decode CMAF groups from the embedded timeline

When a CMAF frame has a synthetic or shifted transport timestamp, such as an untimed stream forwarded over lite-05, this constructs the ordinary GroupConsumer; fmp4::Wire::poll_read passes frame.timestamp to decode, which anchors samples to that transport timestamp instead of the fragment's absolute tfdt. Consequently, recv_group() can report relay send time rather than container media time, defeating the latency and jitter measurements this API promises. Use an arrival-order CMAF decoding path that derives the absolute timeline from the fragment.

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.

Declined. The moq-lite frame timestamp is the CMAF track's broadcast timeline by design: fmp4::decode anchors the fragment's earliest sample to it, and tfdt only places samples relative to each other, so a publisher can move a passthrough track to another timeline without rewriting the payload. next() and fetch decode the same way, and anchoring arrival reads to tfdt would put them on a different timeline from decode-order reads of the same track. An untimed frame (any session below moq-lite-05) has no transport timestamp and already decodes at tfdt.

(Written by Claude Opus 5.5)

Comment thread go/wrapper/media/media.go Outdated
Comment on lines +424 to +427
// RecvGroup returns the next decoded group in arrival order (possibly out of sequence),
// or (nil, nil) when the track ends. No group is skipped, and frame timestamps come from
// the container rather than the transport.
func (m *ContainerConsumer) RecvGroup(ctx context.Context) (*ContainerGroupConsumer, error) {

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 Document arrival-order reads in every changed binding

The new public method changes how users consume media, but only the Dart and Kotlin entry pages were updated. I checked doc/lib/py/index.md, doc/lib/go/index.md, and doc/lib/swift/index.md; they still present only decode-order consumption even though this commit changes all three wrappers. Update the corresponding binding documentation so the new path does not ship undiscoverably.

AGENTS.md reference: rs/moq-ffi/AGENTS.md:L9-L14

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.

Fixed in 71f5f37: doc/lib/{py,go,swift,kt,dart} each describe arrival-order reads with that binding's group constructor, as do the Python, Go, and Kotlin READMEs. The API is now a constructor over a raw group (media.DecodeGroup in Go) instead of RecvGroup on the media consumer.

(Written by Claude Opus 5.5)

kixelated and others added 3 commits October 12, 2026 18:46
Replace MoqMediaContainerConsumer::recv_group with a MoqMediaContainerGroupConsumer::new(group, container) constructor that takes over a raw MoqGroupConsumer, mirroring moq_mux::container::GroupConsumer::new. Arrival order comes from the raw track's recv_group, so the media consumer, the shared cursor, and the moq-mux Clone derives go back to how they were on main.

container::GroupConsumer no longer waits on its look-ahead: an untimed video frame returns as soon as the next wire frame is not already there, so a live group adds no latency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title feat(ffi): recv_group on the media consumer, for arrival-order media reads feat(ffi): decode a raw group as media, for arrival-order reads Oct 13, 2026
@kixelated

Copy link
Copy Markdown
Collaborator Author

Re the OpenAI review of fccfe9e:

  • Drift filter and group expiry (P2): narrowed rather than changed. The subscription's max_delay decides which groups arrive and how long a stalled one stays readable, as moq-net's recv_group documents. The new constructor and MoqTrackConsumer::recv_group now say so, and no doc promises that none is skipped. A budget-exempt observation mode would be a new moq-net subscriber policy for one consumer; that's a quest if a caller needs it beyond a generous max_delay.
  • Look-ahead latency (P2): fixed in 71f5f37. container::GroupConsumer peeks only at what has already landed, so a live group returns an untimed frame immediately and a fetched group still gets its durations. a_live_video_frame_is_not_held_for_its_successor fails without the fix. It uses legacy video, which takes the same path as a duration-less CMAF sample, since kind() gates the hold.

(Written by Claude Opus 5.5)

kixelated and others added 2 commits October 12, 2026 20:35
…erGroup fetches one

The Go media package mirrors Rust: the constructor is the type's New, and the fetch says so. The media package is unreleased, so this breaks no published caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

@codex review

The head e53b5f7 reshapes the API: a constructor that decodes a raw group replaces recv_group on the media consumer. It also renames the Go constructors.

(Written by Claude Opus 5.5)

@kixelated kixelated left a comment

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.

Automated review by review (OpenAI)

Reviewed commit: e53b5f7

Both P2 findings from my previous review are addressed:

  • The arrival-order contract now explicitly retains the subscription's max_delay skipping and stalled-group expiry policy.
  • GroupConsumer returns buffered media when look-ahead is pending, removing the successor/FIN wait. The added live-video regression exercises that shared path using legacy video; it is not a dedicated duration-less CMAF fixture.

The constructor over an unread raw group is a simpler direction than the earlier shared-cursor API. The subsequent Go rename consistently separates NewContainerGroupConsumer from FetchContainerGroup across implementation, test usage, and documentation.

No new actionable findings in the reviewed changes. Compared against current main to separate inherited changes from this PR.

Verification: static GitHub review only; no builds or tests run, and CI results were not independently verified in this pass.

(Written by OpenAI)

@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: e53b5f7b0e

ℹ️ 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".

Comment thread rs/moq-ffi/src/media.rs
Comment on lines +1242 to +1243
#[uniffi::constructor]
pub fn new(group: &MoqGroupConsumer, container: MoqContainer) -> Result<Arc<Self>, MoqError> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Document the raw-group constructor for C++

The new UniFFI constructor is also generated as part of the public C++ moq::MediaContainerGroupConsumer surface, but the current head leaves doc/lib/cpp/index.md showing only sequence-ordered MediaContainerConsumer::subscribe. As a result, C++ users looking for the new arrival-order path have no feature documentation or example showing TrackConsumer::recv_group followed by this constructor. Update the C++ guide alongside the other binding guides.

AGENTS.md reference: AGENTS.md:L100-L102

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.

Fixed in d661af7. doc/lib/cpp/index.md now says that MediaContainerConsumer reads in decode order, and that arrival-order reads decode each group from TrackConsumer::recv_group with moq::MediaContainerGroupConsumer::init(group, container). That matches the generated signature, and just cpp check passes.

(Written by Claude Opus 5.5)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

@codex review

The head d661af7 adds the C++ guide line for the raw-group constructor, on top of e53b5f7.

(Written by Claude Opus 5.5)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: d661af7840

ℹ️ 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".

kixelated and others added 3 commits October 13, 2026 09:49
The media consumers drop the 5-word Container names. They reuse the net
nouns they decode, like media.TrackProducer, and the layer namespace tells
them apart: MoqMediaContainerConsumer becomes MoqMediaTrackConsumer,
MoqMediaContainerGroupConsumer becomes MoqMediaGroupConsumer, and their
configs become MoqMediaTrackConfig and MoqMediaGroupConfig. Go's
constructors become NewTrackConsumer, NewGroupConsumer, and FetchGroup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

@codex review

The head 3975261 renames the media consumers per the maintainer's pick: MoqMediaContainerConsumer to MoqMediaTrackConsumer, MoqMediaContainerGroupConsumer to MoqMediaGroupConsumer, and their configs to MoqMediaTrackConfig / MoqMediaGroupConfig, across moq-ffi, every wrapper (media.TrackConsumer, media.GroupConsumer), C++, docs, and interop clients. It also merges main.

(Written by Claude Opus 5.5)

@kixelated kixelated left a comment

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.

Automated review by review (OpenAI)

Reviewed 3975261, comparing with previous reviewed e53b5f7 and accounting for merged base 1998d34.

No new actionable findings. The media.TrackConsumer / media.GroupConsumer rename is consistently carried through Rust, language wrappers, generated Dart bindings, callers, tests and docs. Explicit qualification in Python, Swift and Kotlin keeps raw-group and media consumers distinguishable. This is a clearer API without adding another abstraction.

The previous max_delay contract and nonblocking look-ahead fixes remain intact. The live-video regression still exercises the shared path through legacy video rather than a dedicated duration-less CMAF fixture.

Static GitHub review only; no builds or tests run, and CI was not independently verified.

@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: 3975261e29

ℹ️ 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".

let next = match self.format.poll_read(&mut self.group, waiter)? {
Poll::Ready(next) => next,
Poll::Pending if self.pending.is_empty() => return Poll::Pending,
Poll::Pending => break,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve duration look-ahead for fetched video groups

When MoqMediaGroupConsumer::fetch resolves against an in-progress Legacy or LOC video group, this branch returns the currently buffered frame as soon as the next wire frame is pending. The eventual endpoint marker can therefore no longer backfill that returned frame's duration, even though fetch_group explicitly permits groups that are still receiving frames; only already-finished fetched groups retain durations. Keep the old look-ahead behavior for the fetch path, or otherwise distinguish it from the new live arrival-order path.

AGENTS.md reference: rs/moq-mux/AGENTS.md:L6-L6

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.

Declining. This is the trade the PR already settled under "Look-ahead latency": never wait on look-ahead, and don't add a live/fetched mode flag to GroupConsumer.

No binding loses anything, because MoqMediaFrame carries no duration across the FFI (only payload, timestamp_us, keyframe). A finished fetched group still gets its durations backfilled. The only case that changes is a Rust caller fetching a Legacy/LOC video group that is still being received, and even then only the newest frame's duration. That frame's endpoint isn't known yet anyway, so a caller can take it from the next frame's timestamp, the way the decode-order consumer does.

(Written by Claude Opus 5.5)

…a-read

# Conflicts:
#	doc/lib/cpp/index.md
#	doc/lib/py/index.md
#	go/wrapper/subscribe.go
#	py/moq-rs/README.md
#	py/moq-rs/moq/subscribe.py
#	swift/Sources/Moq/Broadcast.swift
@kixelated

Copy link
Copy Markdown
Collaborator Author

@codex review

The head 8e69e0c merges main (#5367 catalog track references, #5288, #5405, #5421) into the rename. Conflicts were doc and comment text in doc/lib/{cpp,py}, py/moq-rs, go/wrapper/subscribe.go, and swift/Sources/Moq/Broadcast.swift. Each took main's wording (the rendition's track, or its catalog key) with the media.TrackConsumer / media.GroupConsumer names. dart/moq_ffi is regenerated from the merged library.

(Written by Claude Opus 5.5)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 8e69e0cd50

ℹ️ 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".

…a-read

# Conflicts:
#	quest/m1/ffi-crate-split.md
@kixelated
kixelated added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 85c0daa Oct 13, 2026
32 of 33 checks passed
@kixelated
kixelated deleted the feat/ffi-arrival-media-read branch October 13, 2026 19:58
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