1use std::ffi::CStr;
2use std::io::{Read, Seek};
3
4use byteorder::{BigEndian, ReadBytesExt as _};
5use serde::Serialize;
6
7use crate::mp4box::{
8 box_start, read_box_header_ext, skip_bytes_to, BoxType, Error, Mp4Box, ReadBox, Result,
9 HEADER_EXT_SIZE, HEADER_SIZE,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
13pub struct EmsgBox {
14 pub version: u8,
15 pub flags: u32,
16 pub timescale: u32,
17 pub presentation_time: Option<u64>,
18 pub presentation_time_delta: Option<u32>,
19 pub event_duration: u32,
20 pub id: u32,
21 pub scheme_id_uri: String,
22 pub value: String,
23 pub message_data: Vec<u8>,
24}
25
26impl EmsgBox {
27 fn size_without_message(version: u8, scheme_id_uri: &str, value: &str) -> u64 {
28 HEADER_SIZE + HEADER_EXT_SIZE +
29 4 + Self::time_size(version) +
31 (scheme_id_uri.len() + 1) as u64 +
32 (value.len() as u64 + 1)
33 }
34
35 fn time_size(version: u8) -> u64 {
36 match version {
37 0 => 12,
38 1 => 16,
39 _ => panic!("version must be 0 or 1"),
40 }
41 }
42}
43
44impl Mp4Box for EmsgBox {
45 fn box_type(&self) -> BoxType {
46 BoxType::EmsgBox
47 }
48
49 fn box_size(&self) -> u64 {
50 Self::size_without_message(self.version, &self.scheme_id_uri, &self.value)
51 + self.message_data.len() as u64
52 }
53
54 fn to_json(&self) -> Result<String> {
55 Ok(serde_json::to_string(&self).expect("Failed to convert to JSON"))
56 }
57
58 fn summary(&self) -> Result<String> {
59 let s = format!("id={} value={}", self.id, self.value);
60 Ok(s)
61 }
62}
63
64impl<R: Read + Seek> ReadBox<&mut R> for EmsgBox {
65 fn read_box(reader: &mut R, size: u64) -> Result<Self> {
66 let start = box_start(reader)?;
67 let (version, flags) = read_box_header_ext(reader)?;
68
69 let (
70 timescale,
71 presentation_time,
72 presentation_time_delta,
73 event_duration,
74 id,
75 scheme_id_uri,
76 value,
77 ) = match version {
78 0 => {
79 let scheme_id_uri = read_null_terminated_utf8_string(reader)?;
80 let value = read_null_terminated_utf8_string(reader)?;
81 (
82 reader.read_u32::<BigEndian>()?,
83 None,
84 Some(reader.read_u32::<BigEndian>()?),
85 reader.read_u32::<BigEndian>()?,
86 reader.read_u32::<BigEndian>()?,
87 scheme_id_uri,
88 value,
89 )
90 }
91 1 => (
92 reader.read_u32::<BigEndian>()?,
93 Some(reader.read_u64::<BigEndian>()?),
94 None,
95 reader.read_u32::<BigEndian>()?,
96 reader.read_u32::<BigEndian>()?,
97 read_null_terminated_utf8_string(reader)?,
98 read_null_terminated_utf8_string(reader)?,
99 ),
100 _ => return Err(Error::InvalidData("version must be 0 or 1")),
101 };
102
103 let message_size = size - Self::size_without_message(version, &scheme_id_uri, &value);
104 let mut message_data = Vec::with_capacity(message_size as usize);
105 for _ in 0..message_size {
106 message_data.push(reader.read_u8()?);
107 }
108
109 skip_bytes_to(reader, start + size)?;
110
111 Ok(Self {
112 version,
113 flags,
114 timescale,
115 presentation_time,
116 presentation_time_delta,
117 event_duration,
118 id,
119 scheme_id_uri,
120 value,
121 message_data,
122 })
123 }
124}
125
126fn read_null_terminated_utf8_string<R: Read + Seek>(reader: &mut R) -> Result<String> {
127 let mut bytes = Vec::new();
128 loop {
129 let byte = reader.read_u8()?;
130 bytes.push(byte);
131 if byte == 0 {
132 break;
133 }
134 }
135 #[expect(unsafe_code)]
136 if let Ok(str) = unsafe { CStr::from_bytes_with_nul_unchecked(&bytes) }.to_str() {
138 Ok(str.to_owned())
139 } else {
140 Err(Error::InvalidData("invalid utf8"))
141 }
142}