From 55875d72de32b6f5a4eecc950b8b1510b92fe933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Mon, 19 Jun 2023 05:38:41 +0200 Subject: [PATCH 1/4] Derive Default trait for DataType (#100) --- src/types.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) 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 { From aff3bf6c452c42428e0579e3654af5d62dab633b Mon Sep 17 00:00:00 2001 From: rolleifx <110799844+rolleifx@users.noreply.github.com> Date: Sat, 29 Jul 2023 10:21:34 +0800 Subject: [PATCH 2/4] fix clippy (rustc 1.71.0) (#115) --- src/mp4box/stsc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) }; From c1040472157d175ffc009144863f68f43bf39b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=AD=90=EF=B8=8FNINIKA=E2=AD=90=EF=B8=8F?= Date: Sat, 29 Jul 2023 06:43:13 +0300 Subject: [PATCH 3/4] Allow Hdlr to be not the first in the Meta box (#95) 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 Co-authored-by: Alfred Gutierrez --- src/mp4box/meta.rs | 103 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 15 deletions(-) diff --git a/src/mp4box/meta.rs b/src/mp4box/meta.rs index df57c77..6e155a2 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 } @@ -89,17 +95,41 @@ impl ReadBox<&mut R> for MetaBox { return Err(Error::UnsupportedBoxVersion(BoxType::UdtaBox, version)); } - 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 +153,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 +203,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 +256,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(); From d6c38642de48360693132fd07552a454016d71c3 Mon Sep 17 00:00:00 2001 From: w-flo Date: Sat, 29 Jul 2023 06:06:22 +0200 Subject: [PATCH 4/4] Try to skip extended header in MetaBox. (#111) Fixes #102 Co-authored-by: Alfred Gutierrez --- src/mp4box/meta.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/mp4box/meta.rs b/src/mp4box/meta.rs index 6e155a2..56ca816 100644 --- a/src/mp4box/meta.rs +++ b/src/mp4box/meta.rs @@ -90,9 +90,19 @@ 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 mut current = reader.stream_position()?;