Skip to content

Commit 43ae32f

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 43ae32f

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

mp4parse/src/lib.rs

+36-19
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>,
@@ -4115,16 +4122,12 @@ pub fn read_mp4<T: Read>(f: &mut T) -> Result<MediaContext> {
41154122
context.ok_or(Error::MoovMissing)
41164123
}
41174124

4118-
/// Parse a Movie Header Box
4119-
/// See ISOBMFF (ISO 14496-12:2020) § 8.2.2
4120-
fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
4121-
let mvhd = read_mvhd(f)?;
4122-
debug!("{:?}", mvhd);
4125+
fn validate_timescale(mvhd: &MovieHeaderBox) -> Result<Option<MediaTimeScale>> {
41234126
if mvhd.timescale == 0 {
4124-
return Status::MvhdBadTimescale.into();
4127+
Err(Error::InvalidData(Status::MdhdBadTimescale))
4128+
} else {
4129+
Ok(Some(MediaTimeScale(u64::from(mvhd.timescale))))
41254130
}
4126-
let timescale = Some(MediaTimeScale(u64::from(mvhd.timescale)));
4127-
Ok(timescale)
41284131
}
41294132

41304133
/// Parse a Movie Box
@@ -4134,6 +4137,8 @@ fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
41344137
/// such as with tests/test_case_1185230.mp4.
41354138
fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Result<MediaContext> {
41364139
let MediaContext {
4140+
mut creation,
4141+
mut modification,
41374142
mut timescale,
41384143
mut tracks,
41394144
mut mvex,
@@ -4147,7 +4152,11 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Resu
41474152
while let Some(mut b) = iter.next_box()? {
41484153
match b.head.name {
41494154
BoxType::MovieHeaderBox => {
4150-
timescale = parse_mvhd(&mut b)?;
4155+
let mvhd = read_mvhd(&mut b)?;
4156+
debug!("{:?}", mvhd);
4157+
creation = Some(UtcSecondsSince1904(mvhd.creation));
4158+
modification = Some(UtcSecondsSince1904(mvhd.modification));
4159+
timescale = validate_timescale(&mvhd)?;
41514160
}
41524161
BoxType::TrackBox => {
41534162
let mut track = Track::new(tracks.len());
@@ -4178,6 +4187,8 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Resu
41784187
}
41794188

41804189
Ok(MediaContext {
4190+
creation,
4191+
modification,
41814192
timescale,
41824193
tracks,
41834194
mvex,
@@ -4490,28 +4501,32 @@ fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
44904501
/// Parse an mvhd box.
44914502
fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
44924503
let (version, _) = read_fullbox_extra(src)?;
4504+
let to_u64 = |n| {
4505+
if n == std::u32::MAX {
4506+
std::u64::MAX
4507+
} else {
4508+
u64::from(n)
4509+
}
4510+
};
4511+
let creation;
4512+
let modification;
44934513
match version {
44944514
// 64 bit creation and modification times.
44954515
1 => {
4496-
skip(src, 16)?;
4516+
creation = be_u64(src)?;
4517+
modification = be_u64(src)?;
44974518
}
44984519
// 32 bit creation and modification times.
44994520
0 => {
4500-
skip(src, 8)?;
4521+
creation = to_u64(be_u32(src)?);
4522+
modification = to_u64(be_u32(src)?);
45014523
}
45024524
_ => return Status::MvhdBadVersion.into(),
45034525
}
45044526
let timescale = be_u32(src)?;
45054527
let duration = match version {
45064528
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-
}
4529+
0 => to_u64(be_u32(src)?),
45154530
_ => unreachable!("Should have returned Status::MvhdBadVersion"),
45164531
};
45174532
// Skip remaining valid fields.
@@ -4520,6 +4535,8 @@ fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
45204535
// Padding could be added in some contents.
45214536
skip_box_remain(src)?;
45224537
Ok(MovieHeaderBox {
4538+
creation,
4539+
modification,
45234540
timescale,
45244541
duration,
45254542
})

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)