Skip to content

Commit

Permalink
add creation and modification fields to MediaContext
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dicej committed Dec 24, 2024
1 parent f3b8209 commit 43ae32f
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 20 deletions.
55 changes: 36 additions & 19 deletions mp4parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1503,9 +1505,14 @@ pub enum XmlBox {
BinaryXmlBox(TryVec<u8>),
}

#[derive(Debug)]
pub struct UtcSecondsSince1904(pub u64);

/// Internal data structures.
#[derive(Debug, Default)]
pub struct MediaContext {
pub creation: Option<UtcSecondsSince1904>,
pub modification: Option<UtcSecondsSince1904>,
pub timescale: Option<MediaTimeScale>,
/// Tracks found in the file.
pub tracks: TryVec<Track>,
Expand Down Expand Up @@ -4115,16 +4122,12 @@ pub fn read_mp4<T: Read>(f: &mut T) -> Result<MediaContext> {
context.ok_or(Error::MoovMissing)
}

/// Parse a Movie Header Box
/// See ISOBMFF (ISO 14496-12:2020) § 8.2.2
fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
let mvhd = read_mvhd(f)?;
debug!("{:?}", mvhd);
fn validate_timescale(mvhd: &MovieHeaderBox) -> Result<Option<MediaTimeScale>> {
if mvhd.timescale == 0 {
return Status::MvhdBadTimescale.into();
Err(Error::InvalidData(Status::MdhdBadTimescale))
} else {
Ok(Some(MediaTimeScale(u64::from(mvhd.timescale))))
}
let timescale = Some(MediaTimeScale(u64::from(mvhd.timescale)));
Ok(timescale)
}

/// Parse a Movie Box
Expand All @@ -4134,6 +4137,8 @@ fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
/// such as with tests/test_case_1185230.mp4.
fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Result<MediaContext> {
let MediaContext {
mut creation,
mut modification,
mut timescale,
mut tracks,
mut mvex,
Expand All @@ -4147,7 +4152,11 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> 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());
Expand Down Expand Up @@ -4178,6 +4187,8 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Resu
}

Ok(MediaContext {
creation,
modification,
timescale,
tracks,
mvex,
Expand Down Expand Up @@ -4490,28 +4501,32 @@ fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
/// Parse an mvhd box.
fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
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.
Expand All @@ -4520,6 +4535,8 @@ fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
// Padding could be added in some contents.
skip_box_remain(src)?;
Ok(MovieHeaderBox {
creation,
modification,
timescale,
duration,
})
Expand Down
2 changes: 1 addition & 1 deletion mp4parse/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down

0 comments on commit 43ae32f

Please sign in to comment.