Skip to content

Commit 93acb31

Browse files
committed
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.
1 parent f3b8209 commit 93acb31

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

mp4parse/src/lib.rs

+40-13
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,8 @@ impl FileTypeBox {
956956
/// Movie header box 'mvhd'.
957957
#[derive(Debug)]
958958
struct MovieHeaderBox {
959+
creation: u64,
960+
modification: u64,
959961
pub timescale: u32,
960962
#[allow(dead_code)] // See https://github.com/mozilla/mp4parse-rust/issues/340
961963
duration: u64,
@@ -1503,9 +1505,14 @@ pub enum XmlBox {
15031505
BinaryXmlBox(TryVec<u8>),
15041506
}
15051507

1508+
#[derive(Debug)]
1509+
pub struct UtcSecondsSince1904(pub u64);
1510+
15061511
/// Internal data structures.
15071512
#[derive(Debug, Default)]
15081513
pub struct MediaContext {
1514+
pub creation: Option<UtcSecondsSince1904>,
1515+
pub modification: Option<UtcSecondsSince1904>,
15091516
pub timescale: Option<MediaTimeScale>,
15101517
/// Tracks found in the file.
15111518
pub tracks: TryVec<Track>,
@@ -4123,8 +4130,14 @@ fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
41234130
if mvhd.timescale == 0 {
41244131
return Status::MvhdBadTimescale.into();
41254132
}
4126-
let timescale = Some(MediaTimeScale(u64::from(mvhd.timescale)));
4127-
Ok(timescale)
4133+
}
4134+
4135+
fn validate_timescale(mvhd: &MovieHeaderBox) -> Result<Option<MediaTimeScale>> {
4136+
if mvhd.timescale == 0 {
4137+
Err(Error::InvalidData("zero timescale in mdhd"))
4138+
} else {
4139+
Ok(Some(MediaTimeScale(u64::from(mvhd.timescale))))
4140+
}
41284141
}
41294142

41304143
/// Parse a Movie Box
@@ -4134,6 +4147,8 @@ fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
41344147
/// such as with tests/test_case_1185230.mp4.
41354148
fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Result<MediaContext> {
41364149
let MediaContext {
4150+
mut creation,
4151+
mut modification,
41374152
mut timescale,
41384153
mut tracks,
41394154
mut mvex,
@@ -4147,7 +4162,11 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Resu
41474162
while let Some(mut b) = iter.next_box()? {
41484163
match b.head.name {
41494164
BoxType::MovieHeaderBox => {
4150-
timescale = parse_mvhd(&mut b)?;
4165+
let mvhd = read_mvhd(&mut b)?;
4166+
debug!("{:?}", mvhd);
4167+
creation = Some(UtcSecondsSince1904(mvhd.creation));
4168+
modification = Some(UtcSecondsSince1904(mvhd.modification));
4169+
timescale = validate_timescale(&mvhd)?;
41514170
}
41524171
BoxType::TrackBox => {
41534172
let mut track = Track::new(tracks.len());
@@ -4178,6 +4197,8 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Resu
41784197
}
41794198

41804199
Ok(MediaContext {
4200+
creation,
4201+
modification,
41814202
timescale,
41824203
tracks,
41834204
mvex,
@@ -4490,28 +4511,32 @@ fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
44904511
/// Parse an mvhd box.
44914512
fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
44924513
let (version, _) = read_fullbox_extra(src)?;
4514+
let to_u64 = |n| {
4515+
if n == std::u32::MAX {
4516+
std::u64::MAX
4517+
} else {
4518+
u64::from(n)
4519+
}
4520+
};
4521+
let creation;
4522+
let modification;
44934523
match version {
44944524
// 64 bit creation and modification times.
44954525
1 => {
4496-
skip(src, 16)?;
4526+
creation = be_u64(src)?;
4527+
modification = be_u64(src)?;
44974528
}
44984529
// 32 bit creation and modification times.
44994530
0 => {
4500-
skip(src, 8)?;
4531+
creation = to_u64(be_u32(src)?);
4532+
modification = to_u64(be_u32(src)?);
45014533
}
45024534
_ => return Status::MvhdBadVersion.into(),
45034535
}
45044536
let timescale = be_u32(src)?;
45054537
let duration = match version {
45064538
1 => be_u64(src)?,
4507-
0 => {
4508-
let d = be_u32(src)?;
4509-
if d == u32::MAX {
4510-
u64::MAX
4511-
} else {
4512-
u64::from(d)
4513-
}
4514-
}
4539+
0 => to_u64(be_u32(src)?),
45154540
_ => unreachable!("Should have returned Status::MvhdBadVersion"),
45164541
};
45174542
// Skip remaining valid fields.
@@ -4520,6 +4545,8 @@ fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
45204545
// Padding could be added in some contents.
45214546
skip_box_remain(src)?;
45224547
Ok(MovieHeaderBox {
4548+
creation,
4549+
modification,
45234550
timescale,
45244551
duration,
45254552
})

mp4parse/src/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ fn read_mvhd_invalid_timescale() {
401401
let mut stream = iter.next_box().unwrap().unwrap();
402402
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
403403
assert_eq!(stream.head.size, 120);
404-
let r = super::parse_mvhd(&mut stream);
404+
let r = super::validate_timescale(&super::read_mvhd(&mut stream).unwrap());
405405
assert!(r.is_err());
406406
}
407407

0 commit comments

Comments
 (0)