Skip to content

fix(moq-gst): stop moqsrc panicking on backwards timestamps; globally unique pad ids#1646

Merged
kixelated merged 2 commits into
mainfrom
fix/moqsrc-ts-underflow-pad-id
Jun 7, 2026
Merged

fix(moq-gst): stop moqsrc panicking on backwards timestamps; globally unique pad ids#1646
kixelated merged 2 commits into
mainfrom
fix/moqsrc-ts-underflow-pad-id

Conversation

@kixelated
Copy link
Copy Markdown
Collaborator

Follow-up to #1633 (review findings #1 and #2).

1. PTS-underflow panic (the important one) 🟠

build_buffer pins its PTS reference to the first decoded frame and computes frame.timestamp - reference. Frames arrive in decode order, so a B-frame's presentation timestamp can fall before the reference. Timestamp's Sub is checked_sub(..).expect("time overflow") — it panics on underflow (moq-net/src/model/time.rs:281). That crashes the pump task and, because the panic happens before the pad-removal cleanup, leaks the pad and kills the rendition. Open-GOP / leading-B-frame content triggers it.

Fix: checked_sub and clamp to ClockTime::ZERO instead of crashing.

2. Pad-id counter was per-session 🟡

next_pad_id reset to 0 on every run_session, despite the doc claiming a "process-unique counter". A rapid Paused -> Ready -> Paused restart could mint video_0 while the previous session's cancelled pump was still removing its video_0 (two same-named pads on the element). Promote it to a process-wide AtomicU64 so it actually matches the doc and the collision window is gone.

Test

Added a throwaway publisher that sends a keyframe at ts=2000ms then frames at ts=1000ms+ (the backwards-timestamp case):

build result
before panicked at time.rs:281: time overflow after 1 buffer; pad leaked
after streams all 201 buffers, clean EOS, no panic

Real H264 decode (cdn.moq.dev/demo bbb.hang) still decodes (20/20 frames). clippy + fmt --check clean.

Deliberately not included (noted in the #1633 review as lower priority): blocking pad.push() on tokio workers (situational), redundant CMAF init parsing per catalog update (minor perf), and reconcile unit tests.

(Written by Claude)

🤖 Generated with Claude Code

… unique pad ids

Two follow-ups from the #1633 review:

- build_buffer pinned its PTS reference to the first decoded frame and computed
  `frame.timestamp - reference`. Frames arrive in decode order, so a B-frame's
  presentation timestamp can precede the reference; `Timestamp`'s `Sub` panics on
  underflow (`checked_sub(..).expect`), which crashed the pump task and leaked its
  pad, killing the rendition. Open-GOP / leading-B content triggers this. Use
  `checked_sub` and clamp to zero instead.

- The pad-id counter was per-session (reset to 0 each run_session) despite the
  "process-unique" doc. A quick Paused->Ready->Paused restart could mint `video_0`
  while the previous session's cancelled pump was still removing its own `video_0`.
  Promote it to a process-wide `AtomicU64`.

Verified with a publisher that sends a keyframe at ts=2000ms then frames at
ts=1000ms+: unfixed build panics after 1 buffer; fixed build streams all 201 and
EOSes cleanly. Real H264 decode (bbb.hang) still works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 7, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d0b29ae-1846-4e86-9876-ac5c162ed883

📥 Commits

Reviewing files that changed from the base of the PR and between f26e276 and 720c83d.

📒 Files selected for processing (2)
  • rs/moq-gst/Cargo.toml
  • rs/moq-gst/src/source/imp.rs
💤 Files with no reviewable changes (1)
  • rs/moq-gst/Cargo.toml

Walkthrough

This PR refactors pad ID allocation to use a process-wide AtomicU64 (removing the per-session next_pad_id), updates reconcile call sites and allocation to use NEXT_PAD_ID.fetch_add(Ordering::Relaxed), introduces relative_pts(timestamp, reference) using checked_sub and clamping to gst::ClockTime::ZERO, updates build_buffer to call relative_pts, adds unit tests for relative_pts, and tweaks rs/moq-gst Cargo.toml to set doctest = false and remove test = false.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main changes: fixing a panic on backwards timestamps and ensuring globally unique pad IDs.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the motivation, technical details, and testing approach for both fixes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/moqsrc-ts-underflow-pad-id

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
rs/moq-gst/src/source/imp.rs (1)

533-539: ⚡ Quick win

Add an in-file regression test for the backward-PTS clamp path.

This fix looks correct, but a small unit test here would lock in the no-panic underflow behavior.

As per coding guidelines, "Add tests where they're easy to write; Rust tests are integrated within source files".

🤖 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-gst/src/source/imp.rs` around lines 533 - 539, Add a small in-file
unit test that exercises the backward-PTS clamp path so the checked_sub
underflow returns gst::ClockTime::ZERO rather than panicking: create a test in
imp.rs (#[cfg(test)] mod tests) that constructs a frame with a timestamp smaller
than the reference used in the match (exercise the code path using
frame.timestamp.checked_sub(reference) in the surrounding function), call the
function or the minimal public/private helper that performs the subtraction, and
assert the result equals gst::ClockTime::ZERO and that no panic occurs; keep the
test self-contained using dummy/frame builders present in the file or
constructing minimal types needed.

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.

Nitpick comments:
In `@rs/moq-gst/src/source/imp.rs`:
- Around line 533-539: Add a small in-file unit test that exercises the
backward-PTS clamp path so the checked_sub underflow returns
gst::ClockTime::ZERO rather than panicking: create a test in imp.rs
(#[cfg(test)] mod tests) that constructs a frame with a timestamp smaller than
the reference used in the match (exercise the code path using
frame.timestamp.checked_sub(reference) in the surrounding function), call the
function or the minimal public/private helper that performs the subtraction, and
assert the result equals gst::ClockTime::ZERO and that no panic occurs; keep the
test self-contained using dummy/frame builders present in the file or
constructing minimal types needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e4aaeeb6-262c-41f7-a1e9-2da0ae2568b9

📥 Commits

Reviewing files that changed from the base of the PR and between 4fe88d0 and f26e276.

📒 Files selected for processing (1)
  • rs/moq-gst/src/source/imp.rs

Extract the relative-PTS math into `relative_pts` and add an in-file unit test
asserting a timestamp before the reference clamps to zero (the B-frame /
decode-order case) instead of panicking, plus that a forward delta is preserved.

The lib had `test = false`, which kept `#[cfg(test)]` code from compiling at all;
drop it so the test runs (via `cargo test -p moq-gst` -- moq-gst is excluded from
default-members, so the normal `cargo test` run is unaffected). Revert that one
line if you'd rather keep the crate test-free.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kixelated
Copy link
Copy Markdown
Collaborator Author

Addressed the nitpick in 720c83d: extracted relative_pts and added an in-file unit test for the backward-PTS clamp (asserts a pre-reference timestamp yields ClockTime::ZERO instead of panicking, and that a forward delta is preserved). The lib had test = false, which kept #[cfg(test)] from compiling, so I dropped it; the test runs via cargo test -p moq-gst (moq-gst is excluded from default-members, so the normal cargo test is unaffected). Easy to revert that one line if you’d rather keep the crate test-free. (Written by Claude)

@kixelated kixelated merged commit cb0271c into main Jun 7, 2026
1 check passed
@kixelated kixelated deleted the fix/moqsrc-ts-underflow-pad-id branch June 7, 2026 18:51
@moq-bot moq-bot Bot mentioned this pull request Jun 7, 2026
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