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'ISOBMFFFullBox(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 opaquemessage_data[].sizeandversionare 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) vspresentation_time(u64, version 1, representation-relative). Selecting a variant is selecting the box version.EmsgVersion— theversionbyte (0 / 1) with its spec label.EmsgBox::is_scte35— recognises the SCTE 35 scheme (SCTE35_SCHEME_PREFIX,urn:scte:scte35…), in which casemessage_datacarries a SCTE 35splice_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§
- Emsg
Version - The
versionbyte of the'emsg'FullBox(DASH-IF Part 10 Table 6-2). - Error
- An
emsgparse / serialize error. - Presentation
Time - 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 flagsvalue mandated foremsg(DASH-IF Part 10 / ISO box syntax: 0).- FULLBOX_
HEADER_ LEN - Size in bytes of the
FullBoxheader: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
versionforEmsgVersion::SegmentRelative. - VERSION_
1 - The wire value of
versionforEmsgVersion::RepresentationRelative.
Type Aliases§
- Result
- Result alias for
emsgparsing.