Say which frame and which marks caused a provenance drop

The heuristic path has always logged its first drop per track with the
frame's timestamp and the clip marks it was judged against. The
provenance path did not log at all, so a title that dropped MOST of its
frames there failed the volume gate without a single line saying which
frame, which clip, or which marks — a full-length rip produced zero drop
events and an error, which is not a diagnosable failure.

Also pins the all-identical-spans hazard as a test. Every PlayItem
referencing one clip file gives every clip the same span, which passes
the tiling check and is therefore "trusted" while carrying no
information at all about which PlayItem a byte belongs to. That is not
what the discs on hand do — theirs have one distinct span per clip — but
the check conflates "the spans tile" with "the spans distinguish", and
only the second justifies placing a frame by its byte offset.
This commit is contained in:
Matthew Jackson
2026-08-07 03:05:32 -07:00
parent a0f76a4f18
commit 776a4fd6eb
+69
View File
@@ -430,6 +430,25 @@ impl SeamPlan {
// (a clip's file is not trimmed to its marks). Counted, so the
// volume gates in the sinks can see it.
self.dropped[track] = self.dropped[track].saturating_add(1);
// Once per track, on the FIRST drop only — a join legitimately
// drops a handful of frames and this must not become per-frame
// noise. The heuristic path below has always logged this; the
// provenance path did not, so a title that dropped MOST of its
// frames here produced a volume-gate failure and not one line
// saying which frame, which clip, or which marks it was judged
// against. That is not a diagnosable failure.
if self.dropped[track] == 1 {
tracing::info!(
target: "freemkv::mux",
track,
clip = found,
byte = b,
raw_ns,
in_ns = c.in_ns,
out_ns = c.out_ns,
"frame outside its clip's marks (by provenance); dropping"
);
}
return None;
}
return Some(raw_ns.saturating_add(c.offset_ns));
@@ -451,6 +470,17 @@ impl SeamPlan {
let c = self.clips[clip];
if raw_ns < c.in_ns || raw_ns > c.out_ns {
self.dropped[track] = self.dropped[track].saturating_add(1);
if self.dropped[track] == 1 {
tracing::info!(
target: "freemkv::mux",
track,
clip,
raw_ns,
in_ns = c.in_ns,
out_ns = c.out_ns,
"frame with no provenance outside its track's clip; dropping"
);
}
return None;
}
let out = raw_ns.saturating_add(c.offset_ns);
@@ -1336,6 +1366,45 @@ mod tests {
);
}
/// A title whose PlayItems all reference ONE clip file: every span is
/// identical, so the tiling check's "equal to previous is allowed" arm
/// matches every entry and the spans are TRUSTED — while carrying no
/// information at all about which PlayItem a byte belongs to.
///
/// That combination is the dangerous one: provenance looks authoritative
/// and is actually blind, so every frame resolves to the FIRST PlayItem and
/// everything past its mark range is dropped.
#[test]
fn one_clip_file_behind_every_play_item_is_not_distinguishable_by_byte() {
const N: u32 = 8;
const SPAN: (u64, u64) = (0, 40_000_000_000);
const SEG: u32 = 600 * 45_000; // 10 min per sub-range
let clips: Vec<crate::disc::Clip> = (0..N)
.map(|i| crate::disc::Clip {
clip_id: "00001".to_string(), // the SAME file every time
in_time: i * SEG,
out_time: (i + 1) * SEG,
duration_secs: 600.0,
source_packets: 0,
feed_span: Some(SPAN),
})
.collect();
let plan = SeamPlan::from_clips(&clips).expect("plan");
assert!(
plan.spans_trusted,
"identical spans pass the tiling check -- this is the hazard"
);
// Every byte in the file resolves to the FIRST play item, so a byte
// offset cannot say which of the 8 ranges a frame belongs to.
for b in [0u64, 1_000_000, 20_000_000_000, 39_999_999_999] {
assert_eq!(
plan.clip_at_byte(b),
Some(0),
"byte {b} resolves to the first play item, always"
);
}
}
/// A playlist whose clips each restart their own STC is the common case on
/// the branched discs in the hoard: every clip's marks cover the same low
/// values, so the marks are NOT points on one title-wide clock.