diff --git a/crates/av1/src/decoder.rs b/crates/av1/src/decoder.rs index b60f94dd..9f90f2f8 100644 --- a/crates/av1/src/decoder.rs +++ b/crates/av1/src/decoder.rs @@ -466,6 +466,9 @@ impl Decoder { let mut r = BitReader::new(payload); let shown = self.frame_header(&mut r, temporal_id, spatial_id)?; if let Some(o) = shown { + if out.iter().filter(|(o, _, _)| matches!(o, Output::Existing { .. })).count() >= 32 { + return Err(Error::Invalid("too many frame outputs in chunk")); + } out.push((o, tu, spatial_id)); continue; } diff --git a/crates/av1/src/header.rs b/crates/av1/src/header.rs index e89876c7..85133df8 100644 --- a/crates/av1/src/header.rs +++ b/crates/av1/src/header.rs @@ -906,6 +906,9 @@ impl FrameHeader { let mut widest = 0; let mut start = 0; while start < sb_cols { + if t.mi_col_starts.len() >= MAX_TILE_COLS { + return Err(Error::Invalid("tile_cols exceeds MAX_TILE_COLS")); + } t.mi_col_starts.push(start << sb_shift); let max_width = (sb_cols - start).min(max_tile_width_sb); let size = r.ns(max_width)? + 1; @@ -923,6 +926,9 @@ impl FrameHeader { let max_tile_height_sb = (max_tile_area_sb / widest.max(1)).max(1); let mut start = 0; while start < sb_rows { + if t.mi_row_starts.len() >= MAX_TILE_ROWS { + return Err(Error::Invalid("tile_rows exceeds MAX_TILE_ROWS")); + } t.mi_row_starts.push(start << sb_shift); let max_height = (sb_rows - start).min(max_tile_height_sb); let size = r.ns(max_height)? + 1; diff --git a/crates/h264/src/dpb.rs b/crates/h264/src/dpb.rs index 5662e299..da652b47 100644 --- a/crates/h264/src/dpb.rs +++ b/crates/h264/src/dpb.rs @@ -327,12 +327,18 @@ impl Dpb { had5 = true; } 6 => { - for e in &mut self.entries { - if e.mark == RefMark::Long && e.long_term_frame_idx == m.long_term_frame_idx { - e.mark = RefMark::Unused; + let allowed = match self.max_long_term_frame_idx { + Some(max) => m.long_term_frame_idx <= max, + None => false, + }; + if allowed { + for e in &mut self.entries { + if e.mark == RefMark::Long && e.long_term_frame_idx == m.long_term_frame_idx { + e.mark = RefMark::Unused; + } } + *cur_long = Some(m.long_term_frame_idx); } - *cur_long = Some(m.long_term_frame_idx); } _ => {} } @@ -410,6 +416,11 @@ impl Dpb { } else { while self.entries.len() >= self.capacity { if !self.bump(out) { + if let Some(pos) = self.entries.iter().position(|e| !e.needed_for_output) { + self.entries.remove(pos); + } else { + self.entries.remove(0); + } break; } } diff --git a/crates/hevc/src/decoder.rs b/crates/hevc/src/decoder.rs index 9038c3ad..4b46044d 100644 --- a/crates/hevc/src/decoder.rs +++ b/crates/hevc/src/decoder.rs @@ -440,11 +440,22 @@ impl Decoder { let template = Frame::new(0, 0, w, h, sps.log2_ctb, sps.bit_depth_luma, sps.bit_depth_chroma); let next_id = &mut self.next_id; let mut missing = 0u64; + let max_missing = (sps.max_dec_pic_buffering as u64).clamp(1, 16); + let mut shared_gray: Option = None; let mut make_missing = |p: i32| -> FrameRef { missing += 1; + if missing > max_missing + && let Some(ref g) = shared_gray + { + return g.clone(); + } let id = *next_id; *next_id += 1; - Arc::new(Frame::gray(id, p, &template)) + let f = Arc::new(Frame::gray(id, p, &template)); + if shared_gray.is_none() { + shared_gray = Some(f.clone()); + } + f }; let rps = self.dpb.apply_rps(sh, poc, max_lsb, irap_no_rasl, &mut make_missing)?; if missing > 0 { diff --git a/crates/media/src/audio.rs b/crates/media/src/audio.rs index 80fe0b43..5f623a3e 100644 --- a/crates/media/src/audio.rs +++ b/crates/media/src/audio.rs @@ -24,6 +24,8 @@ pub struct Pcm { pub samples: Vec, } +const MAX_PCM_SAMPLES: usize = 64 * 1024 * 1024; + impl Pcm { /// Sample frames (samples per channel). pub fn frames(&self) -> usize { @@ -47,8 +49,10 @@ impl Pcm { fn push_planar(planes: &[Vec], out: &mut Vec) { let n = planes.iter().map(Vec::len).min().unwrap_or(0); - out.reserve(n * planes.len()); - for i in 0..n { + let remaining = MAX_PCM_SAMPLES.saturating_sub(out.len()); + let to_push = n.min(remaining / planes.len().max(1)); + out.reserve(to_push * planes.len()); + for i in 0..to_push { for p in planes { out.push(p[i]); } @@ -96,7 +100,13 @@ fn decode_ogg_opus(bytes: &Bytes) -> Option> { let timing = deckcraft_ogg::OpusTiming::of(stream, dec.pre_skip() as u32).map_err(|e| MediaError::Corrupt(e.to_string()))?; let mut out = Vec::new(); for i in 0..stream.packets.len() { + if MAX_PCM_SAMPLES.saturating_sub(out.len()) < channels.max(1) as usize { + break; + } let Ok(p) = file.read_packet(src, s, i) else { continue }; + if p.is_empty() { + continue; + } match dec.decode(Some(&p)) { Ok(planes) => Pcm::push_planar(&planes, &mut out), Err(e) => log::debug!("opus packet {i}: {e}"), @@ -126,7 +136,13 @@ fn decode_mkv_opus(bytes: &Bytes) -> Option> { let mut out = Vec::new(); let n = file.tracks.get(ti).map(|t| t.samples.len()).unwrap_or(0); for i in 0..n { + if MAX_PCM_SAMPLES.saturating_sub(out.len()) < channels.max(1) as usize { + break; + } let Ok(p) = file.read_sample(src, ti, i) else { continue }; + if p.is_empty() { + continue; + } match dec.decode(Some(&p)) { Ok(planes) => Pcm::push_planar(&planes, &mut out), Err(e) => log::debug!("opus packet {i}: {e}"), diff --git a/crates/ogg/src/lib.rs b/crates/ogg/src/lib.rs index 7342574e..69ff1858 100644 --- a/crates/ogg/src/lib.rs +++ b/crates/ogg/src/lib.rs @@ -212,10 +212,11 @@ pub fn open(src: &(impl ByteSource + ?Sized)) -> Result { let mut pos = 0u64; let mut found_any = false; let mut hdr = [0u8; 27 + 255]; + let mut scanner = CaptureScanner::new(); while pos + 27 <= len { src.read_at(pos, &mut hdr[..27])?; if &hdr[..4] != b"OggS" || hdr[4] != 0 { - match find_capture(src, pos + 1, len)? { + match scanner.find(src, pos + 1, len)? { Some(p) => { if found_any { warnings.push(format!("lost sync at byte {pos}; resumed at {p}")); @@ -336,21 +337,47 @@ pub fn open(src: &(impl ByteSource + ?Sized)) -> Result { Ok(OggFile { streams, warnings }) } -fn find_capture(src: &(impl ByteSource + ?Sized), from: u64, len: u64) -> Result> { - let mut at = from; - while at + 4 <= len { - let n = ((len - at) as usize).min(1 << 20); - let mut buf = vec![0u8; n]; - src.read_at(at, &mut buf)?; - if let Some(i) = buf.windows(4).position(|w| w == b"OggS") { - return Ok(Some(at + i as u64)); +struct CaptureScanner { + buf: Vec, + buf_start: u64, + budget: usize, +} + +impl CaptureScanner { + fn new() -> Self { + Self { buf: Vec::new(), buf_start: 0, budget: 32 * 1024 * 1024 } + } + + fn find(&mut self, src: &(impl ByteSource + ?Sized), from: u64, len: u64) -> Result> { + let mut at = from; + while at + 4 <= len { + let buf_end = self.buf_start + self.buf.len() as u64; + if at >= self.buf_start && at + 4 <= buf_end { + let off = (at - self.buf_start) as usize; + if let Some(i) = self.buf[off..].windows(4).position(|w| w == b"OggS") { + return Ok(Some(at + i as u64)); + } + at = buf_end.saturating_sub(3); + continue; + } + if self.budget == 0 { + return Ok(None); + } + let n = ((len - at) as usize).min(64 * 1024).min(self.budget); + if n < 4 { + break; + } + self.buf.resize(n, 0); + src.read_at(at, &mut self.buf)?; + self.buf_start = at; + self.budget = self.budget.saturating_sub(n); + if let Some(i) = self.buf.windows(4).position(|w| w == b"OggS") { + return Ok(Some(at + i as u64)); + } + at += (n - 3) as u64; } - if n < 4 { - break; - } - at += (n - 3) as u64; + Ok(None) } - Ok(None) } fn read_parts(src: &(impl ByteSource + ?Sized), parts: &[(u64, u32)]) -> Result> {