mux: write PTS (presentation), not DTS, as the MKV block timecode

The video codec parsers (HEVC, H.264, VC-1, MPEG-2) used
pes.dts.or(pes.pts) as each frame's timestamp. MKV block timecodes
are presentation timestamps; frames are stored in decode order and
the player reorders for display by timecode. Using DTS makes the
timecode monotonic in storage order, presenting B-frames in decode
order — visible motion judder / wrong frames on playback, and
PTS-based seeking lands on the wrong frame.

The compressed video was always byte-correct (verified by NAL-level
diff against a known-good demux); this was purely a timestamp defect
affecting every B-frame title. Fix: prefer PTS (pes.pts.or(pes.dts)).
Verified on a real UHD iso->mkv: emitted PTS now reorders for
B-frames identically to a reference muxer.

Update the two tests that asserted the old DTS-preferred behavior and
add an HEVC regression test pinning PTS as the block timecode.
This commit is contained in:
MattJackson
2026-06-05 20:01:13 -07:00
parent 6be5198886
commit 5b702a76a7
4 changed files with 61 additions and 18 deletions
+11 -7
View File
@@ -41,8 +41,11 @@ impl CodecParser for Vc1Parser {
return Vec::new();
}
// Use DTS when available (monotonic for B-frame content), fall back to PTS
let ts_ns = pes.dts.or(pes.pts).map(pts_to_ns).unwrap_or(0);
// MKV block timecodes are PRESENTATION timestamps; frames are stored in
// decode order and the player reorders by timecode. Use PTS, not DTS —
// DTS presents B-frames in decode order (visible judder) and breaks
// PTS-based seeking. Fall back to DTS only if PTS is absent.
let ts_ns = pes.pts.or(pes.dts).map(pts_to_ns).unwrap_or(0);
let mut has_seq_header = false;
let mut has_entry_point = false;
let mut frame_start: Option<usize> = None;
@@ -419,10 +422,10 @@ mod tests {
assert_eq!(frames[0].pts_ns, 1_000_000_000);
}
// --- DTS preferred over PTS ---
// --- PTS (presentation) used for the MKV block timecode, not DTS ---
#[test]
fn dts_preferred_over_pts() {
fn pts_preferred_over_dts() {
let mut parser = Vc1Parser::new();
let mut data = Vec::new();
@@ -431,13 +434,14 @@ mod tests {
let pes = PesPacket {
pid: 0x1011,
pts: Some(180000),
dts: Some(90000),
pts: Some(180000), // presentation
dts: Some(90000), // decode
data,
};
let frames = parser.parse(&pes);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].pts_ns, 1_000_000_000);
// PTS must be used — MKV block timecodes are presentation timestamps.
assert_eq!(frames[0].pts_ns, 2_000_000_000);
}
// --- find_next_sc utility ---