diff --git a/src/mp4box/meta.rs b/src/mp4box/meta.rs index df57c77..56ca816 100644 --- a/src/mp4box/meta.rs +++ b/src/mp4box/meta.rs @@ -21,7 +21,7 @@ pub enum MetaBox { hdlr: HdlrBox, #[serde(skip)] - data: Vec, + data: Vec<(BoxType, Vec)>, }, } @@ -41,7 +41,13 @@ impl MetaBox { size += ilst.box_size(); } } - Self::Unknown { hdlr, data } => size += hdlr.box_size() + data.len() as u64, + Self::Unknown { hdlr, data } => { + size += hdlr.box_size() + + data + .iter() + .map(|(_, data)| data.len() as u64 + HEADER_SIZE) + .sum::() + } } size } @@ -84,22 +90,56 @@ impl ReadBox<&mut R> for MetaBox { fn read_box(reader: &mut R, size: u64) -> Result { let start = box_start(reader)?; - let (version, _) = read_box_header_ext(reader)?; - if version != 0 { - return Err(Error::UnsupportedBoxVersion(BoxType::UdtaBox, version)); + let extended_header = reader.read_u32::()?; + if extended_header != 0 { + // ISO mp4 requires this header (version & flags) to be 0. Some + // files skip the extended header and directly start the hdlr box. + let possible_hdlr = BoxType::from(reader.read_u32::()?); + if possible_hdlr == BoxType::HdlrBox { + // This file skipped the extended header! Go back to start. + reader.seek(SeekFrom::Current(-8))?; + } else { + // Looks like we actually have a bad version number or flags. + let v = (extended_header >> 24) as u8; + return Err(Error::UnsupportedBoxVersion(BoxType::MetaBox, v)); + } } - let hdlr_header = BoxHeader::read(reader)?; - if hdlr_header.name != BoxType::HdlrBox { - return Err(Error::BoxNotFound(BoxType::HdlrBox)); - } - let hdlr = HdlrBox::read_box(reader, hdlr_header.size)?; - - let mut ilst = None; - let mut current = reader.stream_position()?; let end = start + size; + let content_start = current; + + // find the hdlr box + let mut hdlr = None; + while current < end { + // Get box header. + let header = BoxHeader::read(reader)?; + let BoxHeader { name, size: s } = header; + + match name { + BoxType::HdlrBox => { + hdlr = Some(HdlrBox::read_box(reader, s)?); + } + _ => { + // XXX warn!() + skip_box(reader, s)?; + } + } + + current = reader.stream_position()?; + } + + let Some(hdlr) = hdlr else { + return Err(Error::BoxNotFound(BoxType::HdlrBox)); + }; + + // rewind and handle the other boxes + reader.seek(SeekFrom::Start(content_start))?; + current = reader.stream_position()?; + + let mut ilst = None; + match hdlr.handler_type { MDIR => { while current < end { @@ -123,8 +163,27 @@ impl ReadBox<&mut R> for MetaBox { Ok(MetaBox::Mdir { ilst }) } _ => { - let mut data = vec![0u8; (end - current) as usize]; - reader.read_exact(&mut data)?; + let mut data = Vec::new(); + + while current < end { + // Get box header. + let header = BoxHeader::read(reader)?; + let BoxHeader { name, size: s } = header; + + match name { + BoxType::HdlrBox => { + skip_box(reader, s)?; + } + _ => { + let mut box_data = vec![0; (s - HEADER_SIZE) as usize]; + reader.read_exact(&mut box_data)?; + + data.push((name, box_data)); + } + } + + current = reader.stream_position()?; + } Ok(MetaBox::Unknown { hdlr, data }) } @@ -154,7 +213,12 @@ impl WriteBox<&mut W> for MetaBox { ilst.write_box(writer)?; } } - Self::Unknown { data, .. } => writer.write_all(data)?, + Self::Unknown { data, .. } => { + for (box_type, data) in data { + BoxHeader::new(*box_type, data.len() as u64 + HEADER_SIZE).write(writer)?; + writer.write_all(data)?; + } + } } Ok(size) } @@ -202,16 +266,35 @@ mod tests { assert_eq!(dst_box, src_box); } + #[test] + fn test_meta_hdrl_non_first() { + 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"; + let mut reader = Cursor::new(data); + let header = BoxHeader::read(&mut reader).unwrap(); + assert_eq!(header.name, BoxType::MetaBox); + + let meta_box = MetaBox::read_box(&mut reader, header.size).unwrap(); + + // this contains \xa9too box in the ilst + // it designates the tool that created the file, but is not yet supported by this crate + assert_eq!( + meta_box, + MetaBox::Mdir { + ilst: Some(IlstBox::default()) + } + ); + } + #[test] fn test_meta_unknown() { let src_hdlr = HdlrBox { handler_type: FourCC::from(*b"test"), ..Default::default() }; - let src_data = b"123"; + let src_data = (BoxType::UnknownBox(0x42494241), b"123".to_vec()); let src_box = MetaBox::Unknown { hdlr: src_hdlr, - data: src_data.to_vec(), + data: vec![src_data], }; let mut buf = Vec::new(); diff --git a/src/mp4box/stsc.rs b/src/mp4box/stsc.rs index e5b444e..a2b034b 100644 --- a/src/mp4box/stsc.rs +++ b/src/mp4box/stsc.rs @@ -85,7 +85,7 @@ impl ReadBox<&mut R> for StscBox { let mut sample_id = 1; for i in 0..entry_count { let (first_chunk, samples_per_chunk) = { - let mut entry = entries.get_mut(i as usize).unwrap(); + let entry = entries.get_mut(i as usize).unwrap(); entry.first_sample = sample_id; (entry.first_chunk, entry.samples_per_chunk) }; diff --git a/src/types.rs b/src/types.rs index 19fd40b..0983468 100644 --- a/src/types.rs +++ b/src/types.rs @@ -657,20 +657,15 @@ pub fn creation_time(creation_time: u64) -> u64 { } } -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)] pub enum DataType { + #[default] Binary = 0x000000, Text = 0x000001, Image = 0x00000D, TempoCpil = 0x000015, } -impl std::default::Default for DataType { - fn default() -> Self { - DataType::Binary - } -} - impl TryFrom for DataType { type Error = Error; fn try_from(value: u32) -> Result {