Repository navigation
feat(gst): moqsrc follows referenced broadcasts' announcements - #6394
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add moq_mux::Follow (Source::follow, Follow::new), built on announced_broadcast: each instance serving a sibling is handed out as it starts or takes over, and its end is reported. A reference naming an epoch follows only its own instance (best effort over epochless routes). moqsrc pumps follow their sibling: the first start binds the rendition, an end idles its pad, and another instance restarts the stream on the same pad. Exporters bind a sibling once it is first announced instead of failing, and end the rendition when that instance goes. @moq/watch drops a rendition whose broadcast proves another epoch from its effective catalog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Delete the quest, point sink-restart-follow at moq_mux::Follow, and size the now-childless broadcast epoch epic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Public Source::bind, resolve, and subscribe_track keep answering for the routes as they stand: HLS answers per request and moq play picks renditions inline, so neither can wait on a sibling that left. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…och/reference-announcements # Conflicts: # quest/m0/broadcast-epoch/reference-announcements.md # quest/m1/reference-epoch-bindings.md # quest/m1/sink-restart-follow.md
…ollow::*
Source::bind, resolve, and subscribe_track wait for a sibling nothing
announces yet, through routed_broadcast, instead of failing Unroutable.
A binding answers its pinned instance once the path is pinned, so a
sibling that announced and then left answers its ended broadcast rather
than waiting for the next. moq-hls drops its own re-issue loop, and its
disconnected-sibling test now names the sibling it means to bind.
Move Follow/Followed to moq_mux::follow::{Instances, Event}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed self-follow moqsrc renditions restarting on the same sibling instance now share that instance's timeline, so audio and video fix one segment mapping instead of each starting from its own first frame. Source::follow fails a self-reference whose epoch the catalog instance contradicts with EpochMismatch, rather than parking forever on a fixed feed no other instance can join. Binding and follow share one epoch check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… idles Every moqsrc rendition picks its sibling instance's timeline when it starts reading it, not only once it owns a pad, so one still subscribing when a replacement takes over shares the replacement's mapping. The first instance at a path keeps the run's timeline. A sibling track finishing while its instance stays up idles the pad and keeps following, so the instance that replaces it starts a new stream there; the finish record clears once a new instance starts on the pad. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A rendition the catalog delists drains the instance it reads, then ends its pad instead of idling for, or restarting on, the sibling's next instance. One already idle when the delist lands ends right away. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
moqsrc recorded a finished track by rendition and path, so a catalog update that replaced an idle sibling pump (a disable and enable, or an epoch handoff) skipped the rendition for every later instance, leaving its pad silent. The record now keeps the instance that finished, and the pump checks it once it knows which instance it reads, instead of reconcile skipping the rendition. A sibling pump idles past a finished instance, holding its pad if it has one; the catalog's own rendition still stops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # cpp/moq/MIGRATE.md # dart/moq/MIGRATE.md # dart/moq_ffi/MIGRATE.md # dart/moq_ffi/lib/src/moq.dart # go/ffi/MIGRATE.md # go/wrapper/MIGRATE.md # kt/moq-ffi/MIGRATE.md # kt/moq/MIGRATE.md # py/moq-ffi/MIGRATE.md # py/moq-rs/MIGRATE.md # quest/m0/broadcast-epoch/README.md # quest/m1/reference-epoch-bindings.md # rs/moq-c/MIGRATE.md # rs/moq-ffi/MIGRATE.md # rs/moq-ffi/src/consumer.rs # rs/moq-mux/MIGRATE.md # rs/moq-mux/src/container/source.rs # rs/moq-mux/src/source.rs # swift/MIGRATE.md # swift/ffi/MIGRATE.md
moqsrc keeps today's sibling handling: a rendition binds its sibling through `Source::bind`, which now waits for the first announcement and keeps that instance. Following a sibling's later instances moves to the moqsrc sibling follow quest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each pump follows its rendition's `moq_mux::follow::Instances`: Start binds, End idles the pad, and another instance restarts the stream on the same pad with a timeline shared by every rendition reading that instance. A finished sibling track idles, recorded per instance; a delisted rendition drains its instance and ends its pad. Split from #6254 with the code its review rounds covered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ExportSource treated any read error as the instance's end once the sibling closed, so malformed media followed by a close exported as a clean EOF. Only a transport error ends the rendition with its instance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A transport failure such as a protocol violation read after the sibling closes failed nothing: any moq-net error counted as the instance's end. Only an orderly shutdown (dropped, cancelled, closed, going away, or a cancelled session) ends the rendition; every other error fails the export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`Dropped` means a producer went away without finishing, so content is incomplete rather than ended. It no longer counts as an orderly end of the sibling instance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…enditions moqsrc kept one long-lived pump per rendition, each following its own broadcast's announcements, plus an `active` map the catalog loop diffed against. A pump that exited on its own stayed in `active` until pruned, so a relist landing in that window started nothing. Each broadcast the catalog references now gets one follower task that owns its instances (`Source::follow`) and its renditions' state: a start reads every rendition on that instance, an end idles them, a takeover restarts them on the same pads. A reader is a task per (rendition, instance), and its exit reaches the follower that owns it, so idle and draining are follower state rather than tasks. The pad table tracks which renditions the latest catalog lists, so a claim and a retirement decide under one lock, replacing the PumpState compare-exchange. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
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: 9776abfef4
ℹ️ 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 follower per (path, epoch) needed a run-wide registry to share an instance's timeline across followers, which kept every sibling path seen in the run and parked broadcast clones in it and in the finished records. One follower per path now follows every instance and checks each rendition's named epoch itself. It numbers its instances, gives the first the run's timeline and each later one its own, and records finished tracks by instance number, so it holds no broadcast it no longer reads and forgets a path once the catalog stops referencing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
1 similar comment
|
@codex review (Written by Claude Opus 5.5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e940e7ea3b
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2cb8230695
ℹ️ 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".
follow::Instances hands out the same instance again after it returns, and the follower treated it as a new one: a fresh serial dropped the record of a track it finished, so its last group replayed on the pad, and a fresh timeline remapped it. The follower keeps its last instance across an end. The same channel announced again keeps its readers; the same epoch served again after an end resumes with its serial and timeline. An epochless return after an end cannot prove its identity and still counts as new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f44597dc3
ℹ️ 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".
…pdate A sibling path the catalog stopped reading kept its follower, with its instance consumers, until the next catalog update or the end of the run. A follower now wakes the run when it runs out of renditions, and the run drops it once the plan it sent last is empty and no disabled rendition names the path, the same rule an update applies. A follower with no renditions left also ends once the catalog closes, since no plan can add any. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 46ea01cbfb
ℹ️ 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 follower kept the ended instance's consumer to recognize its return, which held the broadcast's state while the sibling stayed offline. It now keeps only the epoch, serial, and timeline across an end, and holds the consumer only while the instance serves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
1 similar comment
|
@codex review (Written by Claude Opus 5.5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c2bfc01f0e
ℹ️ 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".
…ck switch Two ways a finished sibling track replayed its final group on its pad: - An instance a rendition pins finished its track, another instance won the path, and the pinned one won it back with a new serial. The follower now keeps the serial and timeline of each instance a rendition pins, by epoch, next to the current one, so its return resumes them. - A rendition finished one track, then another on the same instance, then switched back. The finished records now hold every track per instance rather than only the last. Records stay bounded by the instances the follower keeps and the tracks they served on listed pads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 93fad07b51
ℹ️ 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".
…tance Keeping every track a rendition finished let a publisher grow the records without bound by renaming a rendition's track and finishing each one. Keep only the last finished track per rendition and kept instance, which the catalog bounds. A rendition switched back to a track it finished before another replays that track's last group, which is the accepted cost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ce3862a7e
ℹ️ 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 final snapshot and the catalog's close could reach a follower as one plan, and a closed plan started no reader, so a rendition the snapshot added or reshaped never played. The final plan now starts exactly the renditions it added or reshaped, and still restarts no reader that already ended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
|
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 41 seconds. 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 |
#6254 landed on main. Conflicts in the docs and quest files keep this branch's side, which describes moqsrc following siblings as done, and the completed moqsrc-sibling-follow quest stays removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Completes quest/m0/broadcast-epoch/moqsrc-sibling-follow.md:
moqsrcfollows a catalog-referenced sibling's announcements throughmoq_mux::follow, restarting the rendition on the same pad for each instance, never past a named epoch.Builds on moq-dev/moq#6254 (
moq_mux::follow, the waitingSource::bind), now onmain; this diff is the moqsrc part split from it. It lands before sink restart follow, which then moves the catalog broadcast's own follow onto the same follower.Design: one follower per referenced path
The maintainer chose a follower per referenced sibling path over per-rendition pumps. The catalog loop no longer keeps per-rendition tasks or an
activemap.Run (
follow_catalog): on each catalog update it lists the renditions on the pad table and groups them by the path their reference resolves to. It then sends each group to that path's follower as a plan over awatch. A path no enabled rendition reads any more gets an empty plan. Its follower stays reachable while a disabled rendition names the path or a delisted one drains, so a re-enable or relist reaches the same follower and its finished records. Once it plays nothing, it wakes the run, which lets it go unless the last plan sent names a rendition or a disabled rendition holds the path. Only the run drops a follower, so no plan is lost to a follower exiting on its own. A follower with no renditions left also ends once the catalog closes. A follower whose broadcast failed is followed again on the next update.Follower: one task per referenced path. It owns:
follow::Instances, following every instance (the catalog's own broadcast is the single fixed instance);Its behavior:
Reader: one task per (rendition, instance). It subscribes, claims the rendition's pad, and streams until its track ends, its instance goes, or the follower cancels it. Its exit goes back to the follower that owns it. Readers stay separate tasks so a push blocked downstream never stalls another rendition.
Pads: the pad table records which renditions the latest catalog lists.
PumpStatecompare-exchange.Whether a rendition is reading, idle, or draining is now follower state, not a task. So the held CodeRabbit Major (discussion_r4345973572: a pump that returned but was not yet joined sat in
active, so a relist in that window started nothing) cannot happen. A relist always reaches the follower that owns the rendition.One window remains: a delist and a relist that the plan
watchfolds into one update while a reader goes to claim its pad. The claim reports that the rendition was unlisted, and the follower starts the reader again if the catalog lists it by then.Earlier moqsrc sibling-state findings, under the new design
finishedkeyed by path silenced later instances after a reshape (discussion_r4345884091, discussion_r4345959760): the follower records the serving instance's number, and each reader checks it for its own instance. No broadcast clone is held (discussion_r4354840075).Docs
doc/bin/gstreamer.mdanddoc/concept/hang.mdsay moqsrc follows sibling announcements. The quest is deleted, and the epoch epic carries its own size again.Public API and wire
Tests
a_sibling_rendition_relisted_while_draining_does_not_replay_its_finish(new, discussion_r4355432451).a_sibling_no_rendition_reads_does_not_outlive_its_catalog(new, discussion_r4355614370).a_rendition_delisted_while_subscribing_takes_no_pad_until_relisted(new relist step).a_relisted_sibling_rendition_follows_the_next_instance(new).a_late_sibling_starts_its_rendition_and_returns_after_an_end,a_reannounced_sibling_instance_does_not_replay_its_finish(new, discussion_r4355481216), anda_sibling_is_followed_past_its_catalogs_close(new; it also checks that the catalog's broadcast ending lets go of the sibling, discussion_r4355345886, discussion_r4355392186).a_closing_catalog_plays_the_rendition_its_final_snapshot_adds(new).a_pinned_instance_winning_its_path_back_does_not_replay_its_finish(new).a_rendition_joining_a_sibling_instance_shares_its_timeline(new; it fails when a reader gets its own timeline)a_finished_sibling_track_returns_on_the_next_instancea_delisted_sibling_rendition_follows_no_later_instancea_reenabled_sibling_rendition_plays_the_next_instance_after_a_finish(now also checks that the re-enable replays nothing on the finished instance, discussion_r4355125383)an_epoch_handoff_after_a_sibling_finish_plays_the_new_instanceplan_reconcileset-math tests,a_pump_either_goes_live_or_is_cancelled, and theSiblingsregistry test, along with the code they covered.Checks
cargo test -p moq-gst --lib source: 58 passed, three runs in a row.cargo clippy -p moq-gst --all-targets: clean.just check, except its flake module, which fails here building thegithub-runnerVM image (unshareis not permitted in this environment; unrelated to this change).just test interop --all(no wire, FFI, or gateway change).🤖 Generated with Claude Code
(Written by Claude Opus 5.5)