Repository navigation
fix(mux): bound what the decode clock holds in MKV and TS export - #6417
Conversation
Quest-Claim: fcb4e1c52a746f51
A video track whose PTS stopped advancing past the reorder lookahead in an open group held every frame in the decode clock, outside the jitter buffer's backpressure, and a flush then drained it in quadratic time. The frame held longest now settles once it has waited, by the latest arrival read, longer than its lookahead plus the export's delay, so what the clock holds stays within what the source sends in that time. The lookahead is clamped to MAX_REORDER in the clock, which TS's catalog jitter skipped. Slots are a VecDeque and each slot/frame pair bounds the delay once, so a drain is linear. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EeyiEy5FWz3z44DYZVo7vU
|
Outcome: reproduced unbounded clock retention (500/500 frames held on flat PTS, MKV and TS) and quadratic flush (100k frames 3.8 s). Fixed by settling the frame held longest once it has waited longer than lookahead + export delay by arrival, plus a linear drain (100k: 4.2 ms). No open decisions. just check passed. (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. |
|
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 43 minutes. 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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e74d92e339
ℹ️ 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".
|
Coordination with moq-dev/moq#6554, which removes (Written by Claude Opus 5.5) |
# Conflicts: # quest/m1/release-audit/README.md # rs/moq-mux/src/container/decode.rs # rs/moq-mux/src/container/ts/export.rs
Main removed `export ts --sync` (#6554), so the counted clock, its tests and the sync docs go; the declared clock and its legs test stay, the latter on a B-pyramid Live re-added to the timing harness. Drop the inferred-clock-bound follow-up quest, which #6417 delivers, and unblock encoder-reorder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Started from
quest/m1/release-audit/export-reorder.md, which main has since folded into Decode order (moq-dev/moq#6390), so this PR no longer deletes a quest. moq-dev/moq#6453, that quest's PR, keeps the inferred clock for renditions without a declaredreorderand files its bound asquest/m1/release-audit/inferred-clock-bound.md: this PR is that bound.MKV and TS export run reordered video through
DecodeClockto author DTS. The clock sits upstream of the jitter buffer, sojitter.fullbackpressure never saw it. If PTS stopped advancing past the lookahead in an open group (flat, or running backward), every frame stayed in the clock. Flushing then drained it in quadratic time.Reproduced at the exporter level with a live H.264 track (120 ms catalog jitter, 100 ms delay, 20 ms frames, 500 frames), using
buffered()extended to count clock-held frames:Fix (in
rs/moq-mux/src/container/decode.rs):max(lookahead, reach), clamped toMAX_REORDER) plus the export's delay past it.DecodeClock::settlethen returnsSettle::Front, andpopsettles that frame along with whatever is already due. A live source's PTS keeps pace with its arrival, so the clock holds at most what the source sends in that window. That is the same time-times-ingress bound the jitter buffer uses. No timer, no frame count.popnow takesSettle { Due, Front, All }in place offlush: bool.Frontsettles only the front slot, not every held pair, so a hostile stream cannot force a full rescan on each frame.slotsis now aVecDeque, and aboundedcursor records which slot/frame pairs already bound the delay. Each pair is scanned once and the drain is linear. Outputs are unchanged: every existing clock and exporter test passes.MAX_REORDERitself. TS's catalog-jitterpath had skipped that clamp (MKV already clamped).Measured with the new
decode_clockbench (nightlyrust-extra), swept over held frames x PTS shape x payload size:Payload size (64 B vs 4 KiB) does not change drain time. The clock moves
Byteshandles, so retained memory is held frames x payload, and that is what the exporter tests bound.Tests:
mkv::export_test::stalled_presentation_time_keeps_the_export_boundedandts::export_test::stalled_presentation_time_keeps_the_export_bounded: flat and backward PTS in an open group. Each asserts that the export holds no more than what arrives within lookahead + delay.mkv::export_test::live_reordered_video_keeps_every_frame: a live B-pyramid keeps every frame.Behavior changes for malformed input
jitterabove 2 s now waits at most 2 s (MAX_REORDER), as MKV already did.Decisions
reordermeans 0 and deletes the timestamp-settled clock, while moq-dev/moq#6453 keeps that clock for undeclared renditions and files this bound as a follow-up.reorderper group in TS, MKV and FLV #6453); feat(mux)!: clock a declared catalogreorderper group in TS, MKV and FLV #6453 dropsinferred-clock-bound.mdwhen it merges main. (Chosen by the maintainer.)reorderper group in TS, MKV and FLV #6453 merges and delete that quest here.reorderper group in TS, MKV and FLV #6453 returns to absent-means-0.(Written by Claude Opus 5.5)
Public API / wire impact
None. All changes are
pub(crate)insidemoq-mux. No wire change.Consumers: CLI MKV and TS export.
Checks
export ts --sync(moq-dev/moq#6554): this PR'spop_countedanddiscardhunks are dropped with it.just check:rspassed. Theflakemodule fails locally building the runner VM image (unshare: write failed /proc/self/uid_map, a host user-namespace limit, unrelated to this diff); CI runs it.cargo nextest run -p moq-mux: 1669 passed.quest check --base origin/main: passed.cargo bench -p moq-mux --bench decode_clock: ran;sh/gh/nightly.test.pypasses with the new bench target.🤖 Generated with Claude Code
https://claude.ai/code/session_01EeyiEy5FWz3z44DYZVo7vU