diff --git a/mp4parse/src/lib.rs b/mp4parse/src/lib.rs index 32d1ea75..05eeeb95 100644 --- a/mp4parse/src/lib.rs +++ b/mp4parse/src/lib.rs @@ -266,6 +266,8 @@ struct FileTypeBox { /// Movie header box 'mvhd'. #[derive(Debug)] struct MovieHeaderBox { + creation: u64, + modification: u64, pub timescale: u32, duration: u64, } @@ -777,9 +779,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, @@ -2866,16 +2873,12 @@ pub fn read_mp4(f: &mut T) -> Result { context.ok_or(Error::NoMoov) } -/// Parse a Movie Header Box -/// See ISOBMFF (ISO 14496-12:2015) ยง 8.2.2 -fn parse_mvhd(f: &mut BMFFBox) -> Result> { - let mvhd = read_mvhd(f)?; - debug!("{:?}", mvhd); +fn validate_timescale(mvhd: &MovieHeaderBox) -> Result> { if mvhd.timescale == 0 { - return Err(Error::InvalidData("zero timescale in mdhd")); + Err(Error::InvalidData("zero timescale in mdhd")) + } else { + Ok(Some(MediaTimeScale(u64::from(mvhd.timescale)))) } - let timescale = Some(MediaTimeScale(u64::from(mvhd.timescale))); - Ok(timescale) } /// Parse a Movie Box @@ -2885,6 +2888,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, @@ -2898,7 +2903,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()); @@ -2929,6 +2938,8 @@ fn read_moov(f: &mut BMFFBox, context: Option) -> Resu } Ok(MediaContext { + creation, + modification, timescale, tracks, mvex, @@ -3204,33 +3215,39 @@ 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 Err(Error::InvalidData("unhandled mvhd version")), } let timescale = be_u32(src)?; let duration = match version { 1 => be_u64(src)?, - 0 => { - let d = be_u32(src)?; - if d == std::u32::MAX { - std::u64::MAX - } else { - u64::from(d) - } - } + 0 => to_u64(be_u32(src)?), _ => return Err(Error::InvalidData("unhandled mvhd version")), }; // Skip remaining fields. skip(src, 80)?; Ok(MovieHeaderBox { + creation, + modification, timescale, duration, }) diff --git a/mp4parse/src/tests.rs b/mp4parse/src/tests.rs index 56b739c6..1f925549 100644 --- a/mp4parse/src/tests.rs +++ b/mp4parse/src/tests.rs @@ -385,7 +385,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()); }