timed_metadata/convert/
emsg.rs1use crate::error::{Error, Result};
3use alloc::{string::String, vec::Vec};
4use mp4_emsg::{EmsgBox, PresentationTime};
5
6pub const SCTE35_SCHEME: &str = "urn:scte:scte35:2013:bin";
8
9#[derive(Debug, Clone)]
11pub struct EmsgConfig {
12 pub timescale: u32,
14 pub presentation: PresentationTime,
16 pub event_duration: u32,
18 pub value: String,
20 pub id: u32,
22}
23
24pub 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
38pub 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 let extracted = emsg_to_scte35(&emsg).unwrap();
75 assert_eq!(extracted, splice);
76 }
77}