Place Blu-ray clips by the playlist's marks, not by guessing at PTS jumps
A seamless-branching title's PlayItems do not chain contiguously: one clip's OUT can sit after the next clip's IN, where the disc stores the join twice, or before it, where the playlist skips material. The mux never saw those marks — its own header said so — and inferred seams from PTS jumps instead. Inference cannot recover this. A forward jump is ambiguous: it means the playlist skipped, or it means frames were lost to damaged media, and compressing the latter would falsify timing on exactly the rips that most need it faithful. An overlap smaller than the B-frame reorder threshold is invisible to inference entirely, and its duplicate content then collided in the muxer, where the monotonic nudge flattened a run of audio onto the tick floor and put sound ahead of picture for the rest of the film. Measured on one 11-PlayItem title: the file declared 7893.385 s, which is what the playlist says the title is, and carried packets to 8029.298 s. Both numbers came from the same program on the same disc. Four skips totalling 135.9 s became dead timeline, and a 1.79 s overlap put audio 1.8 s ahead at the half-hour mark. Five of forty-seven titles were affected; every single-clip title was exact. So the marks are read. Each clip contributes exactly out - in, laid end to end, so the output runs as long as the playlist says and a join never rewinds. Titles without usable marks — DVD, HD-DVD, file sources — keep the inference path unchanged, and clips that already chain contiguously produce a constant offset, which is pinned by a test.
This commit is contained in:
@@ -742,7 +742,7 @@ impl DemuxSink {
|
||||
tracks,
|
||||
ref_video_track,
|
||||
ref_first_pts_ns: None,
|
||||
timeline: TimelineContinuity::new(),
|
||||
timeline: TimelineContinuity::with_clips(&title.clips),
|
||||
finished: false,
|
||||
})
|
||||
}
|
||||
@@ -866,7 +866,11 @@ impl Stream for DemuxSink {
|
||||
// an audio ES before the video ES, in which case track 0 is audio and a
|
||||
// non-video epoch driver would ratchet the frontier on sparse/lagging PTS.
|
||||
let drives = Some(frame.track) == self.ref_video_track;
|
||||
let pts = self.timeline.adjust(frame.pts, drives);
|
||||
// See `MkvMuxer::write_frame`: `None` is material outside the
|
||||
// playlist's clip marks and is dropped rather than emitted.
|
||||
let Some(pts) = self.timeline.map(frame.pts, drives) else {
|
||||
return Ok(());
|
||||
};
|
||||
if drives {
|
||||
// Delay reference: recorded here, not in the track's `TrackOut`, so
|
||||
// it survives the `audio://` / `sub://` kind filter dropping the
|
||||
|
||||
Reference in New Issue
Block a user