1use std::io::{Read, Seek};
2
3use serde::Serialize;
4
5use crate::mp4box::hdlr::HdlrBox;
6use crate::mp4box::ilst::IlstBox;
7use crate::mp4box::*;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
10#[serde(tag = "hdlr")]
11#[serde(rename_all = "lowercase")]
12pub enum MetaBox {
13 Mdir {
14 #[serde(skip_serializing_if = "Option::is_none")]
15 ilst: Option<IlstBox>,
16 },
17
18 #[serde(skip)]
19 Unknown {
20 #[serde(skip)]
21 hdlr: HdlrBox,
22
23 #[serde(skip)]
24 data: Vec<(BoxType, Vec<u8>)>,
25 },
26}
27
28const MDIR: FourCC = FourCC { value: *b"mdir" };
29
30impl MetaBox {
31 pub fn get_type(&self) -> BoxType {
32 BoxType::MetaBox
33 }
34
35 pub fn get_size(&self) -> u64 {
36 let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
37 match self {
38 Self::Mdir { ilst } => {
39 size += HdlrBox::default().box_size();
40 if let Some(ilst) = ilst {
41 size += ilst.box_size();
42 }
43 }
44 Self::Unknown { hdlr, data } => {
45 size += hdlr.box_size()
46 + data
47 .iter()
48 .map(|(_, data)| data.len() as u64 + HEADER_SIZE)
49 .sum::<u64>()
50 }
51 }
52 size
53 }
54}
55
56impl Mp4Box for MetaBox {
57 fn box_type(&self) -> BoxType {
58 self.get_type()
59 }
60
61 fn box_size(&self) -> u64 {
62 self.get_size()
63 }
64
65 fn to_json(&self) -> Result<String> {
66 Ok(serde_json::to_string(&self).unwrap())
67 }
68
69 fn summary(&self) -> Result<String> {
70 let s = match self {
71 Self::Mdir { .. } => "hdlr=ilst".to_string(),
72 Self::Unknown { hdlr, data } => {
73 format!("hdlr={} data_len={}", hdlr.handler_type, data.len())
74 }
75 };
76 Ok(s)
77 }
78}
79
80impl Default for MetaBox {
81 fn default() -> Self {
82 Self::Unknown {
83 hdlr: Default::default(),
84 data: Default::default(),
85 }
86 }
87}
88
89impl<R: Read + Seek> ReadBox<&mut R> for MetaBox {
90 fn read_box(reader: &mut R, size: u64) -> Result<Self> {
91 let start = box_start(reader)?;
92
93 let extended_header = reader.read_u32::<BigEndian>()?;
94 if extended_header != 0 {
95 let possible_hdlr = BoxType::from(reader.read_u32::<BigEndian>()?);
98 if possible_hdlr == BoxType::HdlrBox {
99 reader.seek(SeekFrom::Current(-8))?;
101 } else {
102 let v = (extended_header >> 24) as u8;
104 return Err(Error::UnsupportedBoxVersion(BoxType::MetaBox, v));
105 }
106 }
107
108 let mut current = reader.stream_position()?;
109 let end = start + size;
110
111 let content_start = current;
112
113 let mut hdlr = None;
115 while current < end {
116 let header = BoxHeader::read(reader)?;
118 let BoxHeader { name, size: s } = header;
119
120 match name {
121 BoxType::HdlrBox => {
122 hdlr = Some(HdlrBox::read_box(reader, s)?);
123 }
124 _ => {
125 skip_box(reader, s)?;
127 }
128 }
129
130 current = reader.stream_position()?;
131 }
132
133 let Some(hdlr) = hdlr else {
134 return Err(Error::BoxNotFound(BoxType::HdlrBox));
135 };
136
137 reader.seek(SeekFrom::Start(content_start))?;
139 current = reader.stream_position()?;
140
141 let mut ilst = None;
142
143 match hdlr.handler_type {
144 MDIR => {
145 while current < end {
146 let header = BoxHeader::read(reader)?;
148 let BoxHeader { name, size: s } = header;
149
150 match name {
151 BoxType::IlstBox => {
152 ilst = Some(IlstBox::read_box(reader, s)?);
153 }
154 _ => {
155 skip_box(reader, s)?;
157 }
158 }
159
160 current = reader.stream_position()?;
161 }
162
163 Ok(MetaBox::Mdir { ilst })
164 }
165 _ => {
166 let mut data = Vec::new();
167
168 while current < end {
169 let header = BoxHeader::read(reader)?;
171 let BoxHeader { name, size: s } = header;
172
173 match name {
174 BoxType::HdlrBox => {
175 skip_box(reader, s)?;
176 }
177 _ => {
178 let mut box_data = vec![0; (s - HEADER_SIZE) as usize];
179 reader.read_exact(&mut box_data)?;
180
181 data.push((name, box_data));
182 }
183 }
184
185 current = reader.stream_position()?;
186 }
187
188 Ok(MetaBox::Unknown { hdlr, data })
189 }
190 }
191 }
192}
193
194impl<W: Write> WriteBox<&mut W> for MetaBox {
195 fn write_box(&self, writer: &mut W) -> Result<u64> {
196 let size = self.box_size();
197 BoxHeader::new(self.box_type(), size).write(writer)?;
198
199 write_box_header_ext(writer, 0, 0)?;
200
201 let hdlr = match self {
202 Self::Mdir { .. } => HdlrBox {
203 handler_type: MDIR,
204 ..Default::default()
205 },
206 Self::Unknown { hdlr, .. } => hdlr.clone(),
207 };
208 hdlr.write_box(writer)?;
209
210 match self {
211 Self::Mdir { ilst } => {
212 if let Some(ilst) = ilst {
213 ilst.write_box(writer)?;
214 }
215 }
216 Self::Unknown { data, .. } => {
217 for (box_type, data) in data {
218 BoxHeader::new(*box_type, data.len() as u64 + HEADER_SIZE).write(writer)?;
219 writer.write_all(data)?;
220 }
221 }
222 }
223 Ok(size)
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use crate::mp4box::BoxHeader;
231 use std::io::Cursor;
232
233 #[test]
234 fn test_meta_mdir_empty() {
235 let src_box = MetaBox::Mdir { ilst: None };
236
237 let mut buf = Vec::new();
238 src_box.write_box(&mut buf).unwrap();
239 assert_eq!(buf.len(), src_box.box_size() as usize);
240
241 let mut reader = Cursor::new(&buf);
242 let header = BoxHeader::read(&mut reader).unwrap();
243 assert_eq!(header.name, BoxType::MetaBox);
244 assert_eq!(header.size, src_box.box_size());
245
246 let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
247 assert_eq!(dst_box, src_box);
248 }
249
250 #[test]
251 fn test_meta_mdir() {
252 let src_box = MetaBox::Mdir {
253 ilst: Some(IlstBox::default()),
254 };
255
256 let mut buf = Vec::new();
257 src_box.write_box(&mut buf).unwrap();
258 assert_eq!(buf.len(), src_box.box_size() as usize);
259
260 let mut reader = Cursor::new(&buf);
261 let header = BoxHeader::read(&mut reader).unwrap();
262 assert_eq!(header.name, BoxType::MetaBox);
263 assert_eq!(header.size, src_box.box_size());
264
265 let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
266 assert_eq!(dst_box, src_box);
267 }
268
269 #[test]
270 fn test_meta_hdrl_non_first() {
271 let data = b"\x00\x00\x00\x7fmeta\x00\x00\x00\x00\x00\x00\x00Qilst\x00\x00\x00I\xa9too\x00\x00\x00Adata\x00\x00\x00\x01\x00\x00\x00\x00TMPGEnc Video Mastering Works 7 Version 7.0.15.17\x00\x00\x00\"hdlr\x00\x00\x00\x00\x00\x00\x00\x00mdirappl\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
272 let mut reader = Cursor::new(data);
273 let header = BoxHeader::read(&mut reader).unwrap();
274 assert_eq!(header.name, BoxType::MetaBox);
275
276 let meta_box = MetaBox::read_box(&mut reader, header.size).unwrap();
277
278 assert_eq!(
281 meta_box,
282 MetaBox::Mdir {
283 ilst: Some(IlstBox::default())
284 }
285 );
286 }
287
288 #[test]
289 fn test_meta_unknown() {
290 let src_hdlr = HdlrBox {
291 handler_type: FourCC::from(*b"test"),
292 ..Default::default()
293 };
294 let src_data = (BoxType::UnknownBox(0x42494241), b"123".to_vec());
295 let src_box = MetaBox::Unknown {
296 hdlr: src_hdlr,
297 data: vec![src_data],
298 };
299
300 let mut buf = Vec::new();
301 src_box.write_box(&mut buf).unwrap();
302 assert_eq!(buf.len(), src_box.box_size() as usize);
303
304 let mut reader = Cursor::new(&buf);
305 let header = BoxHeader::read(&mut reader).unwrap();
306 assert_eq!(header.name, BoxType::MetaBox);
307 assert_eq!(header.size, src_box.box_size());
308
309 let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
310 assert_eq!(dst_box, src_box);
311 }
312}