From 93acb31c2d0cb17622b6be1fb38a7e23de4cf077 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 22 Jul 2021 09:46:28 -0600 Subject: [PATCH] add `creation` and `modification` fields to `MediaContext` The mvhd creation and modification times are now extracted from the moov if present. While these aren't needed to play the MP4, they can be useful for applications which allow sorting and/or searching by creation or modification time. --- mp4parse/src/lib.rs | 53 ++++++++++++++++++++++++++++++++----------- mp4parse/src/tests.rs | 2 +- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/mp4parse/src/lib.rs b/mp4parse/src/lib.rs index d9c6273c..4b3c5b1e 100644 --- a/mp4parse/src/lib.rs +++ b/mp4parse/src/lib.rs @@ -956,6 +956,8 @@ impl FileTypeBox { /// Movie header box 'mvhd'. #[derive(Debug)] struct MovieHeaderBox { + creation: u64, + modification: u64, pub timescale: u32, #[allow(dead_code)] // See https://github.com/mozilla/mp4parse-rust/issues/340 duration: u64, @@ -1503,9 +1505,14 @@ pub enum XmlBox { BinaryXmlBox(TryVec), } +#[derive(Debug)] +pub struct UtcSecondsSince1904(pub u64); + /// Internal data structures. #[derive(Debug, Default)] pub struct MediaContext { + pub creation: Option, + pub modification: Option, pub timescale: Option, /// Tracks found in the file. pub tracks: TryVec, @@ -4123,8 +4130,14 @@ fn parse_mvhd(f: &mut BMFFBox) -> Result> { if mvhd.timescale == 0 { return Status::MvhdBadTimescale.into(); } - let timescale = Some(MediaTimeScale(u64::from(mvhd.timescale))); - Ok(timescale) +} + +fn validate_timescale(mvhd: &MovieHeaderBox) -> Result> { + if mvhd.timescale == 0 { + Err(Error::InvalidData("zero timescale in mdhd")) + } else { + Ok(Some(MediaTimeScale(u64::from(mvhd.timescale)))) + } } /// Parse a Movie Box @@ -4134,6 +4147,8 @@ fn parse_mvhd(f: &mut BMFFBox) -> Result> { /// such as with tests/test_case_1185230.mp4. fn read_moov(f: &mut BMFFBox, context: Option) -> Result { let MediaContext { + mut creation, + mut modification, mut timescale, mut tracks, mut mvex, @@ -4147,7 +4162,11 @@ fn read_moov(f: &mut BMFFBox, context: Option) -> Resu while let Some(mut b) = iter.next_box()? { match b.head.name { BoxType::MovieHeaderBox => { - timescale = parse_mvhd(&mut b)?; + let mvhd = read_mvhd(&mut b)?; + debug!("{:?}", mvhd); + creation = Some(UtcSecondsSince1904(mvhd.creation)); + modification = Some(UtcSecondsSince1904(mvhd.modification)); + timescale = validate_timescale(&mvhd)?; } BoxType::TrackBox => { let mut track = Track::new(tracks.len()); @@ -4178,6 +4197,8 @@ fn read_moov(f: &mut BMFFBox, context: Option) -> Resu } Ok(MediaContext { + creation, + modification, timescale, tracks, mvex, @@ -4490,28 +4511,32 @@ fn read_ftyp(src: &mut BMFFBox) -> Result { /// Parse an mvhd box. fn read_mvhd(src: &mut BMFFBox) -> Result { let (version, _) = read_fullbox_extra(src)?; + let to_u64 = |n| { + if n == std::u32::MAX { + std::u64::MAX + } else { + u64::from(n) + } + }; + let creation; + let modification; match version { // 64 bit creation and modification times. 1 => { - skip(src, 16)?; + creation = be_u64(src)?; + modification = be_u64(src)?; } // 32 bit creation and modification times. 0 => { - skip(src, 8)?; + creation = to_u64(be_u32(src)?); + modification = to_u64(be_u32(src)?); } _ => return Status::MvhdBadVersion.into(), } let timescale = be_u32(src)?; let duration = match version { 1 => be_u64(src)?, - 0 => { - let d = be_u32(src)?; - if d == u32::MAX { - u64::MAX - } else { - u64::from(d) - } - } + 0 => to_u64(be_u32(src)?), _ => unreachable!("Should have returned Status::MvhdBadVersion"), }; // Skip remaining valid fields. @@ -4520,6 +4545,8 @@ fn read_mvhd(src: &mut BMFFBox) -> Result { // Padding could be added in some contents. skip_box_remain(src)?; Ok(MovieHeaderBox { + creation, + modification, timescale, duration, }) diff --git a/mp4parse/src/tests.rs b/mp4parse/src/tests.rs index 964d4f77..bf98ca95 100644 --- a/mp4parse/src/tests.rs +++ b/mp4parse/src/tests.rs @@ -401,7 +401,7 @@ fn read_mvhd_invalid_timescale() { let mut stream = iter.next_box().unwrap().unwrap(); assert_eq!(stream.head.name, BoxType::MovieHeaderBox); assert_eq!(stream.head.size, 120); - let r = super::parse_mvhd(&mut stream); + let r = super::validate_timescale(&super::read_mvhd(&mut stream).unwrap()); assert!(r.is_err()); }