Skip to main content

Crate scte104

Crate scte104 

Source
Expand description

§scte104 — ANSI/SCTE 104 2023 Automation↔Compression DPI signalling

SCTE 104 is the message protocol an automation system uses to tell a compression/injection system to insert SCTE 35 (DPI) cueing into the outgoing Transport Stream. This crate parses + builds the SCTE 104 messages: single_operation_message and multiple_operation_message and the DPI operations they carry (splice, time_signal, insert-descriptor, segmentation, …).

ANSI/SCTE 104 2023 — Automation System to Compression System Communications Applications Program Interface (API).

Depends only on broadcast-common and is #![no_std] (+ alloc).

§Coverage

  • SingleOperationMessage — single-operation framing (§8.2.2, Table 8-1) with basic request/response operations.
  • MultipleOperationMessage — multi-operation framing (§8.2.3, Table 8-2) with Normal, Supplemental, and Control operations.
  • All 15 Table 8-3 operations (basic request/response, including the general_response/init_*/alive_*/inject_* messages of §9 and the config_*/provisioning_*/fault_*/AS_alive_* PAMS⇔AS messages of §10) and all 22 Table 8-4 operations (Normal/Supplemental/Control): splice, time_signal, splice_null, descriptor inserts, segmentation, encryption, schedule, control words, proprietary commands, and more.
  • Timestamp (§12.5): variable-length (none/UTC/VITC/GPI) with typed payloads.
  • Time (§12.4): 8-byte GPS-epoch timestamp used in alive_request/response.
  • AnyOperation: unified dispatch enum with a drift test pinning opID literals to type constants.

§Quick start

use scte104::{SingleOperationMessage, MultipleOperationMessage, operations::{AnyOperation, Operation, splice_request::{SpliceRequest, SpliceInsertType}}};
use broadcast_common::{Parse, Serialize};

// Build a splice_request single_operation_message (basic response wrap).
let msg = SingleOperationMessage::new_request(
    0x0101, 0, 1, 42, 0,
    scte104::operations::AnySingleOperation::Unknown { op_id: 0x0101, body: &[] },
);
let bytes = msg.to_bytes();

// Build a multiple_operation_message with splice + insert_descriptor
let ops = vec![
    Operation {
        op_id: 0x0101,
        data: AnyOperation::SpliceRequest(SpliceRequest {
            splice_insert_type: SpliceInsertType::SpliceStartNormal,
            splice_event_id: 42,
            unique_program_id: 1,
            pre_roll_time: 5000,
            break_duration: 300,
            avail_num: 0,
            avails_expected: 0,
            auto_return_flag: 1,
            not_an_entry_flag: 0,
        }),
    },
];
let mom = MultipleOperationMessage::new(
    0, 1, 42, 0, 0,
    scte104::time::Timestamp::None,
    ops,
);
let bytes = mom.to_bytes();

§Examples

Two runnable examples ship with this crate (cargo run -p scte104 --example <name>).

//! Build and serialize a single_operation_message with a splice_request.
//!
//! This example constructs a basic response (inject_response),
//! serializes it, then parses it back to verify round-trip integrity.

use broadcast_common::{Parse, Serialize};
use scte104::SingleOperationMessage;
use scte104::operations::{AnySingleOperation, InjectResponse};

fn main() {
    // Build an inject_response single_operation_message.
    let msg = SingleOperationMessage::new_response(
        0x0007, // opID = inject_response
        0x0000, // result = success
        0xFFFF, // result_extension
        0,      // protocol_version
        1,      // AS_index
        42,     // message_number
        0,      // DPI_PID_index
        AnySingleOperation::InjectResponse(InjectResponse { message_number: 42 }),
    );

    // Serialize.
    let bytes = msg.to_bytes();
    println!("Serialized {} bytes: {:02x?}", bytes.len(), bytes);

    // Round-trip: parse back.
    let parsed = SingleOperationMessage::parse(&bytes).unwrap();
    assert_eq!(msg, parsed);
    println!("Round-trip OK: message_number={}", parsed.message_number);

    // Build a second message with an unknown opID (raw body preserved).
    let raw_body = [0xCA, 0xFE];
    let msg2 = SingleOperationMessage::new_response(
        0xDEAD,
        0x0000,
        0xFFFF,
        0,
        1,
        99,
        0,
        AnySingleOperation::Unknown {
            op_id: 0xDEAD,
            body: &raw_body,
        },
    );
    let bytes2 = msg2.to_bytes();
    let parsed2 = SingleOperationMessage::parse(&bytes2).unwrap();
    assert_eq!(msg2, parsed2);
    println!("Unknown opID round-trip OK");
}
//! Build a multiple_operation_message with several operations and round-trip.
//!
//! This example constructs a message with a splice_request followed by a
//! time_signal_request (≥2 operations), serializes it, then parses it back.

use broadcast_common::{Parse, Serialize};
use scte104::MultipleOperationMessage;
use scte104::operations::{
    AnyOperation, Operation,
    splice_request::{SpliceInsertType, SpliceRequest},
    time_signal_request::TimeSignalRequest,
};
use scte104::time::Timestamp;

fn main() {
    // Build operations: splice + time_signal.
    let ops = vec![
        Operation {
            op_id: 0x0101,
            data: AnyOperation::SpliceRequest(SpliceRequest {
                splice_insert_type: SpliceInsertType::SpliceStartNormal,
                splice_event_id: 42,
                unique_program_id: 1,
                pre_roll_time: 5000,
                break_duration: 300,
                avail_num: 0,
                avails_expected: 0,
                auto_return_flag: 1,
                not_an_entry_flag: 0,
            }),
        },
        Operation {
            op_id: 0x0104,
            data: AnyOperation::TimeSignalRequest(TimeSignalRequest {
                pre_roll_time: 2000,
            }),
        },
    ];

    // Build the multi-op message.
    let msg = MultipleOperationMessage::new(
        0,               // protocol_version
        1,               // AS_index
        42,              // message_number
        0,               // DPI_PID_index
        0,               // SCTE35_protocol_version
        Timestamp::None, // immediate processing
        ops,
    );

    // Serialize.
    let bytes = msg.to_bytes();
    println!(
        "Serialized {} bytes with {} ops",
        bytes.len(),
        msg.operations.len()
    );

    // Round-trip.
    let parsed = MultipleOperationMessage::parse(&bytes).unwrap();
    assert_eq!(msg, parsed);
    assert_eq!(parsed.operations.len(), 2);
    println!("Round-trip OK: 2 operations preserved");

    // Verify operations are correctly parsed.
    for op in &parsed.operations {
        println!("  opID {:#06x}: {}", op.op_id, op.data.name());
    }

    // Demonstrate mutation detection.
    let mut msg2 = msg.clone();
    msg2.message_number = 99;
    assert_ne!(msg.to_bytes(), msg2.to_bytes());
    println!("Mutation changes output: OK");
}

Re-exports§

pub use error::Error;
pub use error::Result;
pub use multi::MultipleOperationMessage;
pub use single::SingleOperationMessage;

Modules§

error
Error types for the scte104 crate.
multi
Multiple operation message framing — ANSI/SCTE 104 2023 §8.2.3, Table 8-2.
operations
SCTE 104 operations (request/response data structures).
single
Single operation message framing — ANSI/SCTE 104 2023 §8.2.2, Table 8-1.
time
Time structures — ANSI/SCTE 104 2023 §12.4, §12.5.
traits
SCTE 104 dispatch traits, mirroring scte35-splice’s CommandDef.