SkillAgentSearch skills...

Scte35

Parse and encoding of data compliant to the SCTE-35 standard.

Install / Use

/learn @rafaelcaricio/Scte35
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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 crc crate for validation (optional) and serde for 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 &section.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(&section).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 &section.splice_descriptors {
        if
View on GitHub
GitHub Stars8
CategoryContent
Updated5d ago
Forks1

Languages

Rust

Security Score

90/100

Audited on Mar 30, 2026

No findings