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