Skip to main content

mp4/mp4box/
emsg.rs

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 + // id
27            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}