playout-runtime
Sans-IO linear channel playout (issue
#748): a schedule
model (programme / ad / slate), transition planning across the join between
two sources, and the SCTE-35 splice_insert() emission points a transition
implies. no_std + alloc. No HTTP, no tokio.
What's here
- [
schedule] — [schedule::Schedule]: an ordered list of [schedule::ScheduleEntry] (programme/ad/slate, a source identity, and a planned start on the channel timeline). The format is deliberately ours to define — see the issue's design-decision comment: SCTE 224 covers policy/blackout signalling, not channel assembly, and adopting it would answer a different question. There is therefore noParse/Serializepair here; it's an in-memory model, not a wire format. - [
transition] — [transition::next_transition] finds the next join the schedule implies and [transition::TransitionPlan::plan] computes it: the channel-timeline instant it happens at, a PTS-rebase offset so the incoming source's own clock lands continuously on the shared timeline, and a discontinuity flag when the codec configuration changes across the join. This is the crate's primary tested property — see Timeline continuity below. - [
scte35] — [scte35::build_splice_insert] builds thesplice_insert()(ANSI/SCTE 35 2023r1 §9.7.3) a transition implies, usingscte35-splice's ownSerialize(not hand-assembled bytes), with its target instant conditioned against real candidate boundaries via [ssai_runtime::splice::condition_splice_point] — reused, not re-implemented.
What this crate is not
- No transcoding or conforming. A differing codec config across a
transition is a discontinuity to signal
([
transition::TransitionPlan::discontinuity]), never something this crate re-encodes. This workspace parses containers and never touches the codec bitstream, and that holds here too. - No actual sample-timestamp rewriting.
transmux's IR transform would apply [transition::TransitionPlan::pts_rebase_offset] to real sample PTS/DTS values; this crate computes the number, not the rewrite.broadcast_hls::mark_init_discontinuitieswould act on [transition::TransitionPlan::discontinuity] at the playlist/init-segment level — neither dependency is pulled in here. - No boundary conditioning of its own. [
scte35::build_splice_insert] callsssai-runtime'scondition_splice_pointrather than duplicating nearest-boundary snapping — two implementations of the same boundary math could disagree about the same boundary. - No HTTP, no tokio. A
multimuxadapter driving a real channel clock against this crate's planning is deliberately future work, mirroring thertsp-runtime/hls-runtime/ssai-runtimesans-IO split.
Timeline continuity: the primary tested property
Issue #748's framing: "a schedule that plays the right thing at the wrong
timestamp is worse than no scheduler." This crate's tests hold
TransitionPlan to that bar rather than treating schedule bookkeeping as
the interesting part:
transition::tests::rebase_lands_the_incoming_source_continuously_on_the_channel_timeline— once rebased, the incoming source's own PTS sequence lands exactly on the channel timeline with no gap and no overlap.transition::tests::rebase_returns_none_rather_than_wrapping_out_of_range— a rebase that would fall outsideu64's range reportsNone, never a wrapped, wrong instant.tests/real_fixture_transition.rsplans a programme -> ad transition against the real, non-IDR-aligned cue infixtures/scte35-ssai/(DASH-IFlivesim2, Apache-2.0): the nearest video keyframe is 6000 ticks (67 ms) after the cue's nominal instant, measured independently straight from the fragment's own boxes (fixtures/scte35-ssai/PROVENANCE.md). The test asserts that measured gap and that the emitted cue carries the conditioned instant, not the raw request — a regression that madebuild_splice_inserta passthrough ofrequested_ptsfails this test loudly.
Examples
schedule_and_transition— build a programme -> ad -> programme schedule and walk every transition it implies.emit_splice_from_real_cue— the full pipeline against the real fixture: decode the cue, plan the transition, condition the splice point, emit and byte-exact-round-trip thesplice_insert().
Install
[]
= "0.1"
Spec references
- SCTE-35
splice_insert(): ANSI/SCTE 35 2023r1 §9.7.3, transcribed atscte35-splice/docs/. - Splice-point conditioning:
ssai-runtime, issue #929.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.