feat(cli): add moq play - #2697
Conversation
Co-authored-by: Codex <noreply@openai.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0dcea4f259
ℹ️ 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".
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe CLI adds a feature-gated 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches✨ Simplify code
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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
rs/moq-cli/src/play.rs (2)
561-582: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMove the WGSL shader into its own file.
The shader is embedded as a Rust string literal with
\n\line continuations. That form is easy to break during edits, and no editor tooling highlights or validates it. Put the shader inrs/moq-cli/src/play.wgsland load it withinclude_str!, or at minimum use a raw string literal.♻️ Proposed change
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("moq play shader"), - source: wgpu::ShaderSource::Wgsl( - "struct VertexOutput {\n\ - ... - }" - .into(), - ), + source: wgpu::ShaderSource::Wgsl(include_str!("play.wgsl").into()), });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rs/moq-cli/src/play.rs` around lines 561 - 582, Move the embedded WGSL source from the shader module construction in play.rs into a new play.wgsl file, preserving the existing shader behavior and contents. Replace the Rust string literal with include_str! referencing that file, while leaving the surrounding device.create_shader_module configuration unchanged.
39-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winShare the catalog-format fallback with
SubscribeArgs.
Args::catalog_formatrepeatsSubscribeArgs::catalog_formatinrs/moq-cli/src/subscribe.rslines 152-157 exactly. This PR already extractedSelectArgs::selectionfor the same reason. Move the fallback into one helper, for example a free function or an inherent method onCatalogFormatArg, and call it from both.♻️ Proposed shared helper
Add to
rs/moq-cli/src/subscribe.rs:/// Resolve the catalog format: the explicit flag, then the broadcast name /// suffix, then the default. pub(crate) fn catalog_format(arg: Option<CatalogFormatArg>, broadcast: &str) -> CatalogFormat { arg.map(Into::into) .or_else(|| CatalogFormat::detect(broadcast)) .unwrap_or_default() }Then in
rs/moq-cli/src/play.rs:impl Args { fn catalog_format(&self, broadcast: &str) -> CatalogFormat { - self.catalog_format - .map(Into::into) - .or_else(|| CatalogFormat::detect(broadcast)) - .unwrap_or_default() + crate::subscribe::catalog_format(self.catalog_format, broadcast) } }As per coding guidelines: "extend existing primitives, and generalize rather than duplicate helpers."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rs/moq-cli/src/play.rs` around lines 39 - 46, The catalog-format fallback is duplicated between Args::catalog_format and SubscribeArgs::catalog_format. Extract the shared resolution logic into one helper, such as a crate-visible function or CatalogFormatArg method, then update both methods to call it while preserving the explicit argument, broadcast detection, and default fallback order.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@rs/moq-cli/src/main.rs`:
- Around line 127-148: Move `play` command dispatch out of the
`#[tokio::main]`-driven `run_play` path so `play::run` executes on the process
main thread. Keep the Tokio runtime available for the asynchronous setup in
`run_play`, but ensure `winit::EventLoop::build()` and `run_app()` invoked by
`play::run` never run on a Tokio worker thread.
In `@rs/moq-cli/src/play.rs`:
- Around line 165-169: Update the playback task-joining loop around
tasks.join_next() and the post-loop media join so it drains every task before
returning, while returning early only when a joined task reports an error. Add a
regression test that starts two playback tasks, completes one first, and
verifies the loop remains active until the second completes.
- Around line 176-208: Update the rendition loops in the playback flow so
failures from source.resolve are handled like Consumer::new failures: log a
warning with the rendition name and error, then continue trying subsequent
renditions. Apply this change to both the video and audio loops, while
preserving the existing successful resolution and decoder behavior.
- Around line 337-340: Update the next_redraw assignment in the play loop to use
checked_add when adding the computed duration to Instant::now(). Preserve the
existing clock and next_timestamp matching, but return None when checked_add
overflows so hostile or corrupt timestamps cannot panic.
---
Nitpick comments:
In `@rs/moq-cli/src/play.rs`:
- Around line 561-582: Move the embedded WGSL source from the shader module
construction in play.rs into a new play.wgsl file, preserving the existing
shader behavior and contents. Replace the Rust string literal with include_str!
referencing that file, while leaving the surrounding device.create_shader_module
configuration unchanged.
- Around line 39-46: The catalog-format fallback is duplicated between
Args::catalog_format and SubscribeArgs::catalog_format. Extract the shared
resolution logic into one helper, such as a crate-visible function or
CatalogFormatArg method, then update both methods to call it while preserving
the explicit argument, broadcast detection, and default fallback order.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d4cf0e47-4fd8-4af1-b0cd-4a2297681eb7
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
doc/bin/cli.mdrs/moq-cli/Cargo.tomlrs/moq-cli/README.mdrs/moq-cli/src/args.rsrs/moq-cli/src/main.rsrs/moq-cli/src/play.rsrs/moq-cli/src/subscribe.rs
ReviewNice shape overall: the winit/wgpu app is self-contained, the feature gate keeps the default binary clean, and factoring Blocking1. CI is red on
I verified locally on macOS that the code is in fact fine:
For the advisory itself, two options:
Either way it'd be good to land the fix and let CI actually compile the feature once, since Linux is the only platform CI covers and the winit x11/wayland paths are exactly what my macOS run didn't touch. 2. The first playback track to finish tears down the whole window. In result = tasks.join_next(), if !tasks.is_empty() => {
return joined(result.expect("guarded by is_empty"));
}and after the loop: let result = tasks.join_next().await;
joined(result.context("all playback tracks stopped")?)
Draining the Correctness3. Rendition selection is one-shot, which undercuts the "follows catalog updates" claim.
4. let mut decode = moq_audio::decode::Config::new();
decode.latency_max = Some(args.latency_max);then let samples = frame.data.len() / size_of::<f32>() / channels as usize;
...
format: moq_audio::Format::F32,Both the sample-count math and the sink layout assume 5. Audio drives a full GPU present per decoded frame.
6. Nothing renders until the first media frame arrives.
Structure / docs7. 8. 9. The main-thread invariant is load-bearing and unstated. Nits
(written by Opus 5) |
Address review on #2697. - Unignore CI: winit's default `wayland-csd-adwaita` pulls sctk-adwaita -> ab_glyph -> ttf-parser, which is RUSTSEC-2026-0192 (unmaintained, no upgrade). It only parses the system font for Wayland client-side decorations, so ignore the advisory rather than dropping the feature and losing a titlebar on GNOME. cargo-deny runs ahead of every compile step in `just rs ci`, so this was also what stopped CI from ever building the feature. - `media()` returned on the first task to finish, and a track ending returns `Ok(())`, so a publisher that ended video tore down the window while audio was still playing. Drain the JoinSet instead and stop once every track that started has ended; disarm the catalog branch once it ends so a `None` stream can't spin. - Ask the decoder for f32 explicitly. Both the sink layout and the sample-count behind the audio clock assumed it, via `Format::default()`. - Wake the render loop on the first audio frame only. Every 20ms frame was requesting a redraw, which repainted the same picture ~50 times a second on an audio-only broadcast. - Draw once from `resumed()` so the window is black while the broadcast resolves instead of showing undefined surface contents. - Hoist the MoQ consume-side wiring out of `run_export`/`run_play` into `spawn_moq_consume`. - Docs: `--audio-codec` gained `pcm`, the selection flags now serve `play` too, and the play section overstated how long catalog updates are followed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed CI. Added One track ending no longer tears down the window. Audio decoder is asked for f32 explicitly ( Audio wakes the render loop once, on the Docs: Structure: hoisted the duplicated MoQ consume-side wiring out of Left alone deliberately: re-selecting a rendition mid-stream when the catalog swaps one out. That's a real feature rather than a fix, and the doc no longer claims it. Verified locally on macOS:
(written by Opus 5) |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@doc/bin/cli.md`:
- Around line 134-138: Update the `play` catalog-selection description to say it
waits for any playable rendition, starts audio and video independently, and
continues following catalog updates until each available media role has started.
Preserve the existing explanation of alphabetical selection, name overrides,
clock behavior, and playback completion.
- Around line 436-437: Update the CLI documentation around the rendition option
guidance and the `play` example to distinguish syntax by subcommand: keep
rendition flags before the sink subcommand for stdout exports, but document them
in the correct position for `play` as shown by its actual command syntax. Ensure
the guidance and example are consistent.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 37fd9c9f-8df8-4bdd-90fe-d72e6fc1bf15
📒 Files selected for processing (5)
deny.tomldoc/bin/cli.mdrs/moq-cli/src/main.rsrs/moq-cli/src/play.rsrs/moq-cli/src/subscribe.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- rs/moq-cli/src/subscribe.rs
- rs/moq-cli/src/main.rs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3c9342014e
ℹ️ 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".
Address bot review on #2697. - A rendition whose `broadcast` reference is unroutable took the whole player down, while an unsupported codec on the same rendition only warned. Warn and fall through to the next rendition either way. - Reject `--video-codec vp8|vp9` and `--audio-codec aac` before dialing. The selection flags are shared with the exports, which pass bytes through, so playback would otherwise filter the catalog down to a rendition its decoders can't open and sit on a blank window. - `Instant::now() + duration` panics rather than saturating, and the duration comes from a wire timestamp. Use `checked_add`; no deadline just means the next frame waits for a media wakeup. - Document the Linux install as `--no-default-features`: `play` enables moq-video, which the default `pipewire` feature then wires up for display capture, so `cargo install moq-cli --features play` wanted libpipewire and libclang for a backend playback never touches. - Docs: the play section claimed it waits for both roles two lines above saying single-role broadcasts work, and the export section claimed the selection flags go before the subcommand, which is only true for the stdout sinks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
rs/moq-cli/src/play.rs (2)
176-183: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winGroup the media inputs in a private context type.
mediahas six positional parameters. Create a private role-specific context struct and pass it as one parameter. This keeps the call site and future pipeline changes coherent.As per coding guidelines: "Refactor awkward internal shapes while making changes: replace functions with four or more arguments or repeated value groups with structs or reusable primitives, and avoid copied one-off helpers."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rs/moq-cli/src/play.rs` around lines 176 - 183, Introduce a private role-specific context struct containing the six inputs currently accepted by media, then update media to receive that struct as its single parameter and adjust all call sites to construct and pass it. Preserve the existing field types and behavior while grouping the media pipeline state coherently.Source: Coding guidelines
219-251: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winAdd regression tests for both recovered failure paths.
Add a test where the first rendition fails to resolve and a later rendition starts playback. Add a deterministic test that uses an overflowing redraw duration and verifies that scheduling returns no deadline without a panic. The current listed tests do not cover either behavior.
As per coding guidelines: "Before fixing a bug, reproduce and explain the root-cause mechanism, fix the lowest layer containing the cause, and add a regression test that fails without the fix."
Also applies to: 394-398
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rs/moq-cli/src/play.rs` around lines 219 - 251, Add regression coverage for both recovery paths in the relevant playback tests: verify that when the first video or audio rendition fails during source.resolve, a later rendition is still attempted and starts playback, and verify that an overflowing redraw duration causes scheduling to return no deadline without panicking. Reproduce each failure deterministically, then place the fix at the lowest layer responsible for the overflow while preserving the existing rendition fallback behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@rs/moq-cli/src/play.rs`:
- Around line 176-183: Introduce a private role-specific context struct
containing the six inputs currently accepted by media, then update media to
receive that struct as its single parameter and adjust all call sites to
construct and pass it. Preserve the existing field types and behavior while
grouping the media pipeline state coherently.
- Around line 219-251: Add regression coverage for both recovery paths in the
relevant playback tests: verify that when the first video or audio rendition
fails during source.resolve, a later rendition is still attempted and starts
playback, and verify that an overflowing redraw duration causes scheduling to
return no deadline without panicking. Reproduce each failure deterministically,
then place the fix at the lowest layer responsible for the overflow while
preserving the existing rendition fallback behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2b90fa18-aeba-4bd4-939c-698f2064aa6c
📒 Files selected for processing (4)
doc/bin/cli.mdrs/moq-cli/src/args.rsrs/moq-cli/src/main.rsrs/moq-cli/src/play.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- rs/moq-cli/src/main.rs
- rs/moq-cli/src/args.rs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 854bc5a2f5
ℹ️ 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".
`media` called `Source::broadcast()` as soon as it was spawned, which lands on `origin::Consumer::request_broadcast`. That fails `Unroutable` on the spot when no session has registered a Dynamic handler yet (see `dynamic_request_unroutable_without_handler` in moq-net), and `run_play` spawns this task immediately after kicking off the reconnect loop, so it beats the handshake essentially every time rather than only under load. Wait for the broadcast to be announced first, like `run_stdout` already does. Doing it inside `media` rather than in `run_play` keeps the window up during the wait, so it reads as a black frame with a working close button instead of a process that appears hung. `run` now takes the `origin::Consumer` and builds the `Source` at the point of use, since both the wait and the source need it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed The startup race is real, and worse than "a race". Undecodable codec selectors are rejected up front. The selection flags are shared with the stdout exports, which pass bytes through and so accept every codec the catalog can name. Also in these two commits:
Not done, deliberately: switching renditions mid-stream when the catalog replaces the one being played. Naively re-selecting on track end spins, because the catalog still lists the rendition and we resubscribe to a track that immediately ends again. Doing it properly means diffing snapshots against the playing rendition and aborting that role's task, which is adaptive-bitrate work rather than a fix for this PR. The docs now state the limitation. Verified locally on macOS: (written by Opus 5) |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
rs/moq-cli/src/play.rs (1)
99-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace positional playback state with a context type.
Use an internal context struct to group playback inputs and shared state. This prevents argument-order mistakes and removes repeated parameter groups.
rs/moq-cli/src/play.rs#L99-L103: Grouporigin,broadcast,args, andnetworkinto a playback request or runner type.rs/moq-cli/src/play.rs#L161-L168: Pass the context instead of six positional values.rs/moq-cli/src/play.rs#L177-L184: Reuse the same context formedia.As per coding guidelines: "Refactor awkward internal shapes while making changes: replace functions with four or more arguments or repeated value groups with structs or reusable primitives."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rs/moq-cli/src/play.rs` around lines 99 - 103, Introduce an internal playback context type in rs/moq-cli/src/play.rs to group origin, broadcast, args, and network, then update run at lines 99-103 to accept that context instead of positional inputs. At lines 161-168, pass the context rather than six separate values, and at lines 177-184, reuse the same context when invoking media.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@rs/moq-cli/src/play.rs`:
- Around line 185-194: In the play test suite, add a regression test for delayed
matching announcements: hold the announcement until after playback is requested,
assert that the media future remains pending, then announce the broadcast and
assert playback starts successfully. Exercise the existing origin/session setup
and announcement APIs around Source::new and announced_broadcast so the test
fails when Source::broadcast runs before registration.
---
Nitpick comments:
In `@rs/moq-cli/src/play.rs`:
- Around line 99-103: Introduce an internal playback context type in
rs/moq-cli/src/play.rs to group origin, broadcast, args, and network, then
update run at lines 99-103 to accept that context instead of positional inputs.
At lines 161-168, pass the context rather than six separate values, and at lines
177-184, reuse the same context when invoking media.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2c7dc746-a708-4155-bbca-390920d9802c
📒 Files selected for processing (2)
rs/moq-cli/src/main.rsrs/moq-cli/src/play.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- rs/moq-cli/src/main.rs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 07b97e04a4
ℹ️ 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".
- Every decoder is gated by platform and cargo feature (no AV1 without nvdec, no VideoToolbox off macOS), so the codec flags can't be validated against the build up front. Instead, a snapshot whose renditions all fail to start with nothing already playing is now an error naming each rejection, rather than a blank window that waits forever. - `Sink::write` never blocks and drops what won't fit, so a burst delivered faster than real time lost samples. Wait when the sink is more than a second ahead of the speaker, and play the buffer out at EOF instead of dropping the sink on top of it. The drain is bounded, since a device that never opens reports a full queue forever. - Reaching decoder EOF is no longer the same event as Ctrl-C: `Ended` lets the queued video present before the loop exits, while `Finished` still stops now. - `media` grew past six parameters, so it's a `Media` struct with a `run`. Docs: a Linux build needs libclang and the V4L2 headers whatever features are picked, because moq-video's camera capture isn't behind one. Putting it behind a feature is the real fix and belongs in its own change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed A catalog this build can't play now says so. The earlier The tail plays out. V4L2: confirmed, documented, not fixed here. Also folded in the "six parameters" note from the out-of-diff review: Verified locally on macOS: clippy Still unverified anywhere: the Linux x11/wayland build of (written by Opus 5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6b5bafac6
ℹ️ 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".
- `CurrentSurfaceTexture::Lost` closed the window with an error. A display change or a resume can produce it, and wgpu documents the recovery: rebuild the surface from the instance, then configure it. Keep the instance around so we can. `Validation` stays fatal. - The announce-wait from the previous commit had no regression test. Split it out as `subscribe` and cover it: without the wait, subscribing to an unannounced broadcast resolves `Unroutable` immediately (asserted directly, so the mechanism is pinned too); with it, the future parks until the broadcast is announced. Drop the wait and the test fails. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pacing wait only looked at what was already buffered, so an empty sink accepted any single write whole. An Opus packet caps at 120ms, but a PCM one only has to be sample-aligned, so a frame longer than the sink's ring buffer had its tail dropped. The clock then advanced by the frame's full duration against a `buffered()` that never held those samples, putting video permanently ahead of audio. Write at most a second at a time, re-checking headroom between chunks, which keeps the sink under two seconds and inside its own ceiling. Chunks are cut on the sample stride, so alignment is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Pushed
CI still hasn't run. The only run this branch has produced since the deny fix is for (written by Opus 5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c224cda665
ℹ️ 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".
… clock Pacing audio to real time turned the video queue's overflow policy into a bug. Dropping the oldest frames was fine while audio was written as fast as it decoded, because the clock ran ahead and those frames were genuinely late. Now the clock advances at 1x, so a burst of more than 30 frames loses its earliest ones while their timestamps are still in the future, blanking the window until the clock reaches whatever survived. Make the decoder wait for room instead: the presenter signals each pop, and the presentation clock is anchored to the wall clock, so the queue always drains. Also request the redraw a rebuilt surface still owes. `Lost` (and `Outdated`) recover without presenting, and nothing else asks again: a stalled live stream has no next frame to trigger one, and an ending stream exits first. Bounded, since the retry is what schedules the next attempt; past the ceiling the frame is written off with a warning rather than failing playback. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 55fafa5d5f
ℹ️ 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 last commit lumped `Timeout` in with `Occluded` and returned `Shown` for both, but they differ. A timeout means the swapchain was busy, not that there was nothing to draw on: the frame has already been popped and rendered, and nothing else will ask for it, so a stalled live stream stays blank and an ending one exits without its last frame. Route it through the same bounded retry as `Outdated` and `Lost`. `Occluded` still doesn't retry, since being shown again is itself a redraw. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`decode::Consumer` hardcoded `container::legacy::Wire` on both crates, so it read every track as a varint timestamp followed by a codec payload. `moq import fmp4` publishes `Container::Cmaf`, where each frame is a whole moof+mdat fragment, so the parse produced garbage rather than failing: the first frame reached the H.264 decoder as a malformed access unit and died with "annexb: truncated length-prefixed NAL unit". `catalog::hang::Container` already exists for exactly this, dispatching Legacy, Cmaf and Loc at runtime with a `TryFrom<&hang::catalog::Container>`, and the catalog entry is already a parameter here. Use it. This is why `moq play` could not play anything published through `import fmp4`, including the project's own bbb.mp4 demo. It hits moq-transcode's decode path the same way; only the legacy wire worked there too. Verified end to end rather than by a unit test, which would need a live broadcast and a platform decoder: a local relay, `import fmp4` from ffmpeg (H.264 + Opus), and `moq play` rendering through VideoToolbox with CoreAudio output for ~18s, no overflow or underflow, clean exit on window close. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Ran it locally against a real relay, and it found the thing eight rounds of static review didn't: Repro: local Root cause:
What the run then confirmed, which nothing in this PR had been able to before:
Scope note: this last commit touches moq-video and moq-audio rather than moq-cli. No public signature changes (the field is private, No automated regression test for it: reproducing needs a live broadcast plus a platform decoder. The per-wire parsing itself is already covered in moq-mux. (written by Opus 5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 17c51d2bad
ℹ️ 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".
|
CI red here is not from this branch. Everything else on (written by Opus 5) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: da2fec3777
ℹ️ 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".
Co-authored-by: Luke Curley <luke.curley@discordapp.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 38ae42982b
ℹ️ 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 container fix had no test of its own: I verified it by playing a live broadcast, which is not something CI can do. This covers it at the level the bug lived at, publishing one real Opus packet through the CMAF wire and decoding it back through `decode::Consumer`. The assertion is the decoded sample count, not just "some PCM came out". Reading a moof+mdat as legacy framing hands the codec a slice of the moof, which Opus still decodes, just to a shorter buffer: 336 samples instead of 1296. An emptiness check passes either way, so it would not have caught this. Complements the LOC test alongside it: that one covers a different wire, and CMAF is the one `moq import fmp4` actually publishes. No equivalent for moq-video. Its decode consumer opens a platform backend and would need a real encoded access unit to drive, where Opus is pure Rust and this crate can encode its own input. The two crates took the identical change, so this pins the behavior; the video side stays covered by the end-to-end run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`py/moq-rs/pyproject.toml` went to 0.4.2 in the version bump, but `uv.lock` still pinned 0.4.1, so a locked sync disagrees with the workspace. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`play` enables `moq-audio/playback`, which is cpal, which links ALSA on Linux. The dependency note listed only the libclang and V4L2 packages, so following it on a clean Debian box still failed to build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b9456d0f8b
ℹ️ 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".
Only the `cargo install` line carried `--no-default-features`, so the build and run commands right below it still pulled the default `pipewire` feature and wanted libpipewire on Linux. All three now match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # doc/bin/cli.md # rs/moq-audio/src/decode/consumer.rs
`moq play` sets a `latency_max` field that no longer exists: #2688 renamed it to `latency` and retyped it as `Latency`, while #2697 added this call site against the old shape. Neither PR could see the other, and `dev` never runs the `--all-features` build outside a PR, so it landed red and now fails Check on every PR that targets it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
playfeature with a top-levelmoq ... playcommand.Public API changes
playCLI command and PCM as an audio rendition selector.Test plan
nix develop --command just fixnix develop --command cargo nextest run -p moq-cli --features playnix develop --command cargo clippy -p moq-cli --features play --all-targets -- -D warningsnix develop --command just checkCross-package sync
rs/moq-cli/README.mdanddoc/bin/cli.md.Related to #2481.
(Written by GPT-5)