diff --git a/crates/av1/src/decoder.rs b/crates/av1/src/decoder.rs
index 9f90f2f8..43abce22 100644
--- a/crates/av1/src/decoder.rs
+++ b/crates/av1/src/decoder.rs
@@ -405,49 +405,35 @@ impl Decoder {
r
}
+ /// Whether an OBU belongs to the selected operating point (spec 7.5: OBUs of other layers are dropped).
+ fn in_operating_point(&self, obu: &ObuUnit) -> bool {
+ if obu.obu_type != OBU_SEQUENCE_HEADER_T
+ && obu.obu_type != OBU_TEMPORAL_DELIMITER_T
+ && obu.ext
+ && let Some(seq) = &self.seq
+ && seq.op_idc != 0
+ {
+ let idc = seq.op_idc;
+ let in_t = (idc >> obu.temporal_id) & 1;
+ let in_s = (idc >> (obu.spatial_id + 8)) & 1;
+ if in_t == 0 || in_s == 0 {
+ return false;
+ }
+ }
+ true
+ }
+
fn parse_obus(&mut self, data: &[u8], out: &mut Vec<(Output, usize, u32)>) -> Result<()> {
let mut tu = 0usize;
let mut pos = 0;
while pos < data.len() {
- let h = data[pos];
- if h & 0x80 != 0 {
- return Err(Error::Invalid("obu_forbidden_bit"));
+ let obu = parse_obu_unit(data, pos)?;
+ pos = obu.end;
+ if !self.in_operating_point(&obu) {
+ continue;
}
- let obu_type = ((h >> 3) & 0xf) as u32;
- let ext = h & 4 != 0;
- let has_size = h & 2 != 0;
- let mut p = pos + 1;
- let (mut temporal_id, mut spatial_id) = (0, 0);
- if ext {
- let e = *data.get(p).ok_or(Error::Truncated)?;
- temporal_id = (e >> 5) as u32;
- spatial_id = ((e >> 3) & 3) as u32;
- p += 1;
- }
- let size = if has_size {
- let (v, n) = leb128(data.get(p..).ok_or(Error::Truncated)?)?;
- p += n;
- v as usize
- } else {
- data.len().checked_sub(p).ok_or(Error::Truncated)?
- };
- let end = p.checked_add(size).filter(|&e| e <= data.len()).ok_or(Error::Truncated)?;
- let payload = &data[p..end];
- pos = end;
- if obu_type != OBU_SEQUENCE_HEADER_T
- && obu_type != OBU_TEMPORAL_DELIMITER_T
- && ext
- && let Some(seq) = &self.seq
- && seq.op_idc != 0
- {
- let idc = seq.op_idc;
- let in_t = (idc >> temporal_id) & 1;
- let in_s = (idc >> (spatial_id + 8)) & 1;
- if in_t == 0 || in_s == 0 {
- continue;
- }
- }
- match obu_type {
+ let (payload, spatial_id) = (obu.payload, obu.spatial_id);
+ match obu.obu_type {
OBU_SEQUENCE_HEADER_T => {
let s = SequenceHeader::parse(payload)?;
self.seq = Some(s);
@@ -457,27 +443,11 @@ impl Decoder {
tu += 1;
}
OBU_FRAME_HEADER_T | OBU_REDUNDANT_FRAME_HEADER_T | OBU_FRAME_T => {
- if self.seen_frame_header {
- // frame_header_copy(): identical to the active header
- if obu_type != OBU_FRAME_T {
- continue;
- }
- }
- let mut r = BitReader::new(payload);
- let shown = self.frame_header(&mut r, temporal_id, spatial_id)?;
- if let Some(o) = shown {
+ if let Some(o) = self.frame_obu(&obu)? {
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;
- }
- if obu_type == OBU_FRAME_T {
- r.byte_align();
- let rest = payload.get(r.byte_pos()..).ok_or(Error::Truncated)?;
- if let Some(o) = self.tile_group(rest)? {
- out.push((o, tu, spatial_id));
- }
}
}
OBU_TILE_GROUP_T => {
@@ -492,6 +462,22 @@ impl Decoder {
Ok(())
}
+ /// A frame header or frame OBU: returns the picture it completes, if any.
+ fn frame_obu(&mut self, obu: &ObuUnit) -> Result