flowly_mp4/mp4box/
emsg.rs

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