Skip to main content

Crate dvb_emsg

Crate dvb_emsg 

Source
Expand description

dvb-emsg — MPEG-DASH Event Message Box (emsg): inband DASH/CMAF timed events (SCTE 35 splice signalling, ID3 metadata, ad/tracking triggers).

The emsg box delivers sparse, timed application events alongside the media in a DASH/CMAF segment. This crate implements:

  • EmsgBox — the 'emsg' ISOBMFF FullBox (size / 'emsg' / version / flags) plus both version bodies: the two null-terminated UTF-8 strings (scheme_id_uri, value), the integer fields (timescale, event_duration, id), the version-discriminated presentation-time field, and the opaque message_data[]. size and version are recomputed/derived on serialize from the typed fields (no raw passthrough).
  • PresentationTime — the version-discriminated timing field: presentation_time_delta (u32, version 0, segment-relative) vs presentation_time (u64, version 1, representation-relative). Selecting a variant is selecting the box version.
  • EmsgVersion — the version byte (0 / 1) with its spec label.
  • EmsgBox::is_scte35 — recognises the SCTE 35 scheme (SCTE35_SCHEME_PREFIX, urn:scte:scte35…), in which case message_data carries a SCTE 35 splice_info_section.

Note the v0/v1 field ordering differs: version 0 places the two strings first (before the integer fields); version 1 places the integer fields first and the strings last. Both orderings are parsed and serialized.

§⚠ Source footing — softer than the fully-free crates

The emsg field semantics and types (scheme_id_uri, value, timescale, presentation_time/presentation_time_delta, event_duration, id, message_data) are render-verified from a free source: DASH-IF IOP Part 10 (Events and Timed Metadata) V5.0.0, §6.1 + Table 6-2 (transcribed in dvb-emsg/docs/emsg.md).

However, the normative ISOBMFF box syntax

aligned(8) class EventMessageBox extends FullBox('emsg', version, flags = 0)

— i.e. the exact byte-level field ordering, the version-gated branch (presentation_time_delta vs presentation_time), and the null-terminated-string layout, lives in ISO/IEC 23009-1 §5.10.3.3, which is paid and NOT vendored in this repo. DASH-IF Part 10 references it but does not reproduce it. The box layout here is therefore implemented from the well-known public emsg structure (widely reproduced in MPEG-DASH / CMAF) combined with the free DASH-IF Part 10 semantics, with ISO/IEC 23009-1 §5.10.3.3 cited as the formal (paid) normative source. This is softer footing than the fully-free crates in this workspace — flagged here per project policy. The v0/v1 ordering in particular is the part most reliant on the non-vendored ISO source.

Likewise, the SCTE 35 scheme-URI strings and the message_data binding are defined by SCTE 214-1 / ANSI/SCTE 35 (not vendored); is_scte35 only recognises the well-known urn:scte:scte35… URI prefix.

#![no_std] + alloc; depends only on dvb-common.

§Examples

Build a version 0 (segment-relative) emsg from typed fields and round-trip it:

use dvb_emsg::{EmsgBox, PresentationTime};

let scte35 = [0xFCu8, 0x30, 0x11]; // start of a splice_info_section
let b = EmsgBox {
    scheme_id_uri: "urn:scte:scte35:2013:bin",
    value: "",
    timescale: 90_000,
    presentation_time: PresentationTime::Delta(0),
    event_duration: 0xFFFF_FFFF,
    id: 1,
    message_data: &scte35,
};
assert!(b.is_scte35());
let bytes = b.to_vec().unwrap();
assert_eq!(EmsgBox::parse(&bytes).unwrap(), b);

§Runnable examples

Run with cargo run -p dvb-emsg --example <name>.

§build_emsg

/// Build an MPEG-DASH `emsg` box from typed fields, serialize it (recomputing
/// the box `size` + `version`), and dump the wire bytes. Builds one version 0
/// (segment-relative) and one version 1 (representation-relative) box to show
/// the v0/v1 field-ordering difference.
///
/// ```sh
/// cargo run -p dvb-emsg --example build_emsg
/// ```
use dvb_emsg::{EmsgBox, PresentationTime};

fn dump(label: &str, b: &EmsgBox) {
    let bytes = b.to_vec().unwrap();
    println!("{label}: {} bytes, version {}", bytes.len(), b.version());
    print!("  wire:");
    for x in &bytes {
        print!(" {x:02X}");
    }
    println!();
    // Round-trip: parse re-validates the structure.
    assert_eq!(EmsgBox::parse(&bytes).unwrap(), *b);
    println!("  round-trip: OK");
}

