Skip to content

Commit 61912d0

Browse files
authored
Shift DTS & CTS by minimum CTS to mimick ffprobe's behavior (#17)
* shift DTS & CTS by minimum CTS * improve doc of Sample's DTS/CTS
1 parent 8fa1613 commit 61912d0

File tree

1 file changed

+27
-0
lines changed

1 file changed

+27
-0
lines changed

src/reader.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,17 @@ impl Mp4 {
137137
let mut ctts_run_index = -1i64;
138138
let mut dts_shift = 0;
139139

140+
// The smallest presentation timestamp observed in this stream.
141+
//
142+
// This is typically 0, but in the presence of sample reordering (caused by AVC/HVC b-frames), it may be non-zero.
143+
// In fact, many formats don't require this to be zero, but video players typically
144+
// normalize the shown time to start at zero.
145+
// This is roughly equivalent to FFmpeg's internal `min_corrected_pts`
146+
// https://github.com/FFmpeg/FFmpeg/blob/4047b887fc44b110bccb1da09bcb79d6e454b88b/libavformat/isom.h#L202
147+
// To learn more about this I recommend reading the patch that introduced this in FFmpeg:
148+
// https://patchwork.ffmpeg.org/project/ffmpeg/patch/[email protected]/#12592
149+
let mut min_composition_timestamp = i64::MAX;
150+
140151
let mut samples = Vec::<Sample>::new();
141152

142153
fn get_sample_chunk_offset(stbl: &StblBox, chunk_index: u64) -> u64 {
@@ -231,6 +242,7 @@ impl Mp4 {
231242
} else {
232243
decode_timestamp
233244
};
245+
min_composition_timestamp = min_composition_timestamp.min(composition_timestamp);
234246

235247
let is_sync = if let Some(stss) = &stbl.stss {
236248
if last_stss_index < stss.entries.len()
@@ -271,6 +283,15 @@ impl Mp4 {
271283
}
272284
}
273285

286+
// Shift both DTS & CTS by the smallest CTS.
287+
// For details, see declaration of `min_composition_timestamp` above.
288+
if min_composition_timestamp != 0 {
289+
for sample in &mut samples {
290+
sample.decode_timestamp -= min_composition_timestamp;
291+
sample.composition_timestamp -= min_composition_timestamp;
292+
}
293+
}
294+
274295
tracks.insert(
275296
trak.tkhd.track_id,
276297
Track {
@@ -524,10 +545,16 @@ pub struct Sample {
524545

525546
/// Timestamp of the sample at which it should be decoded,
526547
/// in time units.
548+
///
549+
/// This is offsetted:
550+
/// * with decode timestamp shift determined from negative sample offsets
551+
/// * such that the first [`Self::composition_timestamp`] is zero.
527552
pub decode_timestamp: i64,
528553

529554
/// Timestamp of the sample at which the sample should be displayed,
530555
/// in time units.
556+
///
557+
/// This is offsetted such that the first composition timestamp is zero.
531558
pub composition_timestamp: i64,
532559

533560
/// Duration of the sample in time units.

0 commit comments

Comments
 (0)