Scte35
Parse and encoding of data compliant to the SCTE-35 standard.
Install / Use
/learn @rafaelcaricio/Scte35README
SCTE-35 Library
A Rust library for creating and parsing SCTE-35 (Society of Cable Telecommunications Engineers) messages with built-in CRC validation. SCTE-35 is a standard for inserting cue messages into video streams, commonly used for ad insertion points in broadcast television.
Features
- Builder Pattern API - Type-safe builder pattern for creating SCTE-35 messages from scratch with validation
- Serde support - Serialize/deserialize SCTE-35 messages to/from JSON and other formats (enabled by default)
- CRC validation - Built-in CRC-32 validation using MPEG-2 algorithm (enabled by default)
- Human-readable UPID parsing - Full support for 18 standard UPID types with intelligent formatting
- Human-readable segmentation types - Complete set of 48 standard segmentation types with descriptive names
- Segmentation descriptor parsing - Complete parsing of segmentation descriptors including UPID data
- Minimal dependencies - Only the
crccrate for validation (optional) andserdefor serialization (optional) - Full SCTE-35 parsing - Supports all major SCTE-35 command types
- Bit-level precision - Accurate parsing of bit-packed SCTE-35 messages
- Optional CLI tool - Command-line interface for parsing base64-encoded messages with text and JSON output formats
- Type-safe - Strongly typed representations of all SCTE-35 structures
- Data integrity - Detects corrupted or tampered SCTE-35 messages
Installation
With All Features (Default)
Add this to your Cargo.toml:
[dependencies]
scte35 = "0.2.0"
This includes both CRC validation and serde support.
Without Serde Support
If you don't need JSON serialization:
[dependencies]
scte35 = { version = "0.2.0", default-features = false, features = ["crc-validation"] }
Minimal (No CRC or Serde)
For a minimal library without CRC validation or serde:
[dependencies]
scte35 = { version = "0.2.0", default-features = false }
With CLI Tool (Automatically includes CRC validation)
To include the command-line tool, enable the cli feature:
[dependencies]
scte35 = { version = "0.2.0", features = ["cli"] }
Or install the CLI tool directly:
cargo install scte35 --features cli
Note: The CLI feature automatically enables CRC validation to provide complete message diagnostics.
Usage
Library Usage
use scte35::{SpliceCommand, SpliceDescriptor};
use std::time::Duration;
// Your SCTE-35 message as bytes (example message)
let scte35_bytes = vec![
0xFC, 0x30, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x05, 0x06, 0xFE,
0x42, 0x3A, 0x35, 0xBD, 0x00, 0x00, 0xBB, 0x0C, 0x73, 0xF4
];
match scte35::parse(&scte35_bytes) {
Ok(section) => {
println!("Table ID: {}", section.table_id);
println!("Command Type: {}", section.splice_command_type);
match section.splice_command {
SpliceCommand::SpliceInsert(insert) => {
println!("Splice Event ID: 0x{:08x}", insert.splice_event_id);
// Convert break duration to std::time::Duration
if let Some(break_duration) = &insert.break_duration {
let duration: Duration = break_duration.into();
println!("Break Duration: {:?}", duration);
println!("Break Duration: {:.3} seconds", duration.as_secs_f64());
}
// Convert splice time to Duration
if let Some(duration) = insert.splice_time.as_ref()
.and_then(|st| st.to_duration()) {
println!("Splice Time: {:?}", duration);
}
}
SpliceCommand::TimeSignal(signal) => {
if let Some(duration) = signal.splice_time.to_duration() {
println!("Time Signal: {:?}", duration);
}
}
_ => println!("Other command type"),
}
// Parse segmentation descriptors with UPID information
for descriptor in §ion.splice_descriptors {
if let SpliceDescriptor::Segmentation(seg_desc) = descriptor {
println!("Segmentation Event ID: 0x{:08x}", seg_desc.segmentation_event_id);
println!("UPID Type: {}", seg_desc.upid_type_description());
println!("Segmentation Type: {}", seg_desc.segmentation_type_description());
if let Some(upid_str) = seg_desc.upid_as_string() {
println!("UPID: {}", upid_str);
}
}
}
}
Err(e) => eprintln!("Error parsing SCTE-35: {}", e),
}
CRC Validation
By default, the library validates CRC-32 checksums in SCTE-35 messages to ensure data integrity:
use scte35::parse_splice_info_section;
// Example SCTE-35 message bytes
let scte35_bytes = vec![
0xFC, 0x30, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x05, 0x06, 0xFE,
0x42, 0x3A, 0x35, 0xBD, 0x00, 0x00, 0xBB, 0x0C, 0x73, 0xF4
];
// Parse with automatic CRC validation (default behavior)
match parse_splice_info_section(&scte35_bytes) {
Ok(section) => {
println!("Valid SCTE-35 message parsed successfully");
#[cfg(feature = "crc-validation")]
{
use scte35::CrcValidatable;
println!("CRC-32: 0x{:08X}", section.get_crc());
}
}
Err(e) => {
if e.to_string().contains("CRC validation failed") {
eprintln!("Message corrupted or tampered: {}", e);
} else {
eprintln!("Parse error: {}", e);
}
}
}
#[cfg(feature = "crc-validation")]
{
use scte35::{validate_scte35_crc, CrcValidatable};
// Validate CRC independently
match validate_scte35_crc(&scte35_bytes) {
Ok(true) => println!("CRC validation passed"),
Ok(false) => println!("CRC validation failed or not available"),
Err(e) => eprintln!("Validation error: {}", e),
}
// Validate using the parsed section
if let Ok(section) = parse_splice_info_section(&scte35_bytes) {
match section.validate_crc(&scte35_bytes) {
Ok(true) => println!("Message integrity verified"),
Ok(false) => println!("Message integrity check failed"),
Err(e) => eprintln!("Validation error: {}", e),
}
}
}
Duration Conversion
SCTE-35 time values are represented as 90kHz clock ticks. This library provides convenient conversion to Rust's std::time::Duration:
use scte35::BreakDuration;
use std::time::Duration;
// Create a break duration of 30 seconds (30 * 90000 ticks)
let break_duration = BreakDuration {
auto_return: 1,
reserved: 0,
duration: 2_700_000,
};
// Convert using Into trait
let duration: Duration = (&break_duration).into();
assert_eq!(duration.as_secs(), 30);
// Or use the method directly
let duration2 = break_duration.to_duration();
assert_eq!(duration2.as_secs(), 30);
Serde Support (JSON Serialization)
The library includes built-in serde support for serializing/deserializing SCTE-35 messages:
use scte35::parse_splice_info_section;
// Parse SCTE-35 message
let scte35_bytes = vec![
0xFC, 0x30, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x05, 0x06, 0xFE,
0x42, 0x3A, 0x35, 0xBD, 0x00, 0x00, 0xBB, 0x0C, 0x73, 0xF4
];
if let Ok(section) = parse_splice_info_section(&scte35_bytes) {
#[cfg(feature = "serde")]
{
use serde_json;
// Serialize to JSON
let json = serde_json::to_string_pretty(§ion).unwrap();
println!("{}", json);
// Deserialize from JSON
let deserialized: scte35::SpliceInfoSection =
serde_json::from_str(&json).unwrap();
assert_eq!(section, deserialized);
}
}
The serde implementation includes:
- Binary data as base64: All raw bytes (private commands, UPID data, alignment bits) are encoded as base64 strings
- Human-readable enums: Segmentation types and UPID types include both numeric values and descriptions
- Time duration info: PTS times and durations include both raw ticks and human-readable formats
- Computed fields: Segmentation descriptors include parsed UPID strings when available
Example JSON output:
{
"table_id": 252,
"section_syntax_indicator": 0,
"private_indicator": 0,
"section_length": 22,
"protocol_version": 0,
"encrypted_packet": 0,
"encryption_algorithm": 0,
"pts_adjustment": 0,
"cw_index": 255,
"tier": 4095,
"splice_command_length": 5,
"splice_command_type": 6,
"splice_command": {
"type": "TimeSignal",
"splice_time": {
"time_specified_flag": 1,
"pts_time": 900000,
"duration_info": {
"ticks": 900000,
"seconds": 10.0,
"human_readable": "10.0s"
}
}
},
"descriptor_loop_length": 0,
"splice_descriptors": [],
"alignment_stuffing_bits": "",
"e_crc_32": null,
"crc_32": 0
}
Segmentation Types
The library provides human-readable segmentation types that correspond to the numeric IDs in SCTE-35 messages:
use scte35::{SegmentationType, parse_splice_info_section, SpliceDescriptor};
// Work with segmentation types directly
let seg_type = SegmentationType::ProviderAdvertisementStart;
println!("Type: {} (ID: 0x{:02X})", seg_type, seg_type.id());
// Convert from numeric ID (useful when parsing)
let seg_type = SegmentationType::from_id(0x30);
assert_eq!(seg_type, SegmentationType::ProviderAdvertisementStart);
assert_eq!(seg_type.to_string(), "Provider Advertisement Start");
// Example: Parse a message and check segmentation descriptors
let scte35_bytes = vec![
0xFC, 0x30, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x05, 0x06, 0xFE,
0x42, 0x3A, 0x35, 0xBD, 0x00, 0x00, 0xBB, 0x0C, 0x73, 0xF4
];
if let Ok(section) = parse_splice_info_section(&scte35_bytes) {
// Segmentation descriptors automatically populate both fields
// The numeric ID and human-readable type are always consistent
for descriptor in §ion.splice_descriptors {
if