fn main() {
    // A short SCTE 35 splice_info_section payload (truncated; just illustrative).
    let scte35 = [0xFCu8, 0x30, 0x11, 0x00, 0x00];

    // version 0 — segment-relative (presentation_time_delta). Strings first.
    let v0 = EmsgBox {
        scheme_id_uri: "urn:scte:scte35:2013:bin",
        value: "",
        timescale: 90_000,
        presentation_time: PresentationTime::Delta(0),
        event_duration: 0xFFFF_FFFF,
        id: 1,
        message_data: &scte35,
    };
    println!("is_scte35 (v0): {}", v0.is_scte35());
    dump("v0", &v0);

    // version 1 — representation-relative (presentation_time u64). Integers
    // first, then the strings.
    let v1 = EmsgBox {
        scheme_id_uri: "https://aomedia.org/emsg/ID3",
        value: "0",
        timescale: 1000,
        presentation_time: PresentationTime::Absolute(123_456_789),
        event_duration: 0,
        id: 42,
        message_data: b"ID3 metadata payload",
    };
    dump("v1", &v1);
}

§parse_emsg

/// Read the committed `scte35_emsg_v0.bin` fixture (a version 0 `emsg` carrying
/// a real SCTE 35 splice_info_section in `message_data`), parse it, print the
/// decoded fields, and prove a byte-exact round-trip + recomputed `size`.
///
/// ```sh
/// cargo run -p dvb-emsg --example parse_emsg
/// ```
use std::fs;

use dvb_emsg::EmsgBox;

fn main() {
    let path = concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/tests/fixtures/scte35_emsg_v0.bin"
    );
    let bytes = match fs::read(path) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("fixture not available ({e}); nothing to do");
            return;
        }
    };

    let b = EmsgBox::parse(&bytes).unwrap();
    println!("emsg box: {} bytes (version {})", bytes.len(), b.version());
    println!("  scheme_id_uri: {:?}", b.scheme_id_uri);
    println!("  value:         {:?}", b.value);
    println!("  timescale:     {}", b.timescale);
    println!("  presentation:  {:?}", b.presentation_time);
    println!("  event_duration:{}", b.event_duration);
    println!("  id:            {}", b.id);
    println!("  message_data:  {} bytes", b.message_data.len());
    println!("  is_scte35:     {}", b.is_scte35());
    if b.is_scte35() {
        if let Some(&first) = b.message_data.first() {
            // The SCTE 35 splice_info_section starts with table_id 0xFC.
            println!("  message_data[0] = 0x{first:02X} (SCTE 35 table_id)");
        }
    }

    // Byte-exact round-trip: serialize recomputes the size field.
    let out = b.to_vec().unwrap();
    assert_eq!(
        out, bytes,
        "serialize must be byte-identical to the fixture"
    );
    assert_eq!(out.len(), b.serialized_len());
    println!("round-trip byte-exact + size recomputed: OK");
}

Structs§

EmsgBox
A parsed/owned MPEG-DASH Event Message Box (emsg).

Enums§

EmsgVersion
The version byte of the 'emsg' FullBox (DASH-IF Part 10 Table 6-2).
Error
An emsg parse / serialize error.
PresentationTime
The version-discriminated presentation-time field (DASH-IF Part 10 Table 6-2): the only field whose type and reference point differ by version.

Constants§

EMSG_BOX_TYPE
The 4-byte ISOBMFF box type for an Event Message Box.
EMSG_FLAGS
flags value mandated for emsg (DASH-IF Part 10 / ISO box syntax: 0).
FULLBOX_HEADER_LEN
Size in bytes of the FullBox header: size(4) + type(4) + version(1)
SCTE35_SCHEME_PREFIX
The SCTE 35 scheme-URI prefix carried in emsg.scheme_id_uri (e.g. urn:scte:scte35:2013:bin), per SCTE 214-1 / DASH-IF Part 10 §7.3, §9.2.5.
STRING_TERMINATOR
The single string terminator byte for the null-terminated UTF-8 fields.
VERSION_0
The wire value of version for EmsgVersion::SegmentRelative.
VERSION_1
The wire value of version for EmsgVersion::RepresentationRelative.

Type Aliases§

Result
Result alias for emsg parsing.