Skip to main content

scte35_splice/
lib.rs

1//! # scte35-splice — ANSI/SCTE 35 2023r1 splice information
2//!
3//! Spec-cited parser **and builder** for the SCTE 35 Digital Program Insertion
4//! cueing message (`splice_info_section`, table_id `0xFC`), with the workspace's
5//! symmetric [`Parse`](broadcast_common::Parse)/[`Serialize`](broadcast_common::Serialize)
6//! discipline: every wire type round-trips byte-for-byte.
7//!
8//! The implemented edition is **ANSI/SCTE 35 2023r1** (the single-document
9//! edition; SCTE has since split the standard into 35-1 / 35-2). Layouts are
10//! transcribed in `scte35-splice/docs/scte_35.md` and cited per module.
11//!
12//! ## Coverage
13//!
14//! - [`SpliceInfoSection`] — the full §9.6 header (protocol_version, the
15//!   encryption flags, 33-bit `pts_adjustment`, `cw_index`, 12-bit `tier`,
16//!   `splice_command_length`/type, descriptor loop, CRC_32). Encrypted sections
17//!   are kept raw and round-trip losslessly; clear sections expose typed
18//!   commands and descriptors.
19//! - Commands ([`commands`]): `splice_null`, `splice_schedule`, `splice_insert`,
20//!   `time_signal`, `bandwidth_reservation`, `private_command`, unified by
21//!   [`AnyCommand`](commands::AnyCommand).
22//! - Splice descriptors ([`descriptors`]): `avail`, `DTMF`, `segmentation`
23//!   (with [`SegmentationTypeId`](descriptors::SegmentationTypeId) /
24//!   [`SegmentationUpidType`](descriptors::SegmentationUpidType)), `time`,
25//!   `audio`, unified by
26//!   [`AnySpliceDescriptor`](descriptors::AnySpliceDescriptor) with a raw
27//!   fall-through.
28//! - Typed UPID sub-structures: [`descriptors::Mpu`] (§10.3.3.3, Table 24)
29//!   and [`descriptors::MidUpid`] (§10.3.3.4, Table 25) decoded on demand via
30//!   [`descriptors::SegmentationDescriptor::mpu`] /
31//!   [`descriptors::SegmentationDescriptor::mid`].
32//! - Decoded accessors: 90 kHz fields → [`core::time::Duration`]
33//!   (`pts_time`, `break_duration`, `pts_adjustment`).
34//!
35//! ## Quick start
36//!
37//! ```
38//! use scte35_splice::{SpliceInfoSection, commands::AnyCommand};
39//! use broadcast_common::{Parse, Serialize};
40//!
41//! // A minimal time_signal() section with no descriptors, built and emitted.
42//! let ts = scte35_splice::commands::TimeSignal {
43//!     splice_time: scte35_splice::time::SpliceTime::with_pts(0x0_0012_3456),
44//! };
45//! let section = SpliceInfoSection::new_clear(AnyCommand::TimeSignal(ts), &[]);
46//! let bytes = section.to_bytes();
47//! assert_eq!(bytes[0], 0xFC); // table_id
48//!
49//! // ...and parsed straight back.
50//! let parsed = SpliceInfoSection::parse(&bytes).unwrap();
51//! assert!(matches!(parsed.clear.as_ref().unwrap().command, AnyCommand::TimeSignal(_)));
52//! ```
53//!
54//! ## dvb-si integration
55//!
56//! SCTE 35 sections ride on a PID labelled in the PMT by a registration
57//! descriptor carrying the format_identifier `"CUEI"` (which dvb-si already
58//! parses). Once you have the `0xFC` section bytes, route them here:
59//!
60//! ```
61//! use scte35_splice::SpliceInfoSection;
62//! use broadcast_common::{Parse, Serialize};
63//!
64//! // A splice_null() section produced by this crate stands in for bytes a
65//! // dvb-si demux would hand you from the SCTE 35 PID.
66//! let section = SpliceInfoSection::new_clear(
67//!     scte35_splice::commands::AnyCommand::SpliceNull(Default::default()),
68//!     &[],
69//! );
70//! let on_the_wire = section.to_bytes();
71//!
72//! // table_id 0xFC marks it as a splice_info_section; parse it.
73//! assert_eq!(on_the_wire[0], scte35_splice::section::TABLE_ID);
74//! let parsed = SpliceInfoSection::parse(&on_the_wire).unwrap();
75//! assert_eq!(parsed.descriptors().count(), 0);
76//! ```
77//!
78//! ## Reserved-bit & CRC policy
79//!
80//! Reserved bits are written as `1` on serialize (the spec's convention) and
81//! ignored on parse. `splice_info_section` uses the MPEG-2 CRC-32
82//! ([`broadcast_common::crc32_mpeg2`], §9.6.1): parse verifies it, serialize
83//! recomputes it.
84
85#![forbid(unsafe_code)]
86#![warn(missing_docs)]
87#![cfg_attr(docsrs, feature(doc_cfg))]
88#![cfg_attr(not(feature = "std"), no_std)]
89// Runnable examples, embedded so they render on docs.rs and stay in sync with
90// the actual `examples/*.rs` files (shown, not compiled).
91#![doc = "\n# Examples\n"]
92#![doc = "Two runnable examples ship with this crate (`cargo run -p scte35-splice --example <name>`).\n"]
93#![doc = "\n## `parse_splice_insert`\n\n```rust,ignore"]
94#![doc = include_str!("../examples/parse_splice_insert.rs")]
95#![doc = "```\n\n## `round_trip_and_descriptors`\n\n```rust,ignore"]
96#![doc = include_str!("../examples/round_trip_and_descriptors.rs")]
97#![doc = "```"]
98
99extern crate alloc;
100
101pub mod commands;
102pub mod descriptors;
103pub mod dvb_ta;
104pub mod error;
105pub mod section;
106pub mod time;
107pub mod traits;
108
109pub use error::{Error, Result};
110pub use section::{ClearPayload, SpliceInfoSection};
111pub use traits::{CommandDef, SpliceDescriptorDef};