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 Jul 23, 2021
1 parent 61c1030 commit ea703bb
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 19 deletions.
53 changes: 35 additions & 18 deletions mp4parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ struct FileTypeBox {
/// Movie header box 'mvhd'.
#[derive(Debug)]
struct MovieHeaderBox {
creation: u64,
modification: u64,
pub timescale: u32,
duration: u64,
}
Expand Down Expand Up @@ -777,9 +779,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 @@ -2866,16 +2873,12 @@ pub fn read_mp4<T: Read>(f: &mut T) -> Result<MediaContext> {
context.ok_or(Error::NoMoov)
}

/// Parse a Movie Header Box
/// See ISOBMFF (ISO 14496-12:2015) § 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 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
Expand All @@ -2885,6 +2888,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 @@ -2898,7 +2903,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 @@ -2929,6 +2938,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 @@ -3204,33 +3215,39 @@ 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 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,
})
Expand Down
2 changes: 1 addition & 1 deletion mp4parse/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down

0 comments on commit ea703bb

Please sign in to comment.