Resolve a frame clip by binary search, not a linear scan

The provenance lookup runs once per frame per track. A linear scan over the
clip list is fine for the 11-PlayItem fixture and is not fine for the real
hoard: two discs there carry 900 PlayItems in one title, and several carry
250-450, so a scan would be hundreds of comparisons on every one of millions of
frames.

spans_trusted already guarantees the spans tile the feed contiguously and in
order, which makes this a partition point. A repeated clip reuses its first
reference span, so the search walks back to the FIRST entry sharing it — the
bytes are read once, so the material is emitted once, at that entry offset, and
the answer is stable regardless of which duplicate the partition lands on.

Also adds a test at the real 900-clip scale: every clip places, output stays
monotonic across all 900, an arbitrary byte resolves to the right clip out of
ascending order, and a byte past the end belongs to no clip.
This commit is contained in:
Matthew Jackson
2026-08-06 23:22:38 -07:00
parent b9ca75f471
commit 79dbb2cf84
+183 -4
View File
@@ -259,6 +259,35 @@ impl SeamPlan {
/// Place a raw PTS for `track`, advancing that track's own clip cursor.
///
/// Which clip owns feed byte `b`, by BINARY SEARCH.
///
/// `spans_trusted` guarantees the spans tile the feed contiguously in
/// order, so this is a partition point rather than a scan. That matters:
/// discs in the test hoard reach 900 clips, and this runs once per frame
/// per track — a linear scan would be ~900 comparisons on every one of
/// millions of frames, which is real time spent for no reason.
///
/// A repeated clip reuses its first reference's span, so the search lands
/// on the FIRST entry with that span. The bytes are only read once, so the
/// material is emitted once, at that entry's offset.
fn clip_at_byte(&self, b: u64) -> Option<usize> {
let i = self
.clips
.partition_point(|c| c.feed_span.is_some_and(|(s, _)| s <= b));
let cand = i.checked_sub(1)?;
// Walk back over any earlier entries sharing this span (a repeated
// clip) so the answer is stable and is always the first of them.
let (s, e) = self.clips[cand].feed_span?;
if b < s || b >= e {
return None;
}
let mut first = cand;
while first > 0 && self.clips[first - 1].feed_span == Some((s, e)) {
first -= 1;
}
Some(first)
}
/// `None` means DROP: the frame lies outside every clip's marks, so the
/// playlist does not include it.
fn place(
@@ -316,10 +345,7 @@ impl SeamPlan {
// heuristics, which are at least approximately right. Degrade instead.
if self.spans_trusted
&& let Some(b) = src_byte
&& let Some(found) = self
.clips
.iter()
.position(|c| c.feed_span.is_some_and(|(s, e)| b >= s && b < e))
&& let Some(found) = self.clip_at_byte(b)
{
let c = self.clips[found];
let placed = raw_ns >= c.in_ns && raw_ns <= c.out_ns;
@@ -1063,6 +1089,159 @@ mod tests {
);
}
/// On frames that are NOT ambiguous, provenance and the mark heuristics
/// must give the SAME answer.
///
/// This is the strongest available cross-check. The heuristics are wrong
/// only inside an overlap, where two clips' mark ranges both contain the
/// timestamp; everywhere else they are the behaviour that shipped and was
/// verified on real discs. So for a frame that falls inside exactly one
/// clip, the two methods disagreeing means the NEW path is wrong.
#[test]
fn provenance_agrees_with_marks_wherever_marks_are_unambiguous() {
let clips = clips_with_spans();
let plain = seamless_branching_clips();
let mut disagreements = Vec::new();
for (i, c) in clips.iter().enumerate() {
let in_ns = mpls_ticks_to_ns(c.in_time);
let out_ns = mpls_ticks_to_ns(c.out_time);
// Sample across the clip.
for f in [1u32, 2, 4, 8] {
let raw = in_ns + (out_ns - in_ns) / f as i64;
// Unambiguous = inside THIS clip's marks and no other's.
let owners = clips
.iter()
.filter(|o| {
raw >= mpls_ticks_to_ns(o.in_time) && raw <= mpls_ticks_to_ns(o.out_time)
})
.count();
if owners != 1 {
continue;
}
let (s, _) = c.feed_span.expect("span");
let mut a = SeamPlan::from_clips(&clips).expect("plan");
let mut b = SeamPlan::from_clips(&plain).expect("plan");
let by_bytes = a.place(raw, 0, true, Some(s + 10));
let by_marks = b.place(raw, 0, true, None);
if by_bytes != by_marks {
disagreements.push((i, raw, by_bytes, by_marks));
}
}
}
assert!(
disagreements.is_empty(),
"provenance and marks disagreed on unambiguous frames: {disagreements:?}"
);
}
/// Walk a whole title through the plan with provenance and assert the two
/// properties that decide whether a rip is watchable: output never moves
/// backwards for a track, and the total span matches the title's declared
/// duration.
///
/// A title can have the right TOTAL duration while being wrong in the
/// middle, which is why monotonicity is checked per frame rather than only
/// at the ends.
#[test]
fn a_full_pass_over_the_real_table_is_monotonic_and_totals_correctly() {
let clips = clips_with_spans();
let mut plan = SeamPlan::from_clips(&clips).expect("plan");
let declared: i64 = clips
.iter()
.map(|c| mpls_ticks_to_ns(c.out_time) - mpls_ticks_to_ns(c.in_time))
.sum();
let mut last: Option<i64> = None;
let mut first: Option<i64> = None;
let mut placed = 0u64;
for c in &clips {
let (s, e) = c.feed_span.expect("span");
let in_ns = mpls_ticks_to_ns(c.in_time);
let out_ns = mpls_ticks_to_ns(c.out_time);
// 40 frames per clip, evenly spaced, each stamped from its own span.
for k in 0..40i64 {
let raw = in_ns + (out_ns - in_ns) * k / 40;
let byte = s + (e - s) * k as u64 / 40;
if let Some(out) = plan.place(raw, 0, true, Some(byte)) {
if let Some(l) = last {
assert!(
out >= l,
"output moved backwards: {l} -> {out} (clip {}, raw {raw})",
c.clip_id
);
}
first.get_or_insert(out);
last = Some(out);
placed += 1;
}
}
}
assert!(placed > 400, "most frames must be placed, got {placed}");
let span = last.expect("last") - first.expect("first");
// Within one clip's frame spacing of the declared duration.
let slack = declared / 40;
assert!(
(span - declared).abs() <= slack,
"output span {span} differs from declared {declared} by more than {slack}"
);
}
/// A heavily branched title — the hoard has discs with 900 PlayItems —
/// must resolve every frame to the right clip, and must do it by binary
/// search rather than by scanning 900 entries per frame per track.
#[test]
fn a_nine_hundred_clip_title_resolves_every_frame_correctly() {
const N: u32 = 900;
const CLIP_TICKS: u32 = 10 * 45_000; // 10s each
const CLIP_BYTES: u64 = 5_000_000;
let clips: Vec<crate::disc::Clip> = (0..N)
.map(|i| crate::disc::Clip {
clip_id: format!("{i:05}"),
in_time: i * CLIP_TICKS,
out_time: (i + 1) * CLIP_TICKS,
duration_secs: 10.0,
source_packets: 0,
feed_span: Some((i as u64 * CLIP_BYTES, (i as u64 + 1) * CLIP_BYTES)),
})
.collect();
let mut plan = SeamPlan::from_clips(&clips).expect("plan");
assert!(
plan.spans_trusted,
"a contiguous 900-clip table must be trusted"
);
// Every clip, sampled at its middle, must place — and monotonically.
let mut last: Option<i64> = None;
for i in 0..N {
let raw = mpls_ticks_to_ns(i * CLIP_TICKS + CLIP_TICKS / 2);
let byte = i as u64 * CLIP_BYTES + CLIP_BYTES / 2;
let out = plan
.place(raw, 0, true, Some(byte))
.unwrap_or_else(|| panic!("clip {i} did not place"));
if let Some(l) = last {
assert!(out >= l, "output moved backwards at clip {i}: {l} -> {out}");
}
last = Some(out);
}
// The lookup must find the right clip from an arbitrary byte, not just
// in ascending order — a track can arrive out of step with another.
for i in [0u32, 1, 449, 898, 899] {
let byte = i as u64 * CLIP_BYTES + 7;
assert_eq!(
plan.clip_at_byte(byte),
Some(i as usize),
"byte {byte} must resolve to clip {i}"
);
}
assert_eq!(
plan.clip_at_byte(N as u64 * CLIP_BYTES + 1),
None,
"a byte past the last clip belongs to no clip"
);
}
/// Build a real seamless-branching clip table (`00801.mpls`, 11 PlayItems, marks
/// read off the disc) so the arithmetic is pinned to measured ground truth
/// rather than to invented numbers.