Skip to content

Commit 8345bba

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 61c1030 commit 8345bba

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
@@ -266,6 +266,8 @@ struct FileTypeBox {
266266
/// Movie header box 'mvhd'.
267267
#[derive(Debug)]
268268
struct MovieHeaderBox {
269+
creation: u64,
270+
modification: u64,
269271
pub timescale: u32,
270272
duration: u64,
271273
}
@@ -777,9 +779,14 @@ pub enum XmlBox {
777779
BinaryXmlBox(TryVec<u8>),
778780
}
779781

782+
#[derive(Debug)]
783+
pub struct UtcSecondsSince1904(pub u64);
784+
780785
/// Internal data structures.
781786
#[derive(Debug, Default)]
782787
pub struct MediaContext {
788+
pub creation: Option<UtcSecondsSince1904>,
789+
pub modification: Option<UtcSecondsSince1904>,
783790
pub timescale: Option<MediaTimeScale>,
784791
/// Tracks found in the file.
785792
pub tracks: TryVec<Track>,
@@ -2866,16 +2873,12 @@ pub fn read_mp4<T: Read>(f: &mut T) -> Result<MediaContext> {
28662873
context.ok_or(Error::NoMoov)
28672874
}
28682875

2869-
/// Parse a Movie Header Box
2870-
/// See ISOBMFF (ISO 14496-12:2015) § 8.2.2
2871-
fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
2872-
let mvhd = read_mvhd(f)?;
2873-
debug!("{:?}", mvhd);
2876+
fn validate_timescale(mvhd: &MovieHeaderBox) -> Result<Option<MediaTimeScale>> {
28742877
if mvhd.timescale == 0 {
2875-
return Err(Error::InvalidData("zero timescale in mdhd"));
2878+
Err(Error::InvalidData("zero timescale in mdhd"))
2879+
} else {
2880+
Ok(Some(MediaTimeScale(u64::from(mvhd.timescale))))
28762881
}
2877-
let timescale = Some(MediaTimeScale(u64::from(mvhd.timescale)));
2878-
Ok(timescale)
28792882
}
28802883

28812884
/// Parse a Movie Box
@@ -2885,6 +2888,8 @@ fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<Option<MediaTimeScale>> {
28852888
/// such as with tests/test_case_1185230.mp4.
28862889
fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Result<MediaContext> {
28872890
let MediaContext {
2891+
mut creation,
2892+
mut modification,
28882893
mut timescale,
28892894
mut tracks,
28902895
mut mvex,
@@ -2898,7 +2903,11 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Resu
28982903
while let Some(mut b) = iter.next_box()? {
28992904
match b.head.name {
29002905
BoxType::MovieHeaderBox => {
2901-
timescale = parse_mvhd(&mut b)?;
2906+
let mvhd = read_mvhd(&mut b)?;
2907+
debug!("{:?}", mvhd);
2908+
creation = Some(UtcSecondsSince1904(mvhd.creation));
2909+
modification = Some(UtcSecondsSince1904(mvhd.modification));
2910+
timescale = validate_timescale(&mvhd)?;
29022911
}
29032912
BoxType::TrackBox => {
29042913
let mut track = Track::new(tracks.len());
@@ -2929,6 +2938,8 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: Option<MediaContext>) -> Resu
29292938
}
29302939

29312940
Ok(MediaContext {
2941+
creation,
2942+
modification,
29322943
timescale,
29332944
tracks,
29342945
mvex,
@@ -3204,33 +3215,39 @@ fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
32043215
/// Parse an mvhd box.
32053216
fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
32063217
let (version, _) = read_fullbox_extra(src)?;
3218+
let to_u64 = |n| {
3219+
if n == std::u32::MAX {
3220+
std::u64::MAX
3221+
} else {
3222+
u64::from(n)
3223+
}
3224+
};
3225+
let creation;
3226+
let modification;
32073227
match version {
32083228
// 64 bit creation and modification times.
32093229
1 => {
3210-
skip(src, 16)?;
3230+
creation = be_u64(src)?;
3231+
modification = be_u64(src)?;
32113232
}
32123233
// 32 bit creation and modification times.
32133234
0 => {
3214-
skip(src, 8)?;
3235+
creation = to_u64(be_u32(src)?);
3236+
modification = to_u64(be_u32(src)?);
32153237
}
32163238
_ => return Err(Error::InvalidData("unhandled mvhd version")),
32173239
}
32183240
let timescale = be_u32(src)?;
32193241
let duration = match version {
32203242
1 => be_u64(src)?,
3221-
0 => {
3222-
let d = be_u32(src)?;
3223-
if d == std::u32::MAX {
3224-
std::u64::MAX
3225-
} else {
3226-
u64::from(d)
3227-
}
3228-
}
3243+
0 => to_u64(be_u32(src)?),
32293244
_ => return Err(Error::InvalidData("unhandled mvhd version")),
32303245
};
32313246
// Skip remaining fields.
32323247
skip(src, 80)?;
32333248
Ok(MovieHeaderBox {
3249+
creation,
3250+
modification,
32343251
timescale,
32353252
duration,
32363253
})

mp4parse/src/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ fn read_mvhd_invalid_timescale() {
385385
let mut stream = iter.next_box().unwrap().unwrap();
386386
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
387387
assert_eq!(stream.head.size, 120);
388-
let r = super::parse_mvhd(&mut stream);
388+
let r = super::validate_timescale(&super::read_mvhd(&mut stream).unwrap());
389389
assert!(r.is_err());
390390
}
391391

0 commit comments

Comments
 (0)