diff --git a/mp4parse_capi/src/lib.rs b/mp4parse_capi/src/lib.rs index 1cd733cc..823e9d22 100644 --- a/mp4parse_capi/src/lib.rs +++ b/mp4parse_capi/src/lib.rs @@ -1727,9 +1727,19 @@ fn get_indice_table( _ => None, }; - let empty_duration: Option> = match &track.empty_duration { - &Some(e) => i64::try_from(e.0).ok().map(Into::into), - _ => None, + let empty_duration: Option> = match track.empty_duration { + Some(duration) => { + let movie_timescale = context.timescale.ok_or(Mp4parseStatus::Invalid)?; + let track_timescale = track.timescale.ok_or(Mp4parseStatus::Invalid)?; + let duration = rational_scale(duration.0, movie_timescale.0, track_timescale.0) + .ok_or(Mp4parseStatus::Invalid)?; + Some( + i64::try_from(duration) + .map_err(|_| Mp4parseStatus::Invalid)? + .into(), + ) + } + None => None, }; // Find the track start offset time from 'elst'. @@ -2376,3 +2386,74 @@ fn parse_no_timescale() { assert_eq!(rv, Mp4parseStatus::Invalid); }; } + +#[cfg(test)] +mod edit_list_tests { + use super::*; + use mp4parse::{MediaScaledTime, MediaTimeScale, TrackScaledTime, TrackTimeScale}; + + fn context() -> MediaContext { + let mut file = std::fs::File::open("tests/video_colr_nclx_hdr10.mp4").unwrap(); + let mut context = mp4parse::read_mp4(&mut file, ParseStrictness::Normal).unwrap(); + context.tracks[0].ctts = None; + context.tracks[0].media_time = Some(TrackScaledTime(0, 0)); + context + } + + #[test] + fn empty_edit_uses_movie_timescale() { + for (movie_scale, track_scale, duration, media_time, expected) in [ + (57600, 60, 960, 0, 1), + (60, 57600, 960, 0, 921600), + (60, 60, 960, 0, 960), + (57600, 60, 960, 2, -1), + (4, 1, u64::MAX - 3, 0, (u64::MAX / 4) as i64), + ] { + let mut context = context(); + context.timescale = Some(MediaTimeScale(movie_scale)); + let track = &mut context.tracks[0]; + track.timescale = Some(TrackTimeScale(track_scale, 0)); + track.empty_duration = Some(MediaScaledTime(duration)); + track.media_time = Some(TrackScaledTime(media_time, 0)); + let track_id = track.track_id.unwrap(); + let mut cache = TryHashMap::default(); + let mut indices = Mp4parseByteData::default(); + assert_eq!( + get_indice_table(&context, &mut cache, track_id, &mut indices), + Ok(()) + ); + assert!(indices.length > 0); + assert!(!indices.indices.is_null()); + let first = unsafe { &*indices.indices }; + assert_eq!(first.start_composition.0, expected); + assert_eq!(first.start_decode.0, 0); + } + } + + #[test] + fn invalid_empty_edit_timescale() { + for (movie_scale, track_scale, duration) in [ + (None, Some(60), 960), + (Some(57600), None, 960), + (Some(0), Some(60), 960), + (Some(1), Some(2), u64::MAX), + (Some(1), Some(1), u64::MAX), + ] { + let mut context = context(); + context.timescale = movie_scale.map(MediaTimeScale); + let track = &mut context.tracks[0]; + track.timescale = track_scale.map(|scale| TrackTimeScale(scale, 0)); + track.empty_duration = Some(MediaScaledTime(duration)); + let track_id = track.track_id.unwrap(); + assert_eq!( + get_indice_table( + &context, + &mut TryHashMap::default(), + track_id, + &mut Mp4parseByteData::default() + ), + Err(Mp4parseStatus::Invalid) + ); + } + } +} diff --git a/mp4parse_capi/tests/test_sample_table.rs b/mp4parse_capi/tests/test_sample_table.rs index 49037a3d..2e357b7f 100644 --- a/mp4parse_capi/tests/test_sample_table.rs +++ b/mp4parse_capi/tests/test_sample_table.rs @@ -81,20 +81,20 @@ fn parse_sample_table() { rv = mp4parse_get_indice_table(parser, track_info.track_id, &mut indice); assert_eq!(rv, Mp4parseStatus::Ok); - // Compare the last few data from stagefright. + // The 95-tick empty edit at 1000 Hz adds 8550 ticks at 90000 Hz. let video_indice_291 = Indice { start_offset: 280_226.into(), end_offset: 280_855.into(), - start_composition: 876995.into(), - end_composition: 879996.into(), + start_composition: 885450.into(), + end_composition: 888451.into(), start_decode: 873900.into(), sync: false, }; let video_indice_292 = Indice { start_offset: 280_855.into(), end_offset: 281_297.into(), - start_composition: 873996.into(), - end_composition: 876995.into(), + start_composition: 882451.into(), + end_composition: 885450.into(), start_decode: 873901.into(), sync: false, }; @@ -105,16 +105,16 @@ fn parse_sample_table() { let video_indice_295 = Indice { start_offset: 282_391.into(), end_offset: 283_032.into(), - start_composition: 888995.into(), - end_composition: 888996.into(), + start_composition: 897450.into(), + end_composition: 897451.into(), start_decode: 885900.into(), sync: false, }; let video_indice_296 = Indice { start_offset: 283_092.into(), end_offset: 283_526.into(), - start_composition: 885996.into(), - end_composition: 888995.into(), + start_composition: 894451.into(), + end_composition: 897450.into(), start_decode: 885901.into(), sync: false, };