Skip to main content

zvidlib_av1_syntax/
lib.rs

1//! Bounded AV1 low-overhead bitstream and configuration parsing.
2//!
3//! This module intentionally provides syntax/state only; it does not
4//! implement pixel reconstruction. The bounded lossless monochrome
5//! reconstruction in `zvidlib::av1_intra_decoder` and
6//! `zvidlib::av1_inter_decoder` is kept for its own callers; the registered
7//! [`VideoDecoderFactory`](zvidlib_core::VideoDecoderFactory),
8//! `zvidlib::native_av1_video_decoder_factory`, decodes with the complete
9//! Main-profile decoder in `zvidlib-av1-decoder`, which carries its own header
10//! parsing.
11
12use zvidlib_core::{Error, ErrorKind, Limits, Result};
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15#[non_exhaustive]
16pub enum Av1SyntaxSupport {
17    MainProfile,
18    UnsupportedProfile(u8),
19}
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22#[repr(u8)]
23#[non_exhaustive]
24pub enum Av1ObuType {
25    Reserved = 0,
26    SequenceHeader = 1,
27    TemporalDelimiter = 2,
28    FrameHeader = 3,
29    TileGroup = 4,
30    Metadata = 5,
31    Frame = 6,
32    RedundantFrameHeader = 7,
33    TileList = 8,
34    Padding = 15,
35}
36
37impl Av1ObuType {
38    fn from_bits(value: u8) -> Self {
39        match value {
40            1 => Self::SequenceHeader,
41            2 => Self::TemporalDelimiter,
42            3 => Self::FrameHeader,
43            4 => Self::TileGroup,
44            5 => Self::Metadata,
45            6 => Self::Frame,
46            7 => Self::RedundantFrameHeader,
47            8 => Self::TileList,
48            15 => Self::Padding,
49            _ => Self::Reserved,
50        }
51    }
52}
53
54#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55pub struct Av1ObuHeader {
56    pub obu_type: Av1ObuType,
57    pub extension_flag: bool,
58    pub temporal_id: u8,
59    pub spatial_id: u8,
60    pub header_len: u8,
61    pub size_field_len: u8,
62}
63
64#[derive(Clone, Debug, Eq, PartialEq)]
65pub struct Av1OperatingPoint {
66    pub idc: u16,
67    pub level: u8,
68    pub tier: bool,
69    pub decoder_model_present: bool,
70    pub initial_display_delay: Option<u8>,
71}
72
73#[derive(Clone, Debug, Eq, PartialEq)]
74pub struct Av1ColorConfig {
75    pub bit_depth: u8,
76    pub monochrome: bool,
77    pub color_description: Option<(u8, u8, u8)>,
78    pub color_range: bool,
79    pub subsampling_x: bool,
80    pub subsampling_y: bool,
81    pub chroma_sample_position: u8,
82    pub separate_uv_delta_q: bool,
83}
84
85#[derive(Clone, Debug, Eq, PartialEq)]
86pub struct Av1SequenceHeader {
87    pub seq_profile: u8,
88    pub support: Av1SyntaxSupport,
89    pub still_picture: bool,
90    pub reduced_still_picture_header: bool,
91    pub operating_points: Vec<Av1OperatingPoint>,
92    pub max_frame_width: u32,
93    pub max_frame_height: u32,
94    pub frame_width_bits: u8,
95    pub frame_height_bits: u8,
96    pub frame_id_numbers_present: bool,
97    pub delta_frame_id_length: u8,
98    pub additional_frame_id_length: u8,
99    pub enable_interintra_compound: bool,
100    pub enable_masked_compound: bool,
101    pub enable_warped_motion: bool,
102    pub enable_dual_filter: bool,
103    pub enable_order_hint: bool,
104    pub enable_jnt_comp: bool,
105    pub enable_ref_frame_mvs: bool,
106    pub order_hint_bits: u8,
107    pub seq_force_screen_content_tools: u8,
108    pub seq_force_integer_mv: u8,
109    pub enable_superres: bool,
110    pub enable_cdef: bool,
111    pub enable_restoration: bool,
112    pub color_config: Av1ColorConfig,
113    pub film_grain_params_present: bool,
114    pub decoder_model_info_present: bool,
115    pub equal_picture_interval: bool,
116    pub buffer_delay_length: u8,
117    pub buffer_removal_time_length: u8,
118    pub frame_presentation_time_length: u8,
119}
120
121#[derive(Clone, Copy, Debug, Eq, PartialEq)]
122#[repr(u8)]
123#[non_exhaustive]
124pub enum Av1FrameType {
125    Key = 0,
126    Inter = 1,
127    IntraOnly = 2,
128    Switch = 3,
129}
130
131#[derive(Clone, Debug, Eq, PartialEq)]
132pub struct Av1FrameHeader {
133    pub show_existing_frame: bool,
134    pub frame_to_show_map_idx: Option<u8>,
135    pub frame_type: Av1FrameType,
136    pub show_frame: bool,
137    pub showable_frame: bool,
138    pub error_resilient_mode: bool,
139    pub disable_cdf_update: bool,
140    pub allow_screen_content_tools: bool,
141    pub force_integer_mv: bool,
142    pub current_frame_id: Option<u32>,
143    pub frame_size_override_flag: bool,
144    pub order_hint: u32,
145    pub primary_ref_frame: Option<u8>,
146    pub refresh_frame_flags: u8,
147    /// Number of header bits consumed before syntax owned by reconstruction stages.
148    pub parsed_bits: usize,
149}
150
151#[derive(Clone, Debug, Eq, PartialEq)]
152pub struct Av1TileGroup {
153    pub tile_start: u32,
154    pub tile_end: u32,
155    pub data: Vec<u8>,
156    /// Byte ranges for each tile, relative to `data`.
157    pub tile_ranges: Vec<(usize, usize)>,
158}
159
160#[derive(Clone, Debug, Eq, PartialEq)]
161pub struct Av1Metadata {
162    pub metadata_type: u64,
163    pub data: Vec<u8>,
164}
165
166#[derive(Clone, Debug, Eq, PartialEq)]
167#[non_exhaustive]
168pub enum Av1Obu {
169    SequenceHeader {
170        header: Av1ObuHeader,
171        sequence: Av1SequenceHeader,
172    },
173    TemporalDelimiter {
174        header: Av1ObuHeader,
175    },
176    FrameHeader {
177        header: Av1ObuHeader,
178        frame: Av1FrameHeader,
179        payload: Vec<u8>,
180    },
181    TileGroup {
182        header: Av1ObuHeader,
183        group: Av1TileGroup,
184    },
185    Metadata {
186        header: Av1ObuHeader,
187        metadata: Av1Metadata,
188    },
189    Frame {
190        header: Av1ObuHeader,
191        frame: Av1FrameHeader,
192        payload: Vec<u8>,
193    },
194    Skipped {
195        header: Av1ObuHeader,
196        payload_len: u64,
197    },
198}
199
200#[derive(Clone, Debug, Eq, PartialEq)]
201pub struct Av1CodecConfigurationRecord {
202    pub seq_profile: u8,
203    pub seq_level_idx_0: u8,
204    pub seq_tier_0: bool,
205    pub high_bitdepth: bool,
206    pub twelve_bit: bool,
207    pub monochrome: bool,
208    pub chroma_subsampling_x: bool,
209    pub chroma_subsampling_y: bool,
210    pub chroma_sample_position: u8,
211    pub initial_presentation_delay: Option<u8>,
212    pub config_obus: Vec<Av1Obu>,
213    pub support: Av1SyntaxSupport,
214}
215
216impl Av1CodecConfigurationRecord {
217    /// Parses a complete `av1C` box, including its 32-bit size and fourcc.
218    pub fn parse(bytes: &[u8], limits: &Limits) -> Result<Self> {
219        check_allocation(bytes.len(), limits, "av1C box")?;
220        if bytes.len() < 12 {
221            return malformed("av1C box is truncated");
222        }
223        let size32 = u32::from_be_bytes(bytes[0..4].try_into().expect("four bytes"));
224        let (declared, payload_offset) = if size32 == 1 {
225            let large = bytes.get(8..16).ok_or_else(|| {
226                Error::new(ErrorKind::MalformedMedia, "extended av1C box is truncated")
227            })?;
228            let declared = u64::from_be_bytes(large.try_into().expect("eight bytes"));
229            let declared = usize::try_from(declared)
230                .map_err(|_| resource("av1C box size does not fit this platform"))?;
231            (declared, 16)
232        } else {
233            let declared = usize::try_from(size32)
234                .map_err(|_| resource("av1C box size does not fit this platform"))?;
235            (declared, 8)
236        };
237        if declared != bytes.len() || &bytes[4..8] != b"av1C" {
238            return malformed("av1C box size or fourcc is invalid");
239        }
240        let p = bytes
241            .get(payload_offset..)
242            .ok_or_else(|| Error::new(ErrorKind::MalformedMedia, "av1C record is truncated"))?;
243        if p.len() < 4 {
244            return malformed("av1C record is truncated");
245        }
246        if p[0] != 0x81 {
247            return malformed("av1C marker or version is invalid");
248        }
249        // Only `p[3]`'s top three bits are reserved. `p[2]`'s low two bits are
250        // `chroma_sample_position`, cross-checked and stored below.
251        if p[3] & 0b1110_0000 != 0 {
252            return malformed("av1C reserved bits are nonzero");
253        }
254        let seq_profile = p[1] >> 5;
255        if seq_profile > 2 {
256            return malformed("av1C sequence profile is reserved");
257        }
258        let delay = if p[3] & 0x10 != 0 {
259            Some((p[3] & 0x0f) + 1)
260        } else {
261            None
262        };
263        let mut parser = Av1Parser::new(*limits)?;
264        let config_obus = parser.parse_low_overhead(&p[4..])?;
265        for obu in &config_obus {
266            if !matches!(obu, Av1Obu::SequenceHeader { .. } | Av1Obu::Metadata { .. }) {
267                return malformed("av1C configOBUs contain a disallowed OBU type");
268            }
269        }
270        if let Some(Av1Obu::SequenceHeader { sequence, .. }) = config_obus
271            .iter()
272            .find(|obu| matches!(obu, Av1Obu::SequenceHeader { .. }))
273        {
274            let c = &sequence.color_config;
275            let expected_high = c.bit_depth > 8;
276            let expected_twelve = c.bit_depth == 12;
277            if sequence.seq_profile != seq_profile
278                || expected_high != (p[2] & 0x40 != 0)
279                || expected_twelve != (p[2] & 0x20 != 0)
280                || c.monochrome != (p[2] & 0x10 != 0)
281                || c.subsampling_x != (p[2] & 0x08 != 0)
282                || c.subsampling_y != (p[2] & 0x04 != 0)
283                || c.chroma_sample_position != p[2] & 0x03
284            {
285                return malformed("av1C fields disagree with its sequence header OBU");
286            }
287        }
288        Ok(Self {
289            seq_profile,
290            seq_level_idx_0: p[1] & 0x1f,
291            seq_tier_0: p[2] & 0x80 != 0,
292            high_bitdepth: p[2] & 0x40 != 0,
293            twelve_bit: p[2] & 0x20 != 0,
294            monochrome: p[2] & 0x10 != 0,
295            chroma_subsampling_x: p[2] & 0x08 != 0,
296            chroma_subsampling_y: p[2] & 0x04 != 0,
297            chroma_sample_position: p[2] & 0x03,
298            initial_presentation_delay: delay,
299            config_obus,
300            support: support_for_profile(seq_profile),
301        })
302    }
303}
304
305#[derive(Clone, Debug)]
306pub struct Av1Parser {
307    limits: Limits,
308    pub sequence: Option<Av1SequenceHeader>,
309    pub frame: Option<Av1FrameHeader>,
310    seen_frame_header: bool,
311}
312
313impl Av1Parser {
314    pub fn new(limits: Limits) -> Result<Self> {
315        if limits.max_av1_obus_per_unit == 0
316            || limits.max_av1_operating_points == 0
317            || limits.max_av1_operating_points > 32
318            || limits.max_av1_tiles_per_frame == 0
319        {
320            return Err(resource("AV1 parser limits must be nonzero"));
321        }
322        Ok(Self {
323            limits,
324            sequence: None,
325            frame: None,
326            seen_frame_header: false,
327        })
328    }
329
330    /// Seeds the parser with a sequence header retained by a stateful
331    /// decoder. Low-overhead temporal units are not required to repeat the
332    /// sequence header before every frame, so callers decoding a sequence of
333    /// temporal units must carry this state across parser instances.
334    #[doc(hidden)]
335    pub fn set_sequence(&mut self, sequence: Av1SequenceHeader) {
336        self.sequence = Some(sequence);
337    }
338
339    /// Parses one low-overhead AV1 byte stream. Every OBU must carry a leb128 size.
340    pub fn parse_low_overhead(&mut self, bytes: &[u8]) -> Result<Vec<Av1Obu>> {
341        check_allocation(bytes.len(), &self.limits, "AV1 input")?;
342        let capacity = bytes.len().min(self.limits.max_av1_obus_per_unit as usize);
343        let mut obus = Vec::with_capacity(capacity);
344        let mut offset = 0usize;
345        while offset < bytes.len() {
346            if obus.len() >= self.limits.max_av1_obus_per_unit as usize {
347                return Err(resource("AV1 OBU count exceeds configured limit"));
348            }
349            let (header, header_bytes) = parse_obu_header(&bytes[offset..])?;
350            offset = offset
351                .checked_add(header_bytes)
352                .ok_or_else(|| resource("AV1 offset overflow"))?;
353            let (payload_size, size_len) = parse_leb128(&bytes[offset..])?;
354            offset = offset
355                .checked_add(size_len)
356                .ok_or_else(|| resource("AV1 offset overflow"))?;
357            let payload_len = usize::try_from(payload_size)
358                .map_err(|_| resource("AV1 OBU payload does not fit this platform"))?;
359            check_allocation(payload_len, &self.limits, "AV1 OBU payload")?;
360            let end = offset
361                .checked_add(payload_len)
362                .ok_or_else(|| resource("AV1 OBU end overflow"))?;
363            let payload = bytes.get(offset..end).ok_or_else(|| {
364                Error::new(ErrorKind::MalformedMedia, "AV1 OBU payload is truncated")
365            })?;
366            offset = end;
367            let mut header = header;
368            header.size_field_len = u8::try_from(size_len).expect("leb128 is at most eight bytes");
369            obus.push(self.parse_obu(header, payload)?);
370        }
371        Ok(obus)
372    }
373
374    /// Parses a tile-group payload once the frame header's tile layout is known.
375    pub fn parse_tile_group(
376        payload: &[u8],
377        tile_cols_log2: u8,
378        tile_rows_log2: u8,
379        tile_size_bytes: u8,
380        limits: &Limits,
381    ) -> Result<Av1TileGroup> {
382        check_allocation(payload.len(), limits, "AV1 tile group")?;
383        let tile_bits = tile_cols_log2
384            .checked_add(tile_rows_log2)
385            .ok_or_else(|| resource("AV1 tile bit count overflow"))?;
386        if tile_bits >= 32 {
387            return Err(resource("AV1 tile count is too large"));
388        }
389        let tile_count = 1u32
390            .checked_shl(u32::from(tile_bits))
391            .ok_or_else(|| resource("AV1 tile count overflow"))?;
392        if tile_count == 0 || tile_count > limits.max_av1_tiles_per_frame {
393            return Err(resource("AV1 tile count exceeds configured limit"));
394        }
395        if !(1..=4).contains(&tile_size_bytes) {
396            return malformed("AV1 tile size byte count is invalid");
397        }
398        let mut bits = Bits::new(payload);
399        let (tile_start, tile_end) = if tile_count == 1 {
400            (0, 0)
401        } else if bits.flag("tile_start_and_end_present_flag")? {
402            (
403                bits.u32(usize::from(tile_bits), "tg_start")?,
404                bits.u32(usize::from(tile_bits), "tg_end")?,
405            )
406        } else {
407            (0, tile_count - 1)
408        };
409        if tile_start > tile_end || tile_end >= tile_count {
410            return malformed("AV1 tile group range is invalid");
411        }
412        while bits.position() & 7 != 0 {
413            if bits.flag("tile group alignment bit")? {
414                return malformed("AV1 tile group alignment bit is nonzero");
415            }
416        }
417        let data_offset = bits.position() / 8;
418        let data = payload
419            .get(data_offset..)
420            .ok_or_else(|| Error::new(ErrorKind::MalformedMedia, "AV1 tile group is truncated"))?
421            .to_vec();
422        let group_count = tile_end - tile_start + 1;
423        let range_capacity = usize::try_from(group_count)
424            .map_err(|_| resource("AV1 tile group count does not fit this platform"))?;
425        let mut tile_ranges = Vec::new();
426        tile_ranges
427            .try_reserve(range_capacity)
428            .map_err(|_| resource("AV1 tile range allocation failed"))?;
429        let mut cursor = 0usize;
430        for index in 0..group_count {
431            let tile_len = if index + 1 == group_count {
432                data.len().checked_sub(cursor).ok_or_else(|| {
433                    Error::new(ErrorKind::MalformedMedia, "AV1 tile data is truncated")
434                })?
435            } else {
436                let size_len = usize::from(tile_size_bytes);
437                let size_end = cursor
438                    .checked_add(size_len)
439                    .ok_or_else(|| resource("AV1 tile size offset overflow"))?;
440                let size_bytes = data.get(cursor..size_end).ok_or_else(|| {
441                    Error::new(ErrorKind::MalformedMedia, "AV1 tile size is truncated")
442                })?;
443                let mut minus_one = 0u32;
444                for (shift, byte) in size_bytes.iter().enumerate() {
445                    minus_one |= u32::from(*byte) << (shift * 8);
446                }
447                cursor = size_end;
448                usize::try_from(minus_one)
449                    .map_err(|_| resource("AV1 tile size does not fit this platform"))?
450                    .checked_add(1)
451                    .ok_or_else(|| resource("AV1 tile size overflow"))?
452            };
453            let end = cursor
454                .checked_add(tile_len)
455                .ok_or_else(|| resource("AV1 tile end overflow"))?;
456            if end > data.len() {
457                return malformed("AV1 tile data is truncated");
458            }
459            tile_ranges.push((cursor, end));
460            cursor = end;
461        }
462        if cursor != data.len() {
463            return malformed("AV1 tile group has unconsumed data");
464        }
465        Ok(Av1TileGroup {
466            tile_start,
467            tile_end,
468            data,
469            tile_ranges,
470        })
471    }
472
473    fn parse_obu(&mut self, header: Av1ObuHeader, payload: &[u8]) -> Result<Av1Obu> {
474        match header.obu_type {
475            Av1ObuType::SequenceHeader => {
476                let sequence = parse_sequence_header(payload, &self.limits)?;
477                self.sequence = Some(sequence.clone());
478                Ok(Av1Obu::SequenceHeader { header, sequence })
479            }
480            Av1ObuType::TemporalDelimiter => {
481                if !payload.is_empty() {
482                    return malformed("temporal delimiter OBU must be empty");
483                }
484                self.seen_frame_header = false;
485                Ok(Av1Obu::TemporalDelimiter { header })
486            }
487            Av1ObuType::FrameHeader | Av1ObuType::RedundantFrameHeader => {
488                let sequence = self.sequence.as_ref().ok_or_else(|| {
489                    Error::new(
490                        ErrorKind::MalformedMedia,
491                        "frame header precedes sequence header",
492                    )
493                })?;
494                let frame = if header.obu_type == Av1ObuType::RedundantFrameHeader {
495                    self.frame.clone().ok_or_else(|| {
496                        Error::new(
497                            ErrorKind::MalformedMedia,
498                            "redundant frame header has no frame to copy",
499                        )
500                    })?
501                } else {
502                    parse_frame_header(payload, sequence)?
503                };
504                self.seen_frame_header = !frame.show_existing_frame;
505                self.frame = Some(frame.clone());
506                Ok(Av1Obu::FrameHeader {
507                    header,
508                    frame,
509                    payload: payload.to_vec(),
510                })
511            }
512            Av1ObuType::Frame => {
513                let sequence = self.sequence.as_ref().ok_or_else(|| {
514                    Error::new(
515                        ErrorKind::MalformedMedia,
516                        "frame OBU precedes sequence header",
517                    )
518                })?;
519                let frame = parse_frame_header(payload, sequence)?;
520                self.seen_frame_header = false;
521                self.frame = Some(frame.clone());
522                Ok(Av1Obu::Frame {
523                    header,
524                    frame,
525                    payload: payload.to_vec(),
526                })
527            }
528            Av1ObuType::TileGroup => {
529                if !self.seen_frame_header {
530                    return malformed("tile group has no active frame header");
531                }
532                // Tile layout parsing belongs to the reconstruction milestone. Until
533                // it supplies a count, one bounded tile covers the group payload.
534                let tile_count = 1u32;
535                if tile_count > self.limits.max_av1_tiles_per_frame {
536                    return Err(resource("AV1 tile count exceeds configured limit"));
537                }
538                Ok(Av1Obu::TileGroup {
539                    header,
540                    group: Self::parse_tile_group(payload, 0, 0, 1, &self.limits)?,
541                })
542            }
543            Av1ObuType::Metadata => {
544                let (metadata_type, used) = parse_leb128(payload)?;
545                let data = payload
546                    .get(used..)
547                    .ok_or_else(|| {
548                        Error::new(ErrorKind::MalformedMedia, "AV1 metadata is truncated")
549                    })?
550                    .to_vec();
551                Ok(Av1Obu::Metadata {
552                    header,
553                    metadata: Av1Metadata {
554                        metadata_type,
555                        data,
556                    },
557                })
558            }
559            Av1ObuType::Padding | Av1ObuType::Reserved | Av1ObuType::TileList => {
560                Ok(Av1Obu::Skipped {
561                    header,
562                    payload_len: payload.len() as u64,
563                })
564            }
565        }
566    }
567}
568
569fn parse_obu_header(bytes: &[u8]) -> Result<(Av1ObuHeader, usize)> {
570    let first = *bytes
571        .first()
572        .ok_or_else(|| Error::new(ErrorKind::MalformedMedia, "AV1 OBU header is truncated"))?;
573    if first & 0x80 != 0 || first & 0x01 != 0 {
574        return malformed("AV1 OBU header reserved bits are nonzero");
575    }
576    if first & 0x02 == 0 {
577        return malformed("low-overhead AV1 OBU has no size field");
578    }
579    let extension_flag = first & 0x04 != 0;
580    let (temporal_id, spatial_id, len) = if extension_flag {
581        let ext = *bytes.get(1).ok_or_else(|| {
582            Error::new(
583                ErrorKind::MalformedMedia,
584                "AV1 OBU extension header is truncated",
585            )
586        })?;
587        if ext & 0x07 != 0 {
588            return malformed("AV1 OBU extension reserved bits are nonzero");
589        }
590        (ext >> 5, (ext >> 3) & 0x03, 2)
591    } else {
592        (0, 0, 1)
593    };
594    Ok((
595        Av1ObuHeader {
596            obu_type: Av1ObuType::from_bits((first >> 3) & 0x0f),
597            extension_flag,
598            temporal_id,
599            spatial_id,
600            header_len: len as u8,
601            size_field_len: 0,
602        },
603        len,
604    ))
605}
606
607fn parse_sequence_header(payload: &[u8], limits: &Limits) -> Result<Av1SequenceHeader> {
608    let mut b = Bits::new(payload);
609    let seq_profile = b.u8(3, "seq_profile")?;
610    if seq_profile > 2 {
611        return malformed("AV1 sequence profile is reserved");
612    }
613    let still_picture = b.flag("still_picture")?;
614    let reduced = b.flag("reduced_still_picture_header")?;
615    if reduced && !still_picture {
616        return malformed("reduced AV1 header is not a still picture");
617    }
618    let mut decoder_model_info_present = false;
619    let mut equal_picture_interval = false;
620    let mut buffer_delay_length = 0;
621    let mut buffer_removal_time_length = 0;
622    let mut frame_presentation_time_length = 0;
623    let mut operating_points = Vec::new();
624    if reduced {
625        operating_points.push(Av1OperatingPoint {
626            idc: 0,
627            level: b.u8(5, "seq_level_idx")?,
628            tier: false,
629            decoder_model_present: false,
630            initial_display_delay: None,
631        });
632    } else {
633        let timing_present = b.flag("timing_info_present_flag")?;
634        if timing_present {
635            b.skip(64, "timing_info")?;
636            equal_picture_interval = b.flag("equal_picture_interval")?;
637            if equal_picture_interval {
638                let _ = b.uvlc("num_ticks_per_picture_minus_1")?;
639            }
640            decoder_model_info_present = b.flag("decoder_model_info_present_flag")?;
641            if decoder_model_info_present {
642                buffer_delay_length = b
643                    .u8(5, "buffer_delay_length_minus_1")?
644                    .checked_add(1)
645                    .ok_or_else(|| resource("buffer delay length overflow"))?;
646                b.skip(32, "num_units_in_decoding_tick")?;
647                buffer_removal_time_length = b
648                    .u8(5, "buffer_removal_time_length_minus_1")?
649                    .checked_add(1)
650                    .ok_or_else(|| resource("buffer removal time length overflow"))?;
651                frame_presentation_time_length = b
652                    .u8(5, "frame_presentation_time_length_minus_1")?
653                    .checked_add(1)
654                    .ok_or_else(|| resource("frame presentation time length overflow"))?;
655            }
656        }
657        let initial_display_delay_present = b.flag("initial_display_delay_present_flag")?;
658        let count = usize::from(b.u8(5, "operating_points_cnt_minus_1")?) + 1;
659        if count > usize::from(limits.max_av1_operating_points) {
660            return Err(resource(
661                "AV1 operating point count exceeds configured limit",
662            ));
663        }
664        operating_points
665            .try_reserve(count)
666            .map_err(|_| resource("AV1 operating point allocation failed"))?;
667        for _ in 0..count {
668            let idc = b.u16(12, "operating_point_idc")?;
669            let level = b.u8(5, "seq_level_idx")?;
670            let tier = level > 7 && b.flag("seq_tier")?;
671            let model =
672                decoder_model_info_present && b.flag("decoder_model_present_for_this_op")?;
673            if model {
674                b.skip(
675                    usize::from(buffer_delay_length) * 2 + 1,
676                    "operating parameters",
677                )?;
678            }
679            let delay = if initial_display_delay_present
680                && b.flag("initial_display_delay_present_for_this_op")?
681            {
682                Some(b.u8(4, "initial_display_delay_minus_1")? + 1)
683            } else {
684                None
685            };
686            operating_points.push(Av1OperatingPoint {
687                idc,
688                level,
689                tier,
690                decoder_model_present: model,
691                initial_display_delay: delay,
692            });
693        }
694    }
695    let frame_width_bits = b.u8(4, "frame_width_bits_minus_1")? + 1;
696    let frame_height_bits = b.u8(4, "frame_height_bits_minus_1")? + 1;
697    let max_frame_width = b
698        .u32(usize::from(frame_width_bits), "max_frame_width_minus_1")?
699        .checked_add(1)
700        .ok_or_else(|| resource("AV1 width overflow"))?;
701    let max_frame_height = b
702        .u32(usize::from(frame_height_bits), "max_frame_height_minus_1")?
703        .checked_add(1)
704        .ok_or_else(|| resource("AV1 height overflow"))?;
705    if max_frame_width > limits.max_width || max_frame_height > limits.max_height {
706        return Err(resource("AV1 dimensions exceed configured limits"));
707    }
708    let frame_id_numbers_present = !reduced && b.flag("frame_id_numbers_present_flag")?;
709    let (delta_frame_id_length, additional_frame_id_length) = if frame_id_numbers_present {
710        (
711            b.u8(4, "delta_frame_id_length_minus_2")? + 2,
712            b.u8(3, "additional_frame_id_length_minus_1")? + 1,
713        )
714    } else {
715        (0, 0)
716    };
717    let _use_128x128_superblock = b.flag("use_128x128_superblock")?;
718    let _enable_filter_intra = b.flag("enable_filter_intra")?;
719    let _enable_intra_edge_filter = b.flag("enable_intra_edge_filter")?;
720    let (
721        enable_interintra_compound,
722        enable_masked_compound,
723        enable_warped_motion,
724        enable_dual_filter,
725        enable_order_hint,
726        enable_jnt_comp,
727        enable_ref_frame_mvs,
728        seq_force_screen_content_tools,
729        seq_force_integer_mv,
730        order_hint_bits,
731    ) = if reduced {
732        (false, false, false, false, false, false, false, 2, 2, 0)
733    } else {
734        let enable_interintra_compound = b.flag("enable_interintra_compound")?;
735        let enable_masked_compound = b.flag("enable_masked_compound")?;
736        let enable_warped_motion = b.flag("enable_warped_motion")?;
737        let enable_dual_filter = b.flag("enable_dual_filter")?;
738        let enable_order_hint = b.flag("enable_order_hint")?;
739        let (enable_jnt_comp, enable_ref_frame_mvs) = if enable_order_hint {
740            (b.flag("enable_jnt_comp")?, b.flag("enable_ref_frame_mvs")?)
741        } else {
742            (false, false)
743        };
744        let screen = if b.flag("seq_choose_screen_content_tools")? {
745            2
746        } else {
747            u8::from(b.flag("seq_force_screen_content_tools")?)
748        };
749        let integer = if screen > 0 {
750            if b.flag("seq_choose_integer_mv")? {
751                2
752            } else {
753                u8::from(b.flag("seq_force_integer_mv")?)
754            }
755        } else {
756            2
757        };
758        let bits = if enable_order_hint {
759            b.u8(3, "order_hint_bits_minus_1")? + 1
760        } else {
761            0
762        };
763        (
764            enable_interintra_compound,
765            enable_masked_compound,
766            enable_warped_motion,
767            enable_dual_filter,
768            enable_order_hint,
769            enable_jnt_comp,
770            enable_ref_frame_mvs,
771            screen,
772            integer,
773            bits,
774        )
775    };
776    let enable_superres = b.flag("enable_superres")?;
777    let enable_cdef = b.flag("enable_cdef")?;
778    let enable_restoration = b.flag("enable_restoration")?;
779    let color_config = parse_color_config(&mut b, seq_profile)?;
780    let film_grain_params_present = b.flag("film_grain_params_present")?;
781    b.trailing_bits()?;
782    Ok(Av1SequenceHeader {
783        seq_profile,
784        support: support_for_profile(seq_profile),
785        still_picture,
786        reduced_still_picture_header: reduced,
787        operating_points,
788        max_frame_width,
789        max_frame_height,
790        frame_width_bits,
791        frame_height_bits,
792        frame_id_numbers_present,
793        delta_frame_id_length,
794        additional_frame_id_length,
795        enable_interintra_compound,
796        enable_masked_compound,
797        enable_warped_motion,
798        enable_dual_filter,
799        enable_order_hint,
800        enable_jnt_comp,
801        enable_ref_frame_mvs,
802        order_hint_bits,
803        seq_force_screen_content_tools,
804        seq_force_integer_mv,
805        enable_superres,
806        enable_cdef,
807        enable_restoration,
808        color_config,
809        film_grain_params_present,
810        decoder_model_info_present,
811        equal_picture_interval,
812        buffer_delay_length,
813        buffer_removal_time_length,
814        frame_presentation_time_length,
815    })
816}
817
818fn parse_color_config(b: &mut Bits<'_>, profile: u8) -> Result<Av1ColorConfig> {
819    let high = b.flag("high_bitdepth")?;
820    let twelve = profile == 2 && high && b.flag("twelve_bit")?;
821    let bit_depth = if twelve {
822        12
823    } else if high {
824        10
825    } else {
826        8
827    };
828    let monochrome = profile != 1 && b.flag("mono_chrome")?;
829    let color_description = if b.flag("color_description_present_flag")? {
830        Some((
831            b.u8(8, "color_primaries")?,
832            b.u8(8, "transfer_characteristics")?,
833            b.u8(8, "matrix_coefficients")?,
834        ))
835    } else {
836        None
837    };
838    let identity_rgb = color_description == Some((1, 13, 0));
839    let (color_range, sx, sy, pos, separate) = if monochrome {
840        (b.flag("color_range")?, true, true, 0, false)
841    } else if identity_rgb {
842        if profile != 1 {
843            return malformed("identity RGB requires AV1 profile 1");
844        }
845        (true, false, false, 0, false)
846    } else {
847        let range = b.flag("color_range")?;
848        let (sx, sy) = match profile {
849            0 => (true, true),
850            1 => (false, false),
851            2 if bit_depth == 12 => (b.flag("subsampling_x")?, b.flag("subsampling_y")?),
852            2 => (true, false),
853            _ => unreachable!(),
854        };
855        let pos = if sx && sy {
856            b.u8(2, "chroma_sample_position")?
857        } else {
858            0
859        };
860        (range, sx, sy, pos, b.flag("separate_uv_delta_q")?)
861    };
862    Ok(Av1ColorConfig {
863        bit_depth,
864        monochrome,
865        color_description,
866        color_range,
867        subsampling_x: sx,
868        subsampling_y: sy,
869        chroma_sample_position: pos,
870        separate_uv_delta_q: separate,
871    })
872}
873
874fn parse_frame_header(payload: &[u8], seq: &Av1SequenceHeader) -> Result<Av1FrameHeader> {
875    let mut b = Bits::new(payload);
876    let mut show_existing = false;
877    let mut map_idx = None;
878    let (frame_type, show_frame, showable_frame, error_resilient) = if seq
879        .reduced_still_picture_header
880    {
881        (Av1FrameType::Key, true, false, true)
882    } else {
883        show_existing = b.flag("show_existing_frame")?;
884        if show_existing {
885            map_idx = Some(b.u8(3, "frame_to_show_map_idx")?);
886            if seq.decoder_model_info_present && !seq.equal_picture_interval {
887                b.skip(
888                    usize::from(seq.frame_presentation_time_length),
889                    "frame_presentation_time",
890                )?;
891            }
892            return Ok(Av1FrameHeader {
893                show_existing_frame: true,
894                frame_to_show_map_idx: map_idx,
895                frame_type: Av1FrameType::Inter,
896                show_frame: true,
897                showable_frame: false,
898                error_resilient_mode: false,
899                disable_cdf_update: false,
900                allow_screen_content_tools: false,
901                force_integer_mv: false,
902                current_frame_id: None,
903                frame_size_override_flag: false,
904                order_hint: 0,
905                primary_ref_frame: None,
906                refresh_frame_flags: 0,
907                parsed_bits: b.position(),
908            });
909        }
910        let frame_type = match b.u8(2, "frame_type")? {
911            0 => Av1FrameType::Key,
912            1 => Av1FrameType::Inter,
913            2 => Av1FrameType::IntraOnly,
914            _ => Av1FrameType::Switch,
915        };
916        let show = b.flag("show_frame")?;
917        if show && seq.decoder_model_info_present && !seq.equal_picture_interval {
918            b.skip(
919                usize::from(seq.frame_presentation_time_length),
920                "frame_presentation_time",
921            )?;
922        }
923        let showable = if show {
924            frame_type != Av1FrameType::Key
925        } else {
926            b.flag("showable_frame")?
927        };
928        let error =
929            if frame_type == Av1FrameType::Switch || (frame_type == Av1FrameType::Key && show) {
930                true
931            } else {
932                b.flag("error_resilient_mode")?
933            };
934        (frame_type, show, showable, error)
935    };
936    let disable_cdf_update = b.flag("disable_cdf_update")?;
937    let allow_screen_content_tools = match seq.seq_force_screen_content_tools {
938        2 => b.flag("allow_screen_content_tools")?,
939        v => v != 0,
940    };
941    let force_integer_mv = if allow_screen_content_tools {
942        match seq.seq_force_integer_mv {
943            2 => b.flag("force_integer_mv")?,
944            v => v != 0,
945        }
946    } else {
947        false
948    };
949    let current_frame_id = if seq.frame_id_numbers_present {
950        Some(b.u32(
951            usize::from(seq.delta_frame_id_length + seq.additional_frame_id_length),
952            "current_frame_id",
953        )?)
954    } else {
955        None
956    };
957    let frame_size_override_flag = if frame_type == Av1FrameType::Switch {
958        true
959    } else if seq.reduced_still_picture_header {
960        false
961    } else {
962        b.flag("frame_size_override_flag")?
963    };
964    let order_hint = if seq.enable_order_hint {
965        b.u32(usize::from(seq.order_hint_bits), "order_hint")?
966    } else {
967        0
968    };
969    let intra = matches!(frame_type, Av1FrameType::Key | Av1FrameType::IntraOnly);
970    let primary_ref_frame = if intra || error_resilient {
971        None
972    } else {
973        Some(b.u8(3, "primary_ref_frame")?)
974    };
975    let refresh_frame_flags =
976        if frame_type == Av1FrameType::Switch || (frame_type == Av1FrameType::Key && show_frame) {
977            0xff
978        } else {
979            b.u8(8, "refresh_frame_flags")?
980        };
981    Ok(Av1FrameHeader {
982        show_existing_frame: show_existing,
983        frame_to_show_map_idx: map_idx,
984        frame_type,
985        show_frame,
986        showable_frame,
987        error_resilient_mode: error_resilient,
988        disable_cdf_update,
989        allow_screen_content_tools,
990        force_integer_mv,
991        current_frame_id,
992        frame_size_override_flag,
993        order_hint,
994        primary_ref_frame,
995        refresh_frame_flags,
996        parsed_bits: b.position(),
997    })
998}
999
1000fn parse_leb128(bytes: &[u8]) -> Result<(u64, usize)> {
1001    let mut value = 0u64;
1002    for i in 0..8usize {
1003        let byte = *bytes.get(i).ok_or_else(|| {
1004            Error::new(ErrorKind::MalformedMedia, "AV1 leb128 value is truncated")
1005        })?;
1006        let part = u64::from(byte & 0x7f)
1007            .checked_shl((i * 7) as u32)
1008            .ok_or_else(|| resource("AV1 leb128 shift overflow"))?;
1009        value = value
1010            .checked_add(part)
1011            .ok_or_else(|| resource("AV1 leb128 value overflow"))?;
1012        if byte & 0x80 == 0 {
1013            return Ok((value, i + 1));
1014        }
1015    }
1016    malformed("AV1 leb128 value exceeds eight bytes")
1017}
1018
1019struct Bits<'a> {
1020    bytes: &'a [u8],
1021    bit: usize,
1022}
1023impl<'a> Bits<'a> {
1024    fn new(bytes: &'a [u8]) -> Self {
1025        Self { bytes, bit: 0 }
1026    }
1027    fn position(&self) -> usize {
1028        self.bit
1029    }
1030    fn u32(&mut self, n: usize, name: &str) -> Result<u32> {
1031        if n > 32 {
1032            return Err(resource(format!("{name} is wider than 32 bits")));
1033        }
1034        let mut value = 0u32;
1035        for _ in 0..n {
1036            value = (value << 1) | u32::from(self.flag(name)?);
1037        }
1038        Ok(value)
1039    }
1040    fn u16(&mut self, n: usize, name: &str) -> Result<u16> {
1041        Ok(self.u32(n, name)? as u16)
1042    }
1043    fn u8(&mut self, n: usize, name: &str) -> Result<u8> {
1044        Ok(self.u32(n, name)? as u8)
1045    }
1046    fn flag(&mut self, name: &str) -> Result<bool> {
1047        let byte = self.bytes.get(self.bit / 8).ok_or_else(|| {
1048            Error::new(
1049                ErrorKind::MalformedMedia,
1050                format!("AV1 {name} is truncated"),
1051            )
1052        })?;
1053        let value = byte & (0x80 >> (self.bit & 7)) != 0;
1054        self.bit = self
1055            .bit
1056            .checked_add(1)
1057            .ok_or_else(|| resource("AV1 bit offset overflow"))?;
1058        Ok(value)
1059    }
1060    fn skip(&mut self, n: usize, name: &str) -> Result<()> {
1061        for _ in 0..n {
1062            self.flag(name)?;
1063        }
1064        Ok(())
1065    }
1066    fn uvlc(&mut self, name: &str) -> Result<u32> {
1067        let mut leading = 0usize;
1068        while !self.flag(name)? {
1069            leading += 1;
1070            if leading >= 32 {
1071                return Err(resource(format!("AV1 {name} exceeds 32 bits")));
1072            }
1073        }
1074        if leading == 0 {
1075            return Ok(0);
1076        }
1077        let suffix = self.u32(leading, name)?;
1078        (1u32 << leading)
1079            .checked_sub(1)
1080            .and_then(|v| v.checked_add(suffix))
1081            .ok_or_else(|| resource(format!("AV1 {name} overflow")))
1082    }
1083    fn trailing_bits(&mut self) -> Result<()> {
1084        if !self.flag("trailing_one_bit")? {
1085            return malformed("AV1 trailing one bit is zero");
1086        }
1087        while self.bit & 7 != 0 {
1088            if self.flag("trailing_zero_bit")? {
1089                return malformed("AV1 trailing zero bit is nonzero");
1090            }
1091        }
1092        if self.bit != self.bytes.len() * 8 {
1093            return malformed("AV1 sequence header has trailing bytes");
1094        }
1095        Ok(())
1096    }
1097}
1098
1099fn support_for_profile(profile: u8) -> Av1SyntaxSupport {
1100    if profile == 0 {
1101        Av1SyntaxSupport::MainProfile
1102    } else {
1103        Av1SyntaxSupport::UnsupportedProfile(profile)
1104    }
1105}
1106fn check_allocation(bytes: usize, limits: &Limits, what: &str) -> Result<()> {
1107    if bytes as u128 > u128::from(limits.max_allocation_bytes) {
1108        Err(resource(format!(
1109            "{what} exceeds configured allocation limit"
1110        )))
1111    } else {
1112        Ok(())
1113    }
1114}
1115fn malformed<T>(message: impl Into<String>) -> Result<T> {
1116    Err(Error::new(ErrorKind::MalformedMedia, message))
1117}
1118fn resource(message: impl Into<String>) -> Error {
1119    Error::new(ErrorKind::ResourceLimit, message)
1120}
1121
1122#[cfg(test)]
1123mod tests {
1124    use super::*;
1125
1126    #[derive(Default)]
1127    struct BitWriter {
1128        bytes: Vec<u8>,
1129        bit: usize,
1130    }
1131
1132    impl BitWriter {
1133        fn bits(&mut self, value: u32, width: usize) {
1134            for shift in (0..width).rev() {
1135                if self.bit & 7 == 0 {
1136                    self.bytes.push(0);
1137                }
1138                if value & (1 << shift) != 0 {
1139                    let last = self.bytes.len() - 1;
1140                    self.bytes[last] |= 0x80 >> (self.bit & 7);
1141                }
1142                self.bit += 1;
1143            }
1144        }
1145
1146        fn trailing_bits(&mut self) {
1147            self.bits(1, 1);
1148            while self.bit & 7 != 0 {
1149                self.bits(0, 1);
1150            }
1151        }
1152    }
1153
1154    fn reduced_sequence_payload() -> Vec<u8> {
1155        let mut b = BitWriter::default();
1156        b.bits(0, 3); // Main profile
1157        b.bits(1, 1); // still_picture
1158        b.bits(1, 1); // reduced_still_picture_header
1159        b.bits(4, 5); // level 2.0
1160        b.bits(0, 4); // one width bit
1161        b.bits(0, 4); // one height bit
1162        b.bits(0, 1); // max width minus one = 0
1163        b.bits(0, 1); // max height minus one = 0
1164        b.bits(0, 1); // use_128x128_superblock
1165        b.bits(0, 1); // enable_filter_intra
1166        b.bits(0, 1); // enable_intra_edge_filter
1167        b.bits(0, 1); // enable_superres
1168        b.bits(0, 1); // enable_cdef
1169        b.bits(0, 1); // enable_restoration
1170        b.bits(0, 1); // high_bitdepth
1171        b.bits(1, 1); // monochrome
1172        b.bits(0, 1); // color description absent
1173        b.bits(0, 1); // studio range
1174        b.bits(0, 1); // film grain absent
1175        b.trailing_bits();
1176        b.bytes
1177    }
1178
1179    /// A reduced still-picture sequence header for 8-bit 4:2:0 Main profile
1180    /// carrying `pos` as its `chroma_sample_position`.
1181    fn reduced_sequence_payload_420(pos: u8) -> Vec<u8> {
1182        let mut b = BitWriter::default();
1183        b.bits(0, 3); // Main profile
1184        b.bits(1, 1); // still_picture
1185        b.bits(1, 1); // reduced_still_picture_header
1186        b.bits(4, 5); // level 2.0
1187        b.bits(0, 4); // one width bit
1188        b.bits(0, 4); // one height bit
1189        b.bits(0, 1); // max width minus one = 0
1190        b.bits(0, 1); // max height minus one = 0
1191        b.bits(0, 1); // use_128x128_superblock
1192        b.bits(0, 1); // enable_filter_intra
1193        b.bits(0, 1); // enable_intra_edge_filter
1194        b.bits(0, 1); // enable_superres
1195        b.bits(0, 1); // enable_cdef
1196        b.bits(0, 1); // enable_restoration
1197        b.bits(0, 1); // high_bitdepth
1198        b.bits(0, 1); // monochrome
1199        b.bits(0, 1); // color description absent
1200        b.bits(0, 1); // studio range
1201        b.bits(u32::from(pos), 2); // chroma_sample_position
1202        b.bits(0, 1); // separate_uv_delta_q
1203        b.bits(0, 1); // film grain absent
1204        b.trailing_bits();
1205        b.bytes
1206    }
1207
1208    fn obu(obu_type: u8, payload: &[u8]) -> Vec<u8> {
1209        assert!(payload.len() < 128);
1210        let mut bytes = vec![(obu_type << 3) | 0x02, payload.len() as u8];
1211        bytes.extend_from_slice(payload);
1212        bytes
1213    }
1214
1215    fn av1c(payload: &[u8]) -> Vec<u8> {
1216        let mut bytes = (8u32 + payload.len() as u32).to_be_bytes().to_vec();
1217        bytes.extend_from_slice(b"av1C");
1218        bytes.extend_from_slice(payload);
1219        bytes
1220    }
1221
1222    fn extended_av1c(payload: &[u8]) -> Vec<u8> {
1223        let mut bytes = 1u32.to_be_bytes().to_vec();
1224        bytes.extend_from_slice(b"av1C");
1225        bytes.extend_from_slice(&(16u64 + payload.len() as u64).to_be_bytes());
1226        bytes.extend_from_slice(payload);
1227        bytes
1228    }
1229
1230    #[test]
1231    fn parses_sequence_frame_tile_metadata_and_padding_obus() {
1232        let mut stream = obu(1, &reduced_sequence_payload());
1233        stream.extend_from_slice(&obu(2, &[]));
1234        stream.extend_from_slice(&obu(3, &[0]));
1235        stream.extend_from_slice(&obu(4, &[0xaa, 0xbb]));
1236        stream.extend_from_slice(&obu(5, &[0xac, 0x02, 7, 8]));
1237        stream.extend_from_slice(&obu(15, &[0, 0]));
1238
1239        let mut parser = Av1Parser::new(Limits::default()).unwrap();
1240        let parsed = parser.parse_low_overhead(&stream).unwrap();
1241        assert_eq!(parsed.len(), 6);
1242        let sequence = parser.sequence.as_ref().unwrap();
1243        assert_eq!(sequence.max_frame_width, 1);
1244        assert_eq!(sequence.max_frame_height, 1);
1245        assert_eq!(sequence.support, Av1SyntaxSupport::MainProfile);
1246        assert_eq!(sequence.color_config.bit_depth, 8);
1247        assert!(sequence.color_config.monochrome);
1248        assert!(matches!(parsed[2], Av1Obu::FrameHeader { .. }));
1249        assert!(matches!(parsed[3], Av1Obu::TileGroup { .. }));
1250        match &parsed[4] {
1251            Av1Obu::Metadata { metadata, .. } => {
1252                assert_eq!(metadata.metadata_type, 300);
1253                assert_eq!(metadata.data, [7, 8]);
1254            }
1255            other => panic!("unexpected OBU: {other:?}"),
1256        }
1257        assert!(matches!(parsed[5], Av1Obu::Skipped { payload_len: 2, .. }));
1258    }
1259
1260    #[test]
1261    fn parses_and_cross_checks_complete_av1c_record() {
1262        let sequence_obu = obu(1, &reduced_sequence_payload());
1263        let mut record = vec![0x81, 0x04, 0x1c, 0];
1264        record.extend_from_slice(&sequence_obu);
1265        let parsed =
1266            Av1CodecConfigurationRecord::parse(&av1c(&record), &Limits::default()).unwrap();
1267        assert_eq!(parsed.seq_profile, 0);
1268        assert_eq!(parsed.seq_level_idx_0, 4);
1269        assert_eq!(parsed.config_obus.len(), 1);
1270        assert_eq!(parsed.support, Av1SyntaxSupport::MainProfile);
1271
1272        let extended =
1273            Av1CodecConfigurationRecord::parse(&extended_av1c(&record), &Limits::default())
1274                .unwrap();
1275        assert_eq!(extended, parsed);
1276    }
1277
1278    #[test]
1279    fn av1c_accepts_every_chroma_sample_position() {
1280        for pos in 0..4u8 {
1281            // seq_tier_0 = 0, 8-bit, color, 4:2:0 subsampling, and `pos` in the
1282            // low two bits that used to be refused as reserved.
1283            let byte2 = 0x0c | pos;
1284
1285            let bare = Av1CodecConfigurationRecord::parse(
1286                &av1c(&[0x81, 0x04, byte2, 0]),
1287                &Limits::default(),
1288            )
1289            .unwrap();
1290            assert_eq!(bare.chroma_sample_position, pos);
1291            assert!(bare.chroma_subsampling_x);
1292            assert!(bare.chroma_subsampling_y);
1293
1294            // The same value also has to survive the sequence-header agreement
1295            // check against an OBU that declares it.
1296            let mut record = vec![0x81, 0x04, byte2, 0];
1297            record.extend_from_slice(&obu(1, &reduced_sequence_payload_420(pos)));
1298            let cross_checked =
1299                Av1CodecConfigurationRecord::parse(&av1c(&record), &Limits::default()).unwrap();
1300            assert_eq!(cross_checked.chroma_sample_position, pos);
1301        }
1302    }
1303
1304    #[test]
1305    fn av1c_still_rejects_p3_reserved_bits_and_sequence_header_disagreement() {
1306        for reserved in [0b0010_0000u8, 0b0100_0000, 0b1000_0000] {
1307            let error = Av1CodecConfigurationRecord::parse(
1308                &av1c(&[0x81, 0x04, 0x0c, reserved]),
1309                &Limits::default(),
1310            )
1311            .unwrap_err();
1312            assert_eq!(error.kind(), ErrorKind::MalformedMedia);
1313        }
1314
1315        // A record whose `chroma_sample_position` contradicts its sequence
1316        // header is still malformed; the field is checked, not ignored.
1317        let mut record = vec![0x81, 0x04, 0x0d, 0];
1318        record.extend_from_slice(&obu(1, &reduced_sequence_payload_420(2)));
1319        let error =
1320            Av1CodecConfigurationRecord::parse(&av1c(&record), &Limits::default()).unwrap_err();
1321        assert_eq!(error.kind(), ErrorKind::MalformedMedia);
1322    }
1323
1324    #[test]
1325    fn av1c_rejects_disallowed_configuration_obus() {
1326        let mut record = vec![0x81, 0x04, 0, 0];
1327        record.extend_from_slice(&obu(2, &[]));
1328        let error =
1329            Av1CodecConfigurationRecord::parse(&av1c(&record), &Limits::default()).unwrap_err();
1330        assert_eq!(error.kind(), ErrorKind::MalformedMedia);
1331    }
1332
1333    #[test]
1334    fn unsupported_profile_is_parsed_but_never_marked_main() {
1335        let parsed =
1336            Av1CodecConfigurationRecord::parse(&av1c(&[0x81, 0x24, 0, 0]), &Limits::default())
1337                .unwrap();
1338        assert_eq!(parsed.support, Av1SyntaxSupport::UnsupportedProfile(1));
1339    }
1340
1341    #[test]
1342    fn malformed_corpus_returns_stable_errors_without_panics() {
1343        let corpus: &[&[u8]] = &[
1344            &[0x0a],
1345            &[0x0a, 0x80],
1346            &[0x0a, 4, 0],
1347            &[0x80, 0],
1348            &[0x0e, 0],
1349            &[0x06, 0],
1350            &[0x16, 0],
1351            &[0x2a, 1, 0x80],
1352        ];
1353        for bytes in corpus {
1354            let result = std::panic::catch_unwind(|| {
1355                Av1Parser::new(Limits::default())
1356                    .unwrap()
1357                    .parse_low_overhead(bytes)
1358            });
1359            assert!(result.is_ok(), "parser panicked for {bytes:02x?}");
1360            let error = result.unwrap().unwrap_err();
1361            assert!(matches!(
1362                error.kind(),
1363                ErrorKind::MalformedMedia | ErrorKind::ResourceLimit
1364            ));
1365        }
1366    }
1367
1368    #[test]
1369    fn gates_dimensions_obu_counts_and_allocations() {
1370        let sequence = obu(1, &reduced_sequence_payload());
1371        let dimensions = Limits {
1372            max_width: 0,
1373            ..Limits::default()
1374        };
1375        let error = Av1Parser::new(dimensions)
1376            .unwrap()
1377            .parse_low_overhead(&sequence)
1378            .unwrap_err();
1379        assert_eq!(error.kind(), ErrorKind::ResourceLimit);
1380
1381        let counts = Limits {
1382            max_av1_obus_per_unit: 1,
1383            ..Limits::default()
1384        };
1385        let mut two = obu(2, &[]);
1386        two.extend_from_slice(&obu(2, &[]));
1387        let error = Av1Parser::new(counts)
1388            .unwrap()
1389            .parse_low_overhead(&two)
1390            .unwrap_err();
1391        assert_eq!(error.kind(), ErrorKind::ResourceLimit);
1392
1393        let allocations = Limits {
1394            max_allocation_bytes: 1,
1395            ..Limits::default()
1396        };
1397        let error = Av1Parser::new(allocations)
1398            .unwrap()
1399            .parse_low_overhead(&[0x12, 0])
1400            .unwrap_err();
1401        assert_eq!(error.kind(), ErrorKind::ResourceLimit);
1402    }
1403
1404    #[test]
1405    fn parses_bounded_multi_tile_group_sizes() {
1406        // Four tiles, explicit range 1..2, then a one-byte size-minus-one for
1407        // the first tile followed by both tile payloads.
1408        let payload = [0b1011_0000, 1, 0xaa, 0xbb, 0xcc];
1409        let group = Av1Parser::parse_tile_group(&payload, 2, 0, 1, &Limits::default()).unwrap();
1410        assert_eq!((group.tile_start, group.tile_end), (1, 2));
1411        assert_eq!(group.tile_ranges, [(1, 3), (3, 4)]);
1412        assert_eq!(group.data, [1, 0xaa, 0xbb, 0xcc]);
1413
1414        let limits = Limits {
1415            max_av1_tiles_per_frame: 2,
1416            ..Limits::default()
1417        };
1418        assert_eq!(
1419            Av1Parser::parse_tile_group(&payload, 2, 0, 1, &limits)
1420                .unwrap_err()
1421                .kind(),
1422            ErrorKind::ResourceLimit
1423        );
1424    }
1425}