Skip to content

refactor(mux): TS and fMP4 export lifecycles hold one phase each - #8297

Merged
kixelated merged 5 commits into
mainfrom
quest/m1/explicit-state/mux-gst
Nov 9, 2026
Merged

kixelated merged 5 commits into
mainfrom
quest/m1/explicit-state/mux-gst

Conversation

@kixelated

@kixelated kixelated commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

Closes the quest quest/m1/explicit-state/mux-gst.md (deleted here, with its link in the explicit-state epic).

Problem

moq-mux's TS and fMP4 export lifecycles were stored as flags, Options, and side sets. Their combinations stood for phases, and .expect() calls or call order kept the invalid ones out (the shapes REVIEW.md's State check flags). No confirmed bug was folded in. The quest's two suspected bugs had already been checked and found not to be defects.

Approach

Each lifecycle is now one enum whose variants carry their own data:

  • fMP4 export track (fmp4/export.rs): Phase { Reading, Ended, Departed(Resume), Stopped }, with a Subscription { source, described } replacing source/described/finished/resume. A Buffer { Empty, Open(Batch), Closed(Batch) } replaces buffer/buffer_independent/group_finished.
  • fMP4 export: Init { Waiting(queued), Written { first_late, queued } } replaces init_emitted, queued, and the first_late_id = u32::MAX marker. Feed { Live(S), Ended, Stopped } replaces catalog: Option<S> + stopped.
  • TS export program (ts/export.rs): Feed { Live, Ended, Retired(left) } replaces catalog_ended, retired, and left, and resume now resets one value instead of six fields. Two dead if catalog_ended { return Ok(false) } return Ok(false) branches are gone.
  • TS export track: Read { Live, Ended, Withdrawn, Stale } replaces finished + withdrawn and the program's stale: HashSet<String>. The "gone" predicate now reads !cataloged && matches!(read, Ended | Withdrawn) && <nothing left to go out>.
  • TS Follower: refused is now Serving::Refused, which the next instance change overwrites.
  • TS Passthrough: Clock { Unseen, Seen { timebase, open, queue } }, so objects exist only once a PCR can time them, and the "an object follows a PCR" expect is gone. Groups { Waiting(since), Writing(Group) } replaces group + waiting_since, and the "the first object starts a group" expect is gone.
  • GroupBuffer (container/consumer.rs): Content { Empty, Marker, Bare { ended }, Media } replaces empty/media/bare/ended.

Choices made without asking:

  • The TS program keeps catalog: Option<S> with its catalog_format expect. An expired follow must drop the old catalog subscription (a_cancelled_replacement_after_the_follow_can_follow_again pins this), but a selected program always holds a catalog. Folding the catalog into Feed would bring the same Option back.
  • Passthrough::write treats an object written before any group as starting one (Start::Fallback) instead of panicking. The path is unreachable: only a group-starting object opens before the first group.
  • A Stale listed track that rebind moves now leaves Stale. Before, it was unfinished but still listed in stale.
  • moq-dev/moq#7916 no longer touches fmp4/export.rs (only its test), so the fMP4 part did not wait for it.

Impact

None: every change is crate-private. No public API or wire change.

Alternatives

  • Separate Listing state for the TS track's listed + parked: dropped. parked already empties the moment a track is listed, and the enum would touch about 20 sites for no reachable bad state.
  • psi: Option<Psi> phases of the TS program: left alone. Its expect("checked above") calls are local borrow splits, not phase guards.

Follow-ups

None.

Models

Size: M
Implementer: Claude Code, claude-opus-5-5, medium effort
Implementer: Claude Code, claude-opus-5-5, medium effort (iterate, M)

(Written by Claude Opus 5.5 with medium)

Review

Rewritten by quest review after each round.

  • Reviewer: Codex, gpt-6.1-sol, medium effort
  • Session: 01a1bc5a-330c-7591-80f7-552e35fe4363, round 3
  • Head: d104509
  • Base: origin/main at f102ee0
  • Score: 10/10, The only incremental change moves a documentation comment onto its intended helper, introducing no behavioral changes or new issues.
  • Findings: none

