libfreemkv: rc.5.2 SOTL video — full Windows fps, opening-GOP proof, self-sufficient log-level 3
Three Silence-of-the-Lambs (R2 PAL SD-DVD) follow-ups for rc.5.2. SUB-TASK 1 — Windows Explorer showed 12.5 fps (half) for the 576i25 track. Root cause: the DefaultDecodedFieldDuration (20 ms field) element rc.5.1 added to "fix" Windows fps did the opposite. With FlagInterlaced=1 + DefaultDuration=40 ms + DefaultDecodedFieldDuration=20 ms, Explorer halved to 12.5 fps and MediaInfo flipped to VFR. MakeMKV's correct rip omits the field-duration element, keeps FlagInterlaced=1 + FieldOrder=TFF + full-frame DefaultDuration (40 ms), and Explorer shows 25 fps / MediaInfo CFR. Fix: MkvTrack::video now passes field_duration_ns == 0 so the element is no longer written; the 1/DefaultDuration = 25 fps signal (the only one tools trust) is the full-frame value. Interlace signalling (FlagInterlaced, FieldOrder=TFF) is retained — MediaInfo reads scan type from the MPEG-2 ES picture coding extension, so it still reports Interlaced / Top Field First. Tests pin the new TrackEntry elements (element present/absent + values). SUB-TASK 2 — opening "menu"/still-frame video. Traced the MPEG-2 opening-GOP path; the wrong/last seq header and PTS-floor-to-0 hypotheses are RULED OUT with file:line evidence: codecPrivate is the FIRST sequence header (read once at headers-ready, mkvstream.rs:115 + pipelined_stream.rs:289), DVD VOBU structure guarantees each title opens on seq header + I-frame (no mid-GOP open), the parser back-anchors leading still-frames to the disc's real timeline (mpeg2.rs:296-303), and the muxer anchors base on the opening keyframe's real PTS so the t=0 floor (mkv.rs:963) never corrupts it. Regression tests pin all three (parser + muxer level). SUB-TASK 3 — make --log-level 3 self-sufficient (diag.rs + minimal hooks). (a) dump the ACTUAL MKV TrackEntry elements written per track (tag=mkv.track: FlagInterlaced, FieldOrder, DefaultDuration, field duration, Display dims, codecPrivate hex) so Windows-fps-class metadata is verifiable from a log alone. (b) capture the first ~100 coded frames per track (raw) to <output>.opening.bin with a per-frame summary line (tag=mkv.opening.frame: track, key/delta, size, PTS) so opening-GOP/menu issues are diagnosable from a future log without the disc. Both gated to log-level 3; normal runs open no side file and record nothing. CI gate (Rust 1.86): fmt --check, clippy -D warnings, and test --tests all green.
This commit is contained in:
+28
-1
@@ -95,6 +95,18 @@ impl MkvStream {
|
||||
/// Create for writing PES frames → MKV container.
|
||||
/// Codec privates come from title.codec_privates (populated by input stream).
|
||||
pub fn create(writer: Box<dyn WriteSeek + Send>, title: &DiscTitle) -> io::Result<Self> {
|
||||
Self::create_at(writer, title, None)
|
||||
}
|
||||
|
||||
/// As [`create`](Self::create), but `output_path` (when known) enables the
|
||||
/// `--log-level 3` opening-frame capture to `<output>.opening.bin`. A `None`
|
||||
/// path (e.g. an in-memory / stdio sink) silently skips the side-file
|
||||
/// capture; the per-track TrackEntry dump still fires.
|
||||
pub fn create_at(
|
||||
writer: Box<dyn WriteSeek + Send>,
|
||||
title: &DiscTitle,
|
||||
output_path: Option<&std::path::Path>,
|
||||
) -> io::Result<Self> {
|
||||
let mut tracks = Vec::new();
|
||||
let mut has_default_video = false;
|
||||
let mut has_default_audio = false;
|
||||
@@ -118,7 +130,15 @@ impl MkvStream {
|
||||
tracks.push(track);
|
||||
}
|
||||
|
||||
let muxer = MkvMuxer::new(
|
||||
// --log-level 3: dump the ACTUAL TrackEntry elements about to be written
|
||||
// (FlagInterlaced / FieldOrder / DefaultDuration / DefaultDecodedFieldDuration
|
||||
// / Display dims / codecPrivate hex) so the Windows-fps-class metadata is
|
||||
// verifiable from a log alone. No-op when diag is off.
|
||||
for (i, track) in tracks.iter().enumerate() {
|
||||
crate::diag::dump_mkv_track((i + 1) as u64, track);
|
||||
}
|
||||
|
||||
let mut muxer = MkvMuxer::new(
|
||||
writer,
|
||||
&tracks,
|
||||
Some(&title.playlist),
|
||||
@@ -126,6 +146,13 @@ impl MkvStream {
|
||||
&title.chapters,
|
||||
)?;
|
||||
|
||||
// --log-level 3: capture the first ~100 coded frames per track to
|
||||
// `<output>.opening.bin`. Only opens the side file when diag is on AND a
|
||||
// real output path is known; otherwise it's a no-op the muxer never sees.
|
||||
if let Some(path) = output_path {
|
||||
muxer.set_opening_capture(crate::diag::OpeningCapture::new(path, tracks.len()));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
disc_title: title.clone(),
|
||||
mode: Mode::Write {
|
||||
|
||||
Reference in New Issue
Block a user