MPEG-TS CLI tool to detect, insert, and list SCTE-35 cues. Think of it as a lightweight shorthand alternative to common TSDuck workflows for SCTE-35.
- Written with excessive use of GenAI Agents.
- Inject cues: Add base64-encoded SCTE-35
splice_info_sectionmessages. - Dual timestamps:
placement@splice=— control where packets are placed and the internalsplice_timecarried in the cue. - Auto-add SCTE-35 PID: If none exists, allocate one, rewrite PMT (CRC-correct, with CUEI reg descriptor), and update continuity.
- Continuity-safe: Maintains continuity counters for all PIDs it touches.
- List cues: Finds cues via PMT, PSI sections (
table_id 0xFC), or PES (stream_id 0xFC); reports PTS and base64. - Picture Timing SEI: Inject H.264 Picture Timing SEI messages on video keyframes with incrementing timestamps.
- Streaming: Processes TS incrementally; no full-file buffering.
- Well-tested: Unit, edge, and end-to-end tests (fixtures in
test-assets/).
scte35-injector --input test-assets/tears_of_steel_1080p.ts \
--output /tmp/out.ts \
--cue "00:00:25.000@00:00:30.000=/DAWAAAAAAAAAP/wBQb+Qjo1vQAAuwxz9A=="Places packets at 25s and rewrites splice_time to 30s inside the cue. Omit @... to keep the original splice time.
Inject Picture Timing SEI on all keyframes starting at 18:00:00:
scte35-injector --input test-assets/tears_of_steel_1080p.ts \
--output /tmp/out.ts \
--pic-timing-start "18:00:00.000"List cues in a stream:
scte35-injector --input test-assets/scte35_splice_inserts_with_auto_return.ts --list-cuesscte35-injector [OPTIONS] --input <INPUT> --output <OUTPUT>
Options:
-i, --input <INPUT> Input MPEG-TS file path
-o, --output <OUTPUT> Output MPEG-TS file path (required for injection; ignored in list mode)
--cue <CUES> Repeatable: placement[@splice]=<base64 splice_info_section>, placement/splice in HH:MM:SS[.mmm] format
Example: 00:00:10=/DAWAAAAAAAAAP/wBQb+Qjo1vQAAuwxz9A==
--scte35-pid <SCTE_PID> Optional SCTE-35 PID hint (hex or decimal)
--pcr-pid <PCR_PID> Optional PCR PID hint
--video-pid <VIDEO_PID> Optional video PID hint (timing reference)
--list-cues List SCTE-35 cues present in the input, then exit
--pic-timing-start <TIME> Start time for Picture Timing SEI injection (HH:MM:SS.mmm)
-h, --help Print help
-V, --version Print version
placement:HH:MM:SS[.mmm]— where to insert packets on the TS timeline.- Optional
@splice:HH:MM:SS[.mmm]— value to write into the SCTE-35splice_time(time_signal or splice_insert). If omitted, the payload’s existing splice_time is preserved. =<base64>: complete SCTE-35splice_info_section(CRC included).
On inject:
- Payload is validated; if
@spliceis provided, the cue is re-encoded with the newsplice_time(33-bit wrap respected). - Packets are placed at the nearest packet at/before the target PTS derived from the reference timeline.
The --pic-timing-start option injects H.264 Picture Timing SEI messages (ITU-T H.264 Annex D.2.2) into the video stream:
- When: SEI is injected on every video keyframe (IDR NAL unit).
- Format:
HH:MM:SS.mmm— hours, minutes, seconds, milliseconds. - Timing: The start time is used for the first keyframe. Subsequent keyframes receive timestamps incremented based on PTS delta from the first keyframe.
- VUI extraction: If the video SPS contains VUI timing parameters (
time_scale,num_units_in_tick), they are used for accurate frame rate calculation. Otherwise defaults to 29.97fps. - Milliseconds to frames: Milliseconds are converted to
n_framesbased on the detected or default frame rate.
Example combining SCTE-35 cues with Picture Timing SEI:
scte35-injector --input input.ts --output output.ts \
--pic-timing-start "18:00:00.000" \
--cue "00:00:10.000=/DAWAAAAAAAAAP/wBQb+Qjo1vQAAuwxz9A=="- Single-program TS expected. PMT rewrite handles one PMT; multi-PMT not yet supported.
- If no SCTE PID exists, a free PID ≥ 0x30 is allocated and every PMT packet is rewritten to include it (version bump, CRC).
- Bitrate: we insert packets; not currently doing CBR shaping. Add nulls after if you need strict rate.
- Timing reference: first detected video PID unless
--video-pidhint is given. PCR PID is discovered or hinted. - Listing: supports PSI (
table_id 0xFC) and PES (stream_id 0xFC), with PMT discovery or heuristic PID scan. - Streaming: no full-file buffering. Probe caps metadata search to ~200k packets; timeline collection otherwise grows with duration.
src/main.rsCLI entry.src/lib.rsCore parsing, packetization, PID allocation, timing helpers, PES handling.src/inject.rsInjection pipeline (probe → plan → packetize → write).src/list.rsCue listing pipeline.src/h264.rsH.264 NAL parsing, Picture Timing SEI encoding, VUI extraction.test-assets/Sample TS fixtures.
cargo test # unit + integration
cargo run -- --input test-assets/... --list-cues # manual validation- Periodic PMT repetition after adding SCTE PID.
- Optional null-packet padding to maintain bitrate and PCR spacing.
- Multi-program TS support.
- Configurable insertion policy (before/after target PTS) and explicit reference PID selection.
- Additional small synthetic fixtures with golden hashes for faster CI.
- Designed for offline processing; no network or FFmpeg runtime dependencies.
- Assumes input TS is well-formed (188-byte packets). Errors if sync byte missing.