Skip to content

Commit 63ad36d

Browse files
alfredoyangkinetiknz
alfredoyang
authored andcommitted
always use version1 in ctts table parsing and adjust the composition time calculation
1 parent 6cd06d4 commit 63ad36d

File tree

4 files changed

+79
-36
lines changed

4 files changed

+79
-36
lines changed

mp4parse/src/lib.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,12 +1165,10 @@ fn read_ctts<T: Read>(src: &mut BMFFBox<T>) -> Result<CompositionOffsetBox> {
11651165
let mut offsets = Vec::new();
11661166
for _ in 0..counts {
11671167
let (sample_count, time_offset) = match version {
1168-
0 => {
1169-
let count = be_u32(src)?;
1170-
let offset = TimeOffsetVersion::Version0(be_u32(src)?);
1171-
(count, offset)
1172-
},
1173-
1 => {
1168+
// According to spec, Version0 shoule be used when version == 0;
1169+
// however, some buggy contents have negative value when version == 0.
1170+
// So we always use Version1 here.
1171+
0...1 => {
11741172
let count = be_u32(src)?;
11751173
let offset = TimeOffsetVersion::Version1(be_i32(src)?);
11761174
(count, offset)

mp4parse_capi/src/lib.rs

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,15 @@ fn track_time_to_us(time: TrackScaledTime, scale: TrackTimeScale) -> Option<u64>
398398
rational_scale(time.0, scale.0, microseconds_per_second)
399399
}
400400

401+
fn get_track_time_to_us(time: i64, scale: TrackTimeScale) -> i64 {
402+
assert!(time >= 0);
403+
let track_time = TrackScaledTime(time as u64, scale.1);
404+
match track_time_to_us(track_time, scale) {
405+
Some(v) => v as i64,
406+
_ => 0,
407+
}
408+
}
409+
401410
/// Fill the supplied `mp4parse_track_info` with metadata for `track`.
402411
#[no_mangle]
403412
pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
@@ -856,29 +865,6 @@ impl<'a> Iterator for SampleToChunkIterator<'a> {
856865
}
857866
}
858867

859-
// A helper struct to convert track time to us.
860-
struct PresentationTime {
861-
time: i64,
862-
scale: TrackTimeScale
863-
}
864-
865-
impl PresentationTime {
866-
fn new(time: i64, scale: TrackTimeScale) -> PresentationTime {
867-
PresentationTime {
868-
time: time,
869-
scale: scale,
870-
}
871-
}
872-
873-
fn to_us(&self) -> i64 {
874-
let track_time = TrackScaledTime(self.time as u64, self.scale.1);
875-
match track_time_to_us(track_time, self.scale) {
876-
Some(v) => v as i64,
877-
_ => 0,
878-
}
879-
}
880-
}
881-
882868
fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<mp4parse_indice>> {
883869
let timescale = match track.timescale {
884870
Some(t) => t,
@@ -971,18 +957,22 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<mp4p
971957
// composition time => CT(n) = DT(n) + CTTS(n)
972958
// Note:
973959
// composition time needs to add the track offset time from 'elst' table.
974-
let mut sum_delta = PresentationTime::new(0, timescale);
960+
let mut sum_delta = 0;
975961
for sample in sample_table.as_mut_slice() {
976-
let decode_time = sum_delta.to_us();
977-
sum_delta.time += stts_iter.next_delta() as i64;
962+
let decode_time = sum_delta;
963+
sum_delta += stts_iter.next_delta() as i64;
978964

979965
// ctts_offset is the current sample offset time.
980-
let ctts_offset = PresentationTime::new(ctts_offset_iter.next_offset_time(), timescale);
966+
let ctts_offset = ctts_offset_iter.next_offset_time();
981967

982-
let start_composition = decode_time + ctts_offset.to_us() + track_offset_time;
983-
let end_composition = sum_delta.to_us() + ctts_offset.to_us() + track_offset_time;
968+
// ctts_offset could be negative but (decode_time + ctts_offset) should always be positive
969+
// value.
970+
let start_composition = get_track_time_to_us(decode_time + ctts_offset, timescale) + track_offset_time;
971+
// ctts_offset could be negative but (sum_delta + ctts_offset) should always be positive
972+
// value.
973+
let end_composition = get_track_time_to_us(sum_delta + ctts_offset, timescale) + track_offset_time;
984974

985-
sample.start_decode = decode_time;
975+
sample.start_decode = get_track_time_to_us(decode_time, timescale);;
986976
sample.start_composition = start_composition;
987977
sample.end_composition = end_composition;
988978
}

mp4parse_capi/tests/test_sample_table.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,58 @@ fn parse_sample_table_with_elst() {
151151
mp4parse_free(parser);
152152
}
153153
}
154+
155+
#[test]
156+
fn parse_sample_table_with_negative_ctts() {
157+
let mut file = std::fs::File::open("tests/white.mp4").expect("Unknown file");
158+
let io = mp4parse_io {
159+
read: buf_read,
160+
userdata: &mut file as *mut _ as *mut std::os::raw::c_void
161+
};
162+
163+
unsafe {
164+
let parser = mp4parse_new(&io);
165+
166+
let mut rv = mp4parse_read(parser);
167+
assert_eq!(rv, mp4parse_error::MP4PARSE_OK);
168+
169+
let mut counts: u32 = 0;
170+
rv = mp4parse_get_track_count(parser, &mut counts);
171+
assert_eq!(rv, mp4parse_error::MP4PARSE_OK);
172+
assert_eq!(counts, 1);
173+
174+
let mut track_info = mp4parse_track_info {
175+
track_type: mp4parse_track_type::MP4PARSE_TRACK_TYPE_AUDIO,
176+
codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
177+
track_id: 0,
178+
duration: 0,
179+
media_time: 0,
180+
};
181+
rv = mp4parse_get_track_info(parser, 0, &mut track_info);
182+
assert_eq!(rv, mp4parse_error::MP4PARSE_OK);
183+
assert_eq!(track_info.track_type, mp4parse_track_type::MP4PARSE_TRACK_TYPE_VIDEO);
184+
assert_eq!(track_info.codec, mp4parse_codec::MP4PARSE_CODEC_AVC);
185+
186+
let mut is_fragmented_file: u8 = std::u8::MAX;
187+
rv = mp4parse_is_fragmented(parser, track_info.track_id, &mut is_fragmented_file);
188+
assert_eq!(rv, mp4parse_error::MP4PARSE_OK);
189+
assert_eq!(is_fragmented_file, 0);
190+
191+
let mut indice = mp4parse_byte_data::default();
192+
rv = mp4parse_get_indice_table(parser, track_info.track_id, &mut indice);
193+
assert_eq!(rv, mp4parse_error::MP4PARSE_OK);
194+
195+
// There are negative value in 'ctts' table.
196+
let video_indice_0 = mp4parse_indice { start_offset: 48, end_offset: 890, start_composition: 0, end_composition: 33333, start_decode: 0, sync: true };
197+
let video_indice_1 = mp4parse_indice { start_offset: 890, end_offset: 913, start_composition: 133333, end_composition: 166666, start_decode: 33333, sync: false };
198+
let video_indice_2 = mp4parse_indice { start_offset: 913, end_offset: 934, start_composition: 66666, end_composition: 100000, start_decode: 66666, sync: false };
199+
let video_indice_3 = mp4parse_indice { start_offset: 934, end_offset: 955, start_composition: 33333, end_composition: 66666, start_decode: 100000, sync: false };
200+
assert_eq!(indice.length, 300);
201+
assert_eq!(*indice.indices.offset(0), video_indice_0);
202+
assert_eq!(*indice.indices.offset(1), video_indice_1);
203+
assert_eq!(*indice.indices.offset(2), video_indice_2);
204+
assert_eq!(*indice.indices.offset(3), video_indice_3);
205+
206+
mp4parse_free(parser);
207+
}
208+
}

mp4parse_capi/tests/white.mp4

13.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)