Merge main into dev - #4214
Merge main into dev#4214
Conversation
…4127) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s it (#4104) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…count (#4142) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4123) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…section (#4089) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: moq-bot[bot] <186640430+moq-bot[bot]@users.noreply.github.com>
…ks (#4137) Co-authored-by: Brad Greenway <brad.greenway@surveillx.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4125) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
An IETF live join delivers nothing below the group its SUBSCRIBE_OK names as Largest, so declare that as the copy's start. A parked track's warm cache waiting on the copy then sees the upstream moved past it and live readers skip it, as they already do on lite-06+. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
## Problem `moq play`'s media tasks (`rs/moq-cli/src/play/media.rs`) sit behind the `play` feature and talk straight to a cpal `Engine` and a winit `EventLoopProxy`. CI only compiled them in the nightly clippy run, so regressions like #3966's rendition-switch gap were measured by hand with a real speaker. ## Approach - New private seam `play::output`: `Output` (the window's wake handle plus a speaker opener), `Speaker`, and `Sink`. Production implements them on `EventLoopProxy<Event>`, `playback::Engine`, and `playback::Sink`; `Media`, `play_video`, `play_audio`, and `drain` are generic over them. No other production change beyond reading `tokio::time::Instant` (the wall clock unless a test pauses it) and passing `drain` the depth it already knew. - `play::fake::Recorder` (test-only) stands in for both: its sink primes its latency in silence, drains in real time, re-pads on underflow the way `fixed-resample`'s ring does, and cuts what it holds when dropped. It records each write's sounding interval on tokio's clock. - `an_audio_rendition_switch_leaves_no_gap` drives the real `Media` task over an in-process origin: a PCM rendition is retired for a replacement mid-stream at a 500 ms delay, on a paused clock. It asserts the silence between the old tail and the new first sample is under one audio chunk (it measures ~10 ms, the partial period `drain` leaves). Reverting #3966's fix (draining inside `play_audio` before returning) fails it with a 500 ms gap. - CI: `just rs play` runs `nextest -p moq-cli --features play`. `test-changed` (the per-PR Test job), `check-changed` when testing (local `just check`), and the unscoped `check --all` / `ci test` run it whenever moq-cli is selected, which includes any change to moq-audio, moq-video, or moq-mux. The dev shell already carries the ALSA build dependency. - Completes and deletes `quest/m1/play-harness.md`; `play-tunein-backpressure.md` is now unblocked and notes that it owns the fake presenter for the video queue. ## Impact - No public API or wire changes. Everything is private to the `moq` binary's `play` feature. - The per-PR Test job compiles moq-cli a second time with `play` (wgpu, winit, cpal) when moq-cli is selected. ## Alternatives - Making `media.rs` compile without `play`, so the default test pass covers it. The tasks need `moq-video`/`moq-audio` decoders, which are optional deps of the CLI on purpose; the quest chose the feature-enabled test pass instead. - A callback for the wake instead of a trait. The repo prefers handles over callbacks, and the speaker opener needs a type anyway. ## Follow-ups - `drain` returns once 10 ms or less is buffered and then drops the sink, which removes it from the mix, so every retired rendition and every finite track loses up to its last 10 ms. The test's tolerance absorbs it; worth a quest if it is audible. - The harness has no fake presenter for the video queue yet; `play-tunein-backpressure` adds it with its regression test. (Written by Claude Opus 5.5) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4129) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: moq-bot[bot] <186640430+moq-bot[bot]@users.noreply.github.com>
…cks (#4192) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4195) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Grok 4.7 <noreply@x.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
origin.rs polls a track query through kio::Task, and the mock holds uni streams in a Mutex. Both imports were on one side of the merge and the call sites on the other.
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. |
|
MERGE Branch sync: Positive improvement? Yes. Worth the complexity? Yes for a 37-commit sync. The PR body names every conflict and the chosen side. Spot-checks match that write-up:
Different approach? Rebase would rewrite history already on CI was still pending at review time; the description claims This is an automated review, not the maintainer's decision |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4106fa7c3
ℹ️ 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".
| if self.grace.poll(waiter).is_ready() { | ||
| return Poll::Ready(()); |
There was a problem hiding this comment.
Keep waiting for streams whose headers already arrived
When an IETF PUBLISH_DONE arrives after a group or fill handler has decoded its header but before it finishes reading the first object, Counted has not yet incremented Tail::streams(). This branch nevertheless settles after one second, allowing the caller to run track.finish() with a final sequence below the unopened group; when the handler resumes, opening that valid final group is rejected as closed and its payload is lost. Track active handlers separately and apply the grace only to streams whose headers never arrived.
Useful? React with 👍 / 👎.
| pub fn publish_binary_snapshot( | ||
| &self, | ||
| name: String, | ||
| config: MoqBinaryConfig, | ||
| ) -> Result<Arc<MoqBinarySnapshotProducer>, MoqError> { |
There was a problem hiding this comment.
Mirror the binary publishers into every wrapper
This adds a new UniFFI publishing surface, but the ergonomic Python, Go, Swift, Kotlin, and Dart packages and their binding docs do not add the corresponding config, producer handles, or BroadcastProducer methods. In particular, Python, Go, and Swift hide the generated MoqBroadcastProducer behind wrappers with no forwarding method, so consumers of those public packages cannot use this feature at all. Mirror the new surface across the required wrappers and documentation in this change.
AGENTS.md reference: AGENTS.md:L94-L96
Useful? React with 👍 / 👎.
Merges
main(1f2e40c4a, 37 commits) intodev.Conflicts and how they were resolved
doc/lib/rs/moq-auth.md: both. Dev'sKey::decode::<T>stays. Main's fix(auth): read and write legacy put/get token grants #4190 legacyput/getread and write stays, because the mergedClaims/Scopewire already does that. The old "retired fields fail verification" sentence described the pre-fix contract.quest/m1/README.md: kept dev's origin-mount quest. Dropped reconnect-test-ports (finished on main in test(moq-tokio): bind reconnect and worker tests to their own ports #4127; the quest file is deleted) and decoded-frames (finished on dev in feat(ffi)!: decoded video frames own their surface #4094). Kept main's new test-flakes quest.quest/m2/js-discontinuity.md: modify/delete. Kept main's file. Dev deleted it in feat(hang)!: name the container producer's timeline break discontinuity() #4141 after the rename, and main's test(capture): drive native capture through clock edge cases in CI #4125 added the still-open requirement that an omitted end must not write a cadence-estimated duration marker. JSdiscontinuity()still estimates. Restored thequest/m2/README.mdlink the auto-merge dropped with the deletion.rs/moq-net/src/ietf/subscriber.rs: main'sPoll<Result<Ended, Error>>(the track tail) with dev'swaiter.context(). The caller already matches onEnded::GroupandEnded::Track.rs/moq-net/src/lite/publisher.rs: main'sAtomicU64/Orderingimports for the lite-07 stream count. DroppedContext; every call site already useswaiter.context().rs/moq-net/src/model/origin.rs: main'sgroupimport plus dev'suse kio::Task.Pollablewas merged intoTaskon dev (refactor(kio)!: trim the surface and merge Pollable into Task #4156). The import stays because a track query is polled through the trait.rs/moq-net/tests/support/mock.rs: not a marker, a bad auto-merge. Dev had droppedMutexfrom the import; main's held uni streams still use it. Restoredstd::sync::Mutex.rs/moq-relay/src/connection.rs: dev's deletion. Did not restoreauthorizeorGrants. Hidden cluster peers already go throughCluster::scope(with_hidden(request.tls.is_some() || Cluster::is_lan_path(...))). Main's only edit since the last sync was the fix(net)!: ship lite-07 as moq-lite-07-wip, off by default #4148 rename in that comment, soCluster::scopenow saysmoq-lite-07-wip.API / wire impact
None beyond what each side already carries.
Cluster::scopeis unchanged except the comment naming the hidden opt-inmoq-lite-07-wip.Verification
just checkagainstorigin/dev(full suite;justfileis in the diff).(Written by Grok 4.7)