1use std::ffi::CStr;
2use std::io::{Read, Seek, Write};
3
4use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
5use serde::Serialize;
6
7use crate::mp4box::*;
8
9#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
10pub struct EmsgBox {
11 pub version: u8,
12 pub flags: u32,
13 pub timescale: u32,
14 pub presentation_time: Option<u64>,
15 pub presentation_time_delta: Option<u32>,
16 pub event_duration: u32,
17 pub id: u32,
18 pub scheme_id_uri: String,
19 pub value: String,
20 pub message_data: Vec<u8>,
21}
22
23impl EmsgBox {
24 fn size_without_message(version: u8, scheme_id_uri: &str, value: &str) -> u64 {
25 HEADER_SIZE + HEADER_EXT_SIZE +
26 4 + Self::time_size(version) +
28 (scheme_id_uri.len() + 1) as u64 +
29 (value.len() as u64 + 1)
30 }
31
32 fn time_size(version: u8) -> u64 {
33 match version {
34 0 => 12,
35 1 => 16,
36 _ => panic!("version must be 0 or 1"),
37 }
38 }
39}
40
41impl Mp4Box for EmsgBox {
42 fn box_type(&self) -> BoxType {
43 BoxType::EmsgBox
44 }
45
46 fn box_size(&self) -> u64 {
47 Self::size_without_message(self.version, &self.scheme_id_uri, &self.value)
48 + self.message_data.len() as u64
49 }
50
51 fn to_json(&self) -> Result<String> {
52 Ok(serde_json::to_string(&self).unwrap())
53 }
54
55 fn summary(&self) -> Result<String> {
56 let s = format!("id={} value={}", self.id, self.value);
57 Ok(s)
58 }
59}
60
61impl<R: Read + Seek> ReadBox<&mut R> for EmsgBox {
62 fn read_box(reader: &mut R, size: u64) -> Result<Self> {
63 let start = box_start(reader)?;
64 let (version, flags) = read_box_header_ext(reader)?;
65
66 let (
67 timescale,
68 presentation_time,
69 presentation_time_delta,
70 event_duration,
71 id,
72 scheme_id_uri,
73 value,
74 ) = match version {
75 0 => {
76 let scheme_id_uri = read_null_terminated_utf8_string(reader)?;
77 let value = read_null_terminated_utf8_string(reader)?;
78 (
79 reader.read_u32::<BigEndian>()?,
80 None,
81 Some(reader.read_u32::<BigEndian>()?),
82 reader.read_u32::<BigEndian>()?,
83 reader.read_u32::<BigEndian>()?,
84 scheme_id_uri,
85 value,
86 )
87 }
88 1 => (
89 reader.read_u32::<BigEndian>()?,
90 Some(reader.read_u64::<BigEndian>()?),
91 None,
92 reader.read_u32::<BigEndian>()?,
93 reader.read_u32::<BigEndian>()?,
94 read_null_terminated_utf8_string(reader)?,
95 read_null_terminated_utf8_string(reader)?,
96 ),
97 _ => return Err(Error::InvalidData("version must be 0 or 1")),
98 };
99
100 let message_size = size - Self::size_without_message(version, &scheme_id_uri, &value);
101 let mut message_data = Vec::with_capacity(message_size as usize);
102 for _ in 0..message_size {
103 message_data.push(reader.read_u8()?);
104 }
105
106 skip_bytes_to(reader, start + size)?;
107
108 Ok(EmsgBox {
109 version,
110 flags,
111 timescale,
112 presentation_time,
113 presentation_time_delta,
114 event_duration,
115 id,
116 scheme_id_uri,
117 value,
118 message_data,
119 })
120 }
121}
122
123impl<W: Write> WriteBox<&mut W> for EmsgBox {
124 fn write_box(&self, writer: &mut W) -> Result<u64> {
125 let size = self.box_size();
126 BoxHeader::new(self.box_type(), size).write(writer)?;
127
128 write_box_header_ext(writer, self.version, self.flags)?;
129 match self.version {
130 0 => {
131 write_null_terminated_str(writer, &self.scheme_id_uri)?;
132 write_null_terminated_str(writer, &self.value)?;
133 writer.write_u32::<BigEndian>(self.timescale)?;
134 writer.write_u32::<BigEndian>(self.presentation_time_delta.unwrap())?;
135 writer.write_u32::<BigEndian>(self.event_duration)?;
136 writer.write_u32::<BigEndian>(self.id)?;
137 }
138 1 => {
139 writer.write_u32::<BigEndian>(self.timescale)?;
140 writer.write_u64::<BigEndian>(self.presentation_time.unwrap())?;
141 writer.write_u32::<BigEndian>(self.event_duration)?;
142 writer.write_u32::<BigEndian>(self.id)?;
143 write_null_terminated_str(writer, &self.scheme_id_uri)?;
144 write_null_terminated_str(writer, &self.value)?;
145 }
146 _ => return Err(Error::InvalidData("version must be 0 or 1")),
147 }
148
149 for &byte in &self.message_data {
150 writer.write_u8(byte)?;
151 }
152
153 Ok(size)
154 }
155}
156
157fn read_null_terminated_utf8_string<R: Read + Seek>(reader: &mut R) -> Result<String> {
158 let mut bytes = Vec::new();
159 loop {
160 let byte = reader.read_u8()?;
161 bytes.push(byte);
162 if byte == 0 {
163 break;
164 }
165 }
166 if let Ok(str) = unsafe { CStr::from_bytes_with_nul_unchecked(&bytes) }.to_str() {
167 Ok(str.to_string())
168 } else {
169 Err(Error::InvalidData("invalid utf8"))
170 }
171}
172
173fn write_null_terminated_str<W: Write>(writer: &mut W, string: &str) -> Result<()> {
174 for byte in string.bytes() {
175 writer.write_u8(byte)?;
176 }
177 writer.write_u8(0)?;
178 Ok(())
179}
180
181#[cfg(test)]
182mod tests {
183 use std::io::Cursor;
184
185 use crate::mp4box::BoxHeader;
186
187 use super::*;
188
189 #[test]
190 fn test_emsg_version0() {
191 let src_box = EmsgBox {
192 version: 0,
193 flags: 0,
194 timescale: 48000,
195 presentation_time: None,
196 presentation_time_delta: Some(100),
197 event_duration: 200,
198 id: 8,
199 scheme_id_uri: String::from("foo"),
200 value: String::from("foo"),
201 message_data: vec![1, 2, 3],
202 };
203 let mut buf = Vec::new();
204 src_box.write_box(&mut buf).unwrap();
205 assert_eq!(buf.len(), src_box.box_size() as usize);
206
207 let mut reader = Cursor::new(&buf);
208 let header = BoxHeader::read(&mut reader).unwrap();
209 assert_eq!(header.name, BoxType::EmsgBox);
210 assert_eq!(src_box.box_size(), header.size);
211
212 let dst_box = EmsgBox::read_box(&mut reader, header.size).unwrap();
213 assert_eq!(src_box, dst_box);
214 }
215
216 #[test]
217 fn test_emsg_version1() {
218 let src_box = EmsgBox {
219 version: 1,
220 flags: 0,
221 timescale: 48000,
222 presentation_time: Some(50000),
223 presentation_time_delta: None,
224 event_duration: 200,
225 id: 8,
226 scheme_id_uri: String::from("foo"),
227 value: String::from("foo"),
228 message_data: vec![3, 2, 1],
229 };
230 let mut buf = Vec::new();
231 src_box.write_box(&mut buf).unwrap();
232 assert_eq!(buf.len(), src_box.box_size() as usize);
233
234 let mut reader = Cursor::new(&buf);
235 let header = BoxHeader::read(&mut reader).unwrap();
236 assert_eq!(header.name, BoxType::EmsgBox);
237 assert_eq!(src_box.box_size(), header.size);
238
239 let dst_box = EmsgBox::read_box(&mut reader, header.size).unwrap();
240 assert_eq!(src_box, dst_box);
241 }
242}