Skip to main content

timed_metadata/convert/
emsg.rs

1//! SCTE-35 ↔ DASH `emsg` conversion (SCTE 214-3; scheme `urn:scte:scte35:2013:bin`).
2use crate::error::{Error, Result};
3use alloc::{string::String, vec::Vec};
4use mp4_emsg::{EmsgBox, PresentationTime};
5
6/// The SCTE-35 binary carriage scheme for DASH `emsg` (SCTE 214-3).
7pub const SCTE35_SCHEME: &str = "urn:scte:scte35:2013:bin";
8
9/// Parameters for emitting a SCTE-35-carrying `emsg`.
10#[derive(Debug, Clone)]
11pub struct EmsgConfig {
12    /// `timescale` (ticks/second) for the emsg time fields.
13    pub timescale: u32,
14    /// `presentation_time_delta` (v0) or `presentation_time` (v1).
15    pub presentation: PresentationTime,
16    /// `event_duration` in `timescale` units (0 if unknown).
17    pub event_duration: u32,
18    /// `value` string (often the segmentation type id, as text).
19    pub value: String,
20    /// `id` — unique event identifier (u32).
21    pub id: u32,
22}
23
24/// Wrap a verbatim `splice_info_section` as a SCTE-35 `emsg` box (serialized bytes).
25pub fn scte35_to_emsg(splice_raw: &[u8], cfg: &EmsgConfig) -> Result<Vec<u8>> {
26    let boxx = EmsgBox {
27        scheme_id_uri: SCTE35_SCHEME,
28        value: &cfg.value,
29        timescale: cfg.timescale,
30        presentation_time: cfg.presentation,
31        event_duration: cfg.event_duration,
32        id: cfg.id,
33        message_data: splice_raw,
34    };
35    Ok(boxx.to_vec()?)
36}
37
38/// Extract the verbatim `splice_info_section` from a SCTE-35 `emsg` box.
39pub fn emsg_to_scte35(emsg_bytes: &[u8]) -> Result<Vec<u8>> {
40    let boxx = EmsgBox::parse(emsg_bytes)?;
41    if !boxx.is_scte35() {
42        return Err(Error::UnsupportedScheme {
43            scheme: String::from(boxx.scheme_id_uri),
44        });
45    }
46    Ok(boxx.message_data.to_vec())
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use alloc::string::ToString;
53
54    fn splice_2002() -> alloc::vec::Vec<u8> {
55        let hex = "FC302100000000000000FFF01005000007D27FEF7F7E0020F580C0000000000088B9661D";
56        (0..hex.len())
57            .step_by(2)
58            .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
59            .collect()
60    }
61
62    #[test]
63    fn scte35_to_emsg_embeds_splice_verbatim_then_round_trips() {
64        let splice = splice_2002();
65        let cfg = EmsgConfig {
66            timescale: 90_000,
67            presentation: PresentationTime::Delta(0),
68            event_duration: 2_160_000,
69            value: "1".to_string(),
70            id: 1,
71        };
72        let emsg = scte35_to_emsg(&splice, &cfg).unwrap();
73        // message_data must equal the splice verbatim:
74        let extracted = emsg_to_scte35(&emsg).unwrap();
75        assert_eq!(extracted, splice);
76    }
77}