Skip to main content

Crate ssai_runtime

Crate ssai_runtime 

Source
Expand description

Sans-IO SCTE-35 server-side ad insertion (SSAI) session core — issue #929.

Per-session ad-break state (session), a pluggable ad-decision trait (decision), SCTE-35 splice-point conditioning against real segment or keyframe boundaries (splice), and per-session HLS Interstitial (EXT-X-DATERANGE CLASS="com.apple.hls.interstitial", draft-pantos-hls-rfc8216bis Appendix D, transcribed at broadcast-hls/docs/interstitials.md) playlist rendering over broadcast_hls::MediaPlaylist (playlist).

§What it does / what it does not

Does: decides where a break splices (cue aligned to real boundaries, with the actual drift reported), remembers which break each viewer session is in, and renders what that viewer’s playlist should say.

Does not:

  • No HTTP client, no VAST/VMAP. decision::AdDecisionProvider is the only extension point for “which ad to play”; any network I/O is the caller’s job.
  • No media manipulation. Splicing CMAF/TS bytes is transmux’s job; this crate decides where and what the manifest says.
  • No per-viewer media cursor. What differs per session is a small session::BreakState record, never a media_plane::Trunk cursor.
  • No DASH period insertion, no multimux wiring (future adapter), and no SCTE-35 parsing: the core takes plain tick counts.

§Standards supported

StandardEditionScopeStatus
IETF draft-pantos-hls-rfc8216bis Appendix D (Interstitials)draft dated 1 May 2026 (vendored); an Internet-DraftEXT-X-DATERANGE Interstitial schema: CLASS, X-ASSET-URI/X-ASSET-LIST (URI only), X-RESUME-OFFSET, X-PLAYOUT-LIMIT, X-SNAP, X-RESTRICTPartial (no skip-button control D.3, no asset-list JSON schema)
ANSI/SCTE 35 2023r12023r1cue semantics as inputs (decision::BreakContext); 33-bit pts_time wrap handlingPartial (ticks in, no wire decoding; see scte35-splice)

§Features

FeatureDefaultEffect
stdyesLinks std; off gives #![no_std] + alloc
serdenoserde derives on the decision and break-state types

§Quick start

use ssai_runtime::{
    AdBreakDecision, BreakState, InterstitialDateRange, SessionStore, condition_splice_point,
};

// A cue targets tick 900_000 (90 kHz); the real segment boundaries nearest it
// are at 897_000 and 904_500. Allow up to 5_000 ticks of drift.
let boundaries = [810_000_u64, 897_000, 904_500];
let point = condition_splice_point(900_000, &boundaries, 5_000).unwrap();
assert_eq!(point.snapped_pts, 897_000);
assert_eq!(point.delta_ticks, 3_000);

// Whatever ad-decision service the caller uses hands back one decision.
let decision = AdBreakDecision::single_asset("ad-1", "https://ads.example/ad-1.m3u8");

// Track the break for one viewer session ...
let mut store = SessionStore::new();
store
    .begin_break("viewer-42", BreakState::new(decision.clone(), point))
    .unwrap();
assert_eq!(store.len(), 1);

// ... and render the interstitial EXT-X-DATERANGE tag line for their playlist.
let tag = InterstitialDateRange::from_decision(&decision, "2026-01-01T00:00:10.000Z", Some(30.0))
    .to_tag_line()
    .unwrap();
assert!(tag.contains("CLASS=\"com.apple.hls.interstitial\""));
assert!(tag.contains("X-ASSET-URI=\"https://ads.example/ad-1.m3u8\""));

§What is implemented

§Design notes

Sans-IO, no_std + alloc, #![forbid(unsafe_code)], structured Errors. Ad-decision-supplied strings containing ", CR or LF are rejected (no tag injection). 33-bit arithmetic goes through broadcast_common::clock33.

Depends on broadcast-common and broadcast-hls; media splicing is transmux; the HTTP-facing origin is multimux. MSRV 1.95.0; MIT OR Apache-2.0.

Re-exports§

pub use decision::AdBreakDecision;
pub use decision::AdDecisionProvider;
pub use decision::AssetSource;
pub use decision::BreakContext;
pub use decision::RestrictMode;
pub use decision::SnapMode;
pub use error::Error;
pub use error::Result;
pub use playlist::INTERSTITIAL_CLASS;
pub use playlist::InterstitialDateRange;
pub use playlist::SessionPlaylistBase;
pub use playlist::render_session_playlist;
pub use session::BreakState;
pub use session::SessionStore;
pub use splice::ConditionedSplicePoint;
pub use splice::PTS_MODULUS_33;
pub use splice::SnapDirection;
pub use splice::condition_splice_point;
pub use splice::condition_splice_point_wrapping;

Modules§

decision
Ad-break decision types.
error
Crate error type.
playlist
Per-session HLS Interstitial playlist rendering.
session
Per-session ad-break state.
splice
Splice-point conditioning: aligning a SCTE-35 cue’s target presentation time (splice_time().pts_time, ANSI/SCTE 35 2023r1 §9.8.1, or a DASH emsg’s presentation_time) against the primary content’s actual segment/keyframe boundaries.