Skip to content

moq-mux: harden the scte35_inject fixture generator#1696

Merged
kixelated merged 2 commits into
mainfrom
claude/scte35-inject-harden
Jun 12, 2026
Merged

moq-mux: harden the scte35_inject fixture generator#1696
kixelated merged 2 commits into
mainfrom
claude/scte35-inject-harden

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

Hardens the scte35_inject example (the SCTE-35 fixture generator) against the issues CodeRabbit flagged. The SCTE-35 feature itself merged via #1685; this is a follow-up to the example tool only.

  • main returns anyhow::Result<()>; every unwrap/expect/panic becomes a ? with .context(), so routine failures print a clean error instead of a backtrace.
  • Allocate the SCTE-35 PID by walking up from 0x0021 to the first PID the input program doesn't already use, instead of hardcoding 0x0021 and risking a collision with an existing ES. Fresh clips that leave 0x0021 free still land on it, so existing fixtures regenerate byte-identically.
  • Iterate full packets via chunks_exact(188) and reject inputs whose length isn't a multiple of 188, so pid_of never indexes a short trailing slice.
  • Force the cue insertion positions strictly increasing so two cues can't collapse onto one packet and get silently dropped by position().

Test plan

  • scte35_inject compiles.
  • Smoke-tested end-to-end: clean Error: output on bad args / unknown set; picks 0x0021 for fresh inputs (byte-compatible with prior behavior).
  • All 7 moq-mux SCTE-35 tests pass.
  • cargo clippy -D warnings and cargo fmt --check clean via nix.

(Written by Claude)

Address CodeRabbit review on the SCTE-35 fixture generator example:

- main returns anyhow::Result and every unwrap/expect/panic becomes a `?`
  with .context(), so routine failures print a clean error instead of a
  backtrace.
- Allocate the SCTE-35 PID by walking up from 0x0021 to the first PID the
  input program doesn't already use, instead of hardcoding 0x0021 and risking
  a collision with an existing ES. Fresh clips that leave 0x0021 free still
  land on it, so existing fixtures regenerate byte-identically.
- Iterate full packets via chunks_exact(188) and reject inputs whose length
  isn't a multiple of 188, so pid_of never indexes a short trailing slice.
- Force the cue insertion positions strictly increasing so two cues can't
  collapse onto one packet and silently drop via position().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated
kixelated enabled auto-merge (squash) June 12, 2026 19:24
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

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: b5ae91a4-0292-4cc0-9634-5c9732cd7c60

📥 Commits

Reviewing files that changed from the base of the PR and between d568672 and 6467728.

📒 Files selected for processing (1)
  • rs/moq-mux/examples/scte35_inject.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rs/moq-mux/examples/scte35_inject.rs

Walkthrough

Refactors the SCTE-35 injector to use anyhow for contextual errors and makes main return anyhow::Result. Replaces a fixed SCTE-35 PID with a scan-from-constant allocator that avoids collisions, validates TS packetization (188-byte chunks), decodes cues with context, enforces strictly increasing insertion indices (erroring if out-of-range), updates cue_packet to accept scte_pid and return Result, and writes augmented PMT and output TS with contextual I/O errors.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'moq-mux: harden the scte35_inject fixture generator' clearly and concisely summarizes the main change—hardening the SCTE-35 fixture injector example.
Description check ✅ Passed The description provides detailed context about hardening the scte35_inject example, including specific changes (fallible control flow, PID allocation, packet validation), testing done, and commits clarifying the work.
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 claude/scte35-inject-harden

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rs/moq-mux/examples/scte35_inject.rs (1)

89-100: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Continue scanning until both PAT and PMT are resolved.

Line 94 breaks on the first PMT payload, so PAT-late inputs can fail at Line 99 with a false “no PAT/PMT PID” error even though PMT is present. Capture PMT PID from the packet header when needed and only break once both values are known.

Suggested patch
 while let Some(pkt) = reader.read_ts_packet().context("reading TS packet")? {
 	match pkt.payload {
 		Some(TsPayload::Pat(pat)) => pmt_pid = pat.table.first().map(|p| p.program_map_pid),
 		Some(TsPayload::Pmt(pmt)) => {
+			pmt_pid.get_or_insert(pkt.header.pid);
 			orig = Some(pmt);
-			break;
 		}
 		_ => {}
 	}
+	if pmt_pid.is_some() && orig.is_some() {
+		break;
+	}
 }
🤖 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-mux/examples/scte35_inject.rs` around lines 89 - 100, The loop breaks
as soon as a PMT is seen, which can cause pmt_pid to still be None for inputs
where PAT arrives later; update the loop that iterates reader.read_ts_packet()
so it continues scanning until both pmt_pid and orig (the PMT) are known: when
encountering TsPayload::Pat set pmt_pid = pat.table.first().map(...), when
encountering TsPayload::Pmt set orig = Some(pmt) but do not break immediately —
instead also attempt to extract the PMT PID from the packet header if pmt_pid is
None (or simply continue looping until pmt_pid.is_some() && orig.is_some()),
then after the loop use pmt_pid.context(...) and orig.context(...).
🤖 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-mux/examples/scte35_inject.rs`:
- Around line 180-188: The current clamping logic using `let last =
packets.len().saturating_sub(1);` and setting `cue_at[i] = (cue_at[i - 1] +
1).min(last);` can produce duplicate `last` entries and cause later cues to be
dropped by `position()`; fix by ensuring all remaining cues get unique
decreasing positions when you hit the end: compute how many cues remain (e.g.
remaining = cue_at.len() - i) and, if `(cue_at[i - 1] + remaining) >= last`,
instead backfill the tail so cues occupy distinct indices ending at `last` (for
example set cue_at[j] = last - (remaining - (j - i)) for j in i..cue_at.len()),
otherwise continue the current strictly-increasing increment logic; update
references to `cue_at`, `last`, and the loop that enforces uniqueness so
`position()` will not drop cues.

---

Outside diff comments:
In `@rs/moq-mux/examples/scte35_inject.rs`:
- Around line 89-100: The loop breaks as soon as a PMT is seen, which can cause
pmt_pid to still be None for inputs where PAT arrives later; update the loop
that iterates reader.read_ts_packet() so it continues scanning until both
pmt_pid and orig (the PMT) are known: when encountering TsPayload::Pat set
pmt_pid = pat.table.first().map(...), when encountering TsPayload::Pmt set orig
= Some(pmt) but do not break immediately — instead also attempt to extract the
PMT PID from the packet header if pmt_pid is None (or simply continue looping
until pmt_pid.is_some() && orig.is_some()), then after the loop use
pmt_pid.context(...) and orig.context(...).
🪄 Autofix (Beta)

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

Run ID: 62903042-61f5-4291-82ae-be80f8513c0d

📥 Commits

Reviewing files that changed from the base of the PR and between 4d09d42 and d568672.

📒 Files selected for processing (1)
  • rs/moq-mux/examples/scte35_inject.rs

Comment thread rs/moq-mux/examples/scte35_inject.rs
- Cue positions are now made strictly increasing without clamping to the last
  packet, and an `ensure!` errors when the input is too short to place all cues
  after the PMT. The previous clamp could pin several cues to the final packet,
  which `position()` would then silently collapse to one.
- Keep scanning until both the PMT PID and the PMT are known instead of breaking
  on the first PMT, falling back to the carrying PID for PAT-late streams.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated
kixelated merged commit 28505fc into main Jun 12, 2026
1 check passed
@kixelated
kixelated deleted the claude/scte35-inject-harden branch June 12, 2026 19:48
This was referenced Jun 12, 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