timed_metadata/convert/
emsg.rs1use crate::error::{Error, Result};
12use alloc::{string::String, vec::Vec};
13use mp4_emsg::{EmsgBox, PresentationTime};
14
15pub const SCTE35_SCHEME: &str = "urn:scte:scte35:2013:bin";
18
19#[derive(Debug, Clone)]
21pub struct EmsgConfig {
22 pub timescale: u32,
24 pub presentation: PresentationTime,
26 pub event_duration: u32,
28 pub value: String,
30 pub id: u32,
32}
33
34pub fn scte35_to_emsg(splice_raw: &[u8], cfg: &EmsgConfig) -> Result<Vec<u8>> {
36 let boxx = EmsgBox {
37 scheme_id_uri: SCTE35_SCHEME,
38 value: &cfg.value,
39 timescale: cfg.timescale,
40 presentation_time: cfg.presentation,
41 event_duration: cfg.event_duration,
42 id: cfg.id,
43 message_data: splice_raw,
44 };
45 Ok(boxx.to_vec()?)
46}
47
48pub fn emsg_to_scte35(emsg_bytes: &[u8]) -> Result<Vec<u8>> {
50 let boxx = EmsgBox::parse(emsg_bytes)?;
51 if !boxx.is_scte35() {
52 return Err(Error::UnsupportedScheme {
53 scheme: String::from(boxx.scheme_id_uri),
54 });
55 }
56 Ok(boxx.message_data.to_vec())
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use alloc::string::ToString;
63
64 fn splice_2002() -> alloc::vec::Vec<u8> {
65 let hex = "FC302100000000000000FFF01005000007D27FEF7F7E0020F580C0000000000088B9661D";
66 (0..hex.len())
67 .step_by(2)
68 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
69 .collect()
70 }
71
72 #[test]
73 fn scte35_to_emsg_embeds_splice_verbatim_then_round_trips() {
74 let splice = splice_2002();
75 let cfg = EmsgConfig {
76 timescale: 90_000,
77 presentation: PresentationTime::Delta(0),
78 event_duration: 2_160_000,
79 value: "1".to_string(),
80 id: 1,
81 };
82 let emsg = scte35_to_emsg(&splice, &cfg).unwrap();
83 let extracted = emsg_to_scte35(&emsg).unwrap();
85 assert_eq!(extracted, splice);
86 }
87}