mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-24 01:58:19 -06:00
security(codecs): avoid quadratic MPEG timestamp lookup (#2883)
This commit is contained in:
@@ -369,15 +369,7 @@ impl MpegSource {
|
||||
let raw: Vec<Option<i64>> = v.order.iter().map(|&i| units[v.units[i]].pts).collect();
|
||||
let rate = self.info.frame_rate();
|
||||
let dur = |k: i64| (k as i128 * 90_000 * rate.den as i128 / rate.num.max(1) as i128) as i64;
|
||||
let known: Vec<usize> = (0..raw.len()).filter(|&i| raw[i].is_some()).collect();
|
||||
(0..raw.len())
|
||||
.map(|i| {
|
||||
raw[i].or_else(|| {
|
||||
let j = known.iter().rev().find(|&&j| j < i).or_else(|| known.iter().find(|&&j| j > i))?;
|
||||
Some(raw[*j]? + dur(i as i64 - *j as i64))
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
interpolate_pts(&raw, dur)
|
||||
}
|
||||
|
||||
fn read_unit(&self, v: &VideoTrack, i: usize) -> crate::Result<Vec<u8>> {
|
||||
@@ -690,6 +682,23 @@ impl MpegSource {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill missing timestamps from their closest preceding timestamp, or the next one when there is
|
||||
/// no predecessor. The known positions are sorted, so binary search avoids rescanning them for
|
||||
/// every missing frame.
|
||||
fn interpolate_pts(raw: &[Option<i64>], dur: impl Fn(i64) -> i64) -> Vec<Option<i64>> {
|
||||
let known: Vec<usize> = (0..raw.len()).filter(|&i| raw[i].is_some()).collect();
|
||||
raw.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &pts)| {
|
||||
pts.or_else(|| {
|
||||
let next = known.partition_point(|&j| j < i);
|
||||
let j = next.checked_sub(1).or_else(|| known.get(next).map(|_| next))?;
|
||||
Some(raw[known[j]]? + dur(i as i64 - known[j] as i64))
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn chroma_name(c: filmcraft_frame::Chroma) -> &'static str {
|
||||
match c {
|
||||
filmcraft_frame::Chroma::C420 => "4:2:0",
|
||||
@@ -1015,4 +1024,16 @@ mod tests {
|
||||
assert_eq!(order, vec![0, 4, 5, 3]);
|
||||
assert_eq!(pts, vec![0, -2, -3, 3, 1, 2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timestamp_interpolation_handles_long_missing_runs_without_rescanning() {
|
||||
let mut raw = vec![None; 10_000];
|
||||
raw[4_000] = Some(40_000);
|
||||
raw[9_999] = Some(99_990);
|
||||
let pts = interpolate_pts(&raw, |frames| frames * 10);
|
||||
assert_eq!(pts[0], Some(0));
|
||||
assert_eq!(pts[3_999], Some(39_990));
|
||||
assert_eq!(pts[4_001], Some(40_010));
|
||||
assert_eq!(pts[9_998], Some(99_980));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user