kixelated and others added 3 commits November 8, 2026 09:25
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fMP4 export track's subscription is one Phase and its fragment run one
Buffer; the export's init and catalog are each one enum, replacing the
u32::MAX first-late marker. The TS export program's input is one Feed, each
track's read one Read (absorbing the program's stale set), the follower's
refusal a Serving variant, the passthrough's objects live inside its seen
clock, and a group buffer's content kind is one enum.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title TS and fMP4 export lifecycles refactor(mux): TS and fMP4 export lifecycles hold one phase each Nov 8, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review November 8, 2026 20:07
@coderabbitai

coderabbitai Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You'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 28 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7dfde1a9-02c4-4472-94f1-46a075e70eed
Commits

Reviewing files that changed from the base of the PR and between e80b79a and d104509.

Files selected for processing: 7

  • quest/m1/explicit-state/README.md
  • quest/m1/explicit-state/mux-gst.md
  • rs/moq-mux/src/container/consumer.rs
  • rs/moq-mux/src/container/fmp4/export.rs
  • rs/moq-mux/src/container/ts/export.rs
  • rs/moq-mux/src/container/ts/follower.rs
  • rs/moq-mux/src/container/ts/passthrough.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed head b0faaeb.

No actionable findings identified in the lifecycle refactor. Checked fMP4 buffering, departure/rejoin and stop paths; TS retirement, withdrawal/resubscription and PID replacement; follower refusal handling; passthrough PCR/group state; and consumer marker transitions. The previously reported double-close frame-loss issue is fixed: Buffer::close preserves Closed batches, with a focused regression test.

Verification: GitHub-only static inspection of the diff and supporting source. Builds, tests and live media playback were not run.

@pullfrog pullfrog Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ Minor suggestion only — one misplaced doc block.

Reviewed changes

  • Quest done: deletes quest/m1/explicit-state/mux-gst.md and its line in the epic README's ## Required, with no other tree references left behind.
  • fMP4 export lifecycle: the track is one Phase { Reading, Ended, Departed, Stopped } and its fragment run one Buffer { Empty, Open, Closed }; the export's catalog feed and init segment are Feed and Init, removing first_late_id = u32::MAX.
  • fMP4 close fix: Buffer::close preserves an already-Closed batch and end preserves non-Reading phases, with closing_a_closed_buffer_keeps_its_frames covering the double close.
  • TS export lifecycle: Feed { Live, Ended, Retired } replaces the program flags and left, and Read { Live, Ended, Withdrawn, Stale } absorbs finished, withdrawn, and the program's stale set; resume, resubscribe, rebind, retire, list, and the "gone" predicate all map cleanly onto the new transitions.
  • TS follower, passthrough, and group buffer: Serving::Refused, Clock/Groups, and Content { Empty, Marker, Bare, Media } each collapse a flag set into one variant, and the two .expect() phase guards in Passthrough are gone.

The old flag assignments in the base were checked one by one against their new transitions, including the deliberately changed Stale listed track that rebind moves to Live (in practice resubscribe handles it first, so the state is unreachable). Every change is crate-private, so the body's Impact: None holds.

One note outside the code: b0faaebc6 has no completed Check run (the refactor's run on 70c108b8d was cancelled by the new push, and the head's run is skipped), so this head is not CI-verified.

Comment thread rs/moq-mux/src/container/fmp4/export.rs Resolved
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Re-reviewed the delta since the prior Pullfrog review (b0faaebc6..d10450904), a single comment-only commit.

  • group_start doc restored: the /// block orphaned by the new double-close test now sits directly above fn group_start in rs/moq-mux/src/container/fmp4/export.rs, resolving the only prior finding. No behavior change.

The rest of the lifecycle refactor is unchanged from the previously reviewed head. Check was still running on d10450904 at review time, so CI has not yet confirmed this head.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

@kixelated
kixelated added this pull request to the merge queue Nov 9, 2026
Merged via the queue into main with commit bb93e13 Nov 9, 2026
6 checks passed
@kixelated
kixelated deleted the quest/m1/explicit-state/mux-gst branch November 9, 2026 02:31
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