@@ -135,6 +135,7 @@ impl Mp4 {
135135 let mut last_stss_index = 0 ;
136136 let mut last_sample_in_ctts_run = -1i64 ;
137137 let mut ctts_run_index = -1i64 ;
138+ let mut dts_shift = 0 ;
138139
139140 let mut samples = Vec :: < Sample > :: new ( ) ;
140141
@@ -204,7 +205,7 @@ impl Mp4 {
204205 samples[ sample_n - 1 ] . duration =
205206 stts. entries [ stts_run_index as usize ] . sample_delta as u64 ;
206207
207- samples[ sample_n - 1 ] . decode_timestamp + samples[ sample_n - 1 ] . duration
208+ samples[ sample_n - 1 ] . decode_timestamp + samples[ sample_n - 1 ] . duration as i64
208209 } else {
209210 0
210211 } ;
@@ -219,9 +220,14 @@ impl Mp4 {
219220 ctts. entries [ ctts_run_index as usize ] . sample_count as i64 ;
220221 }
221222
222- decode_timestamp. saturating_add_signed (
223- ctts. entries [ ctts_run_index as usize ] . sample_offset as i64 ,
224- )
223+ // dts shift is determined by the smallest negative sample offset:
224+ // https://github.com/FFmpeg/FFmpeg/blob/455db6fe109cf905fe518ea2690495948937438f/libavformat/mov.c#L3671
225+ let offset = ctts. entries [ ctts_run_index as usize ] . sample_offset as i64 ;
226+ if offset < 0 {
227+ dts_shift = dts_shift. max ( -offset) ;
228+ }
229+
230+ decode_timestamp + offset
225231 } else {
226232 decode_timestamp
227233 } ;
@@ -253,7 +259,16 @@ impl Mp4 {
253259 }
254260
255261 if let Some ( last_sample) = samples. last_mut ( ) {
256- last_sample. duration = trak. mdia . mdhd . duration - last_sample. decode_timestamp ;
262+ last_sample. duration =
263+ trak. mdia . mdhd . duration - last_sample. decode_timestamp as u64 ;
264+ }
265+
266+ // Fixup all DTS by the dts shift if there's one.
267+ // https://github.com/FFmpeg/FFmpeg/blob/455db6fe109cf905fe518ea2690495948937438f/libavformat/mov.c#L4271
268+ if dts_shift > 0 {
269+ for sample in & mut samples {
270+ sample. decode_timestamp -= dts_shift;
271+ }
257272 }
258273
259274 tracks. insert (
@@ -333,17 +348,17 @@ impl Mp4 {
333348 let mut decode_timestamp = 0 ;
334349 if track. first_traf_merged || sample_n > 0 {
335350 let prev = & track. samples [ track. samples . len ( ) - 1 ] ;
336- decode_timestamp = prev. decode_timestamp + prev. duration ;
351+ decode_timestamp = prev. decode_timestamp + prev. duration as i64 ;
337352 } else {
338353 if let Some ( tfdt) = & traf. tfdt {
339- decode_timestamp = tfdt. base_media_decode_time ;
354+ decode_timestamp = tfdt. base_media_decode_time as i64 ;
340355 }
341356 track. first_traf_merged = true ;
342357 }
343358
344359 let composition_timestamp = if trun. flags & TrunBox :: FLAG_SAMPLE_CTS != 0 {
345360 decode_timestamp
346- + trun. sample_cts . get ( sample_n) . copied ( ) . unwrap_or ( 0 ) as u64
361+ + trun. sample_cts . get ( sample_n) . copied ( ) . unwrap_or ( 0 ) as i64
347362 } else {
348363 decode_timestamp
349364 } ;
@@ -381,6 +396,7 @@ impl Mp4 {
381396 . copied ( )
382397 . unwrap_or ( default_sample_size) as u64 ;
383398
399+ // Sample offset in bytes. (Must be positive, otherwise this would be outside of the file.)
384400 let sample_offset = if traf_idx == 0 && sample_n == 0 {
385401 if data_offset_present {
386402 base_data_offset
@@ -419,7 +435,7 @@ impl Mp4 {
419435 track. duration = track
420436 . samples
421437 . last ( )
422- . map ( |v| v. decode_timestamp + v . duration )
438+ . map ( |v| v. duration . saturating_add_signed ( v . composition_timestamp ) )
423439 . unwrap_or_default ( ) ;
424440 }
425441 }
@@ -508,11 +524,11 @@ pub struct Sample {
508524
509525 /// Timestamp of the sample at which it should be decoded,
510526 /// in time units.
511- pub decode_timestamp : u64 ,
527+ pub decode_timestamp : i64 ,
512528
513529 /// Timestamp of the sample at which the sample should be displayed,
514530 /// in time units.
515- pub composition_timestamp : u64 ,
531+ pub composition_timestamp : i64 ,
516532
517533 /// Duration of the sample in time units.
518534 pub duration : u64 ,
0 commit comments