Skip to content

Commit d735464

Browse files
committed
Allow Hdlr to be not the first in the Meta box
While the spec says that the hdlr box should be the first one, not all implementations follow that. Actually look over all boxes in Meta to find Hdlr. Also add a test for such weirdly-formatted box Change the way unknown MetaBox is stored: store a list of boxes instead of raw bytes
1 parent 55875d7 commit d735464

File tree

1 file changed

+87
-14
lines changed

1 file changed

+87
-14
lines changed

src/mp4box/meta.rs

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub enum MetaBox {
2121
hdlr: HdlrBox,
2222

2323
#[serde(skip)]
24-
data: Vec<u8>,
24+
data: Vec<(BoxType, Vec<u8>)>,
2525
},
2626
}
2727

@@ -41,7 +41,13 @@ impl MetaBox {
4141
size += ilst.box_size();
4242
}
4343
}
44-
Self::Unknown { hdlr, data } => size += hdlr.box_size() + data.len() as u64,
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+
}
4551
}
4652
size
4753
}
@@ -89,16 +95,40 @@ impl<R: Read + Seek> ReadBox<&mut R> for MetaBox {
8995
return Err(Error::UnsupportedBoxVersion(BoxType::UdtaBox, version));
9096
}
9197

92-
let hdlr_header = BoxHeader::read(reader)?;
93-
if hdlr_header.name != BoxType::HdlrBox {
94-
return Err(Error::BoxNotFound(BoxType::HdlrBox));
98+
let mut current = reader.stream_position()?;
99+
let end = start + size;
100+
101+
let content_start = current;
102+
103+
// find the hdlr box
104+
let mut hdlr = None;
105+
while current < end {
106+
// Get box header.
107+
let header = BoxHeader::read(reader)?;
108+
let BoxHeader { name, size: s } = header;
109+
110+
match name {
111+
BoxType::HdlrBox => {
112+
hdlr = Some(HdlrBox::read_box(reader, s)?);
113+
}
114+
_ => {
115+
// XXX warn!()
116+
skip_box(reader, s)?;
117+
}
118+
}
119+
120+
current = reader.stream_position()?;
95121
}
96-
let hdlr = HdlrBox::read_box(reader, hdlr_header.size)?;
97122

98-
let mut ilst = None;
123+
let Some(hdlr) = hdlr else {
124+
return Err(Error::BoxNotFound(BoxType::HdlrBox));
125+
};
99126

100-
let mut current = reader.stream_position()?;
101-
let end = start + size;
127+
// rewind and handle the other boxes
128+
reader.seek(SeekFrom::Start(content_start))?;
129+
current = reader.stream_position()?;
130+
131+
let mut ilst = None;
102132

103133
match hdlr.handler_type {
104134
MDIR => {
@@ -123,8 +153,27 @@ impl<R: Read + Seek> ReadBox<&mut R> for MetaBox {
123153
Ok(MetaBox::Mdir { ilst })
124154
}
125155
_ => {
126-
let mut data = vec![0u8; (end - current) as usize];
127-
reader.read_exact(&mut data)?;
156+
let mut data = Vec::new();
157+
158+
while current < end {
159+
// Get box header.
160+
let header = BoxHeader::read(reader)?;
161+
let BoxHeader { name, size: s } = header;
162+
163+
match name {
164+
BoxType::HdlrBox => {
165+
skip_box(reader, s)?;
166+
}
167+
_ => {
168+
let mut box_data = vec![0; (s - HEADER_SIZE) as usize];
169+
reader.read_exact(&mut box_data)?;
170+
171+
data.push((name, box_data));
172+
}
173+
}
174+
175+
current = reader.stream_position()?;
176+
}
128177

129178
Ok(MetaBox::Unknown { hdlr, data })
130179
}
@@ -154,7 +203,12 @@ impl<W: Write> WriteBox<&mut W> for MetaBox {
154203
ilst.write_box(writer)?;
155204
}
156205
}
157-
Self::Unknown { data, .. } => writer.write_all(data)?,
206+
Self::Unknown { data, .. } => {
207+
for (box_type, data) in data {
208+
BoxHeader::new(*box_type, data.len() as u64 + HEADER_SIZE).write(writer)?;
209+
writer.write_all(data)?;
210+
}
211+
}
158212
}
159213
Ok(size)
160214
}
@@ -202,16 +256,35 @@ mod tests {
202256
assert_eq!(dst_box, src_box);
203257
}
204258

259+
#[test]
260+
fn test_meta_hdrl_non_first() {
261+
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";
262+
let mut reader = Cursor::new(data);
263+
let header = BoxHeader::read(&mut reader).unwrap();
264+
assert_eq!(header.name, BoxType::MetaBox);
265+
266+
let meta_box = MetaBox::read_box(&mut reader, header.size).unwrap();
267+
268+
// this contains \xa9too box in the ilst
269+
// it designates the tool that created the file, but is not yet supported by this crate
270+
assert_eq!(
271+
meta_box,
272+
MetaBox::Mdir {
273+
ilst: Some(IlstBox::default())
274+
}
275+
);
276+
}
277+
205278
#[test]
206279
fn test_meta_unknown() {
207280
let src_hdlr = HdlrBox {
208281
handler_type: FourCC::from(*b"test"),
209282
..Default::default()
210283
};
211-
let src_data = b"123";
284+
let src_data = (BoxType::UnknownBox(0x42494241), b"123".to_vec());
212285
let src_box = MetaBox::Unknown {
213286
hdlr: src_hdlr,
214-
data: src_data.to_vec(),
287+
data: vec![src_data],
215288
};
216289

217290
let mut buf = Vec::new();

0 commit comments

Comments
 (0)