Skip to main content

codec/pixel_format/av1/
sequence.rs

1//! AV1 sequence header parser and `Av1SequenceHeader` type.
2//! See AV1 specification §5.5.2.
3
4use super::super::bitreader::BitReader;
5use super::obu::find_av1_obu;
6
7// ─── Av1SequenceHeader ─────────────────────────────────────────────
8
9/// Parsed AV1 sequence header fields (from OBU type 1, §5.5.2).
10/// Minimum subset needed to build `StdVideoAV1SequenceHeader` for
11/// Vulkan AV1 decode session parameters.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct Av1SequenceHeader {
14    pub seq_profile: u8,
15    pub still_picture: bool,
16    pub reduced_still_picture_header: bool,
17    pub max_frame_width_minus1: u32,
18    pub max_frame_height_minus1: u32,
19    pub seq_level_idx_0: u8,
20    /// `seq_tier[0]` from AV1 §5.5.1. Only carried in the bitstream
21    /// when `seq_level_idx_0 > 7` (i.e. level >= 4.0); below that the
22    /// spec says tier is implicitly 0 (Main). 0 = Main, 1 = High.
23    /// Required for the AV1 ISOBMFF codec string `av01.P.LLT.DD...`
24    /// (the `T` character).
25    pub seq_tier_0: u8,
26    pub bit_depth: u8,
27    pub monochrome: bool,
28    pub color_primaries: u8,
29    pub transfer_characteristics: u8,
30    pub matrix_coefficients: u8,
31    pub color_range: bool,
32    pub chroma_subsampling_x: bool,
33    pub chroma_subsampling_y: bool,
34    pub film_grain_params_present: bool,
35    pub enable_filter_intra: bool,
36    pub enable_intra_edge_filter: bool,
37    pub enable_interintra_compound: bool,
38    pub enable_masked_compound: bool,
39    pub enable_warped_motion: bool,
40    pub enable_dual_filter: bool,
41    pub enable_order_hint: bool,
42    pub enable_jnt_comp: bool,
43    pub enable_ref_frame_mvs: bool,
44    pub enable_superres: bool,
45    pub enable_cdef: bool,
46    pub enable_restoration: bool,
47    pub order_hint_bits: u8,
48    /// Per AV1 §5.5.1: 0 = all frames block screen-content tools,
49    /// 1 = all frames enable them, 2 = SELECT (each frame signals
50    /// its own bit in the uncompressed_header). Our frame-header
51    /// parser reads a per-frame bit only when this field == 2.
52    pub seq_force_screen_content_tools: u8,
53    /// 0 = all frames force non-integer MV, 1 = all force integer,
54    /// 2 = SELECT. Only relevant when screen-content tools allow.
55    pub seq_force_integer_mv: u8,
56    /// Bit-width of max_frame_width_minus_1 / max_frame_height_minus_1
57    /// fields in the sequence header. Vulkan's Std SPS requires these
58    /// to match so the session parameters object is byte-compatible
59    /// with what the driver re-parses from the bitstream.
60    pub frame_width_bits_minus_1: u8,
61    pub frame_height_bits_minus_1: u8,
62    pub use_128x128_superblock: bool,
63    /// AV1 §5.5.2 color_config bit — signals that U and V planes
64    /// carry separate q-delta values. Feeds
65    /// `StdVideoAV1ColorConfigFlags.separate_uv_delta_q` which the
66    /// Vulkan AV1 decoder reads at session-parameters creation.
67    pub separate_uv_delta_q: bool,
68}
69
70// ─── AV1 uvlc helper ───────────────────────────────────────────────
71
72/// AV1 uvlc (unsigned variable-length code) — count leading zero bits
73/// up to 31; then read that many bits as the suffix; value = (1<<N)-1+suffix.
74fn read_av1_uvlc(br: &mut BitReader) -> Option<u32> {
75    let mut leading_zeros = 0;
76    while leading_zeros < 32 {
77        if br.read_bits(1)? == 1 {
78            break;
79        }
80        leading_zeros += 1;
81    }
82    if leading_zeros >= 32 {
83        return None;
84    }
85    if leading_zeros == 0 {
86        return Some(0);
87    }
88    let suffix = br.read_bits(leading_zeros)?;
89    Some((1u32 << leading_zeros) - 1 + suffix)
90}
91
92// ─── Full sequence header parser ───────────────────────────────────
93
94/// Parse the AV1 sequence header OBU (obu_type=1). Returns the
95/// subset of §5.5.2 fields needed for Vulkan decode-session-params.
96/// Partial parse: we stop after color_config + film_grain_params_present
97/// (everything Vulkan's StdVideoAV1SequenceHeader cares about).
98pub fn parse_av1_sequence_header(sample: &[u8]) -> Option<Av1SequenceHeader> {
99    let obu = find_av1_obu(sample, 1)?;
100    let mut br = BitReader::new(obu);
101    let seq_profile = br.read_bits(3)? as u8;
102    let still_picture = br.read_bits(1)? == 1;
103    let reduced_still_picture_header = br.read_bits(1)? == 1;
104
105    let mut seq_level_idx_0 = 0u8;
106    let mut seq_tier_0 = 0u8;
107    let (_operating_points_cnt_minus_1, _timing_info_present_flag);
108    let mut order_hint_bits = 0u8;
109    let mut enable_order_hint = false;
110
111    if reduced_still_picture_header {
112        seq_level_idx_0 = br.read_bits(5)? as u8;
113        _operating_points_cnt_minus_1 = 0;
114        _timing_info_present_flag = false;
115    } else {
116        let timing_info_present_flag = br.read_bits(1)? == 1;
117        _timing_info_present_flag = timing_info_present_flag;
118        let mut decoder_model_info_present_flag = false;
119        let mut buffer_delay_length_minus_1 = 0u32;
120        if timing_info_present_flag {
121            let _num_units_in_display_tick = br.read_bits(32)?;
122            let _time_scale = br.read_bits(32)?;
123            let equal_picture_interval = br.read_bits(1)? == 1;
124            if equal_picture_interval {
125                let _num_ticks_per_picture_minus_1 = read_av1_uvlc(&mut br)?;
126            }
127            decoder_model_info_present_flag = br.read_bits(1)? == 1;
128            if decoder_model_info_present_flag {
129                buffer_delay_length_minus_1 = br.read_bits(5)?;
130                let _num_units_in_decoding_tick = br.read_bits(32)?;
131                let _buffer_removal_time_length_minus_1 = br.read_bits(5)?;
132                let _frame_presentation_time_length_minus_1 = br.read_bits(5)?;
133            }
134        }
135        // initial_display_delay_present_flag lives OUTSIDE the
136        // timing-info-present branch per AV1 §5.5.1 — my earlier
137        // parse had it nested, which desynced every field that
138        // followed on streams with timing_info absent.
139        let initial_display_delay_present_flag = br.read_bits(1)? == 1;
140        let operating_points_cnt_minus_1 = br.read_bits(5)? as u8;
141        _operating_points_cnt_minus_1 = operating_points_cnt_minus_1;
142        for i in 0..=operating_points_cnt_minus_1 {
143            let _operating_point_idc = br.read_bits(12)?;
144            let seq_level_idx_i = br.read_bits(5)? as u8;
145            // Per AV1 §5.5.1, seq_tier is present only for levels
146            // >= 4.0 (level_idx > 7); below that it's implicitly 0.
147            let seq_tier_i = if seq_level_idx_i > 7 {
148                br.read_bits(1)? as u8
149            } else {
150                0
151            };
152            if i == 0 {
153                seq_level_idx_0 = seq_level_idx_i;
154                seq_tier_0 = seq_tier_i;
155            }
156            // operating_parameters_info(i) — one per-op-point
157            // decoder_model_present_for_this_op gate.
158            if decoder_model_info_present_flag {
159                let decoder_model_present_for_this_op = br.read_bits(1)? == 1;
160                if decoder_model_present_for_this_op {
161                    let n = (buffer_delay_length_minus_1 + 1) as usize;
162                    let _buffer_delay = br.read_bits(n)?;
163                    let _encoder_buffer_delay = br.read_bits(n)?;
164                    let _low_delay_mode_flag = br.read_bits(1)?;
165                }
166            }
167            if initial_display_delay_present_flag {
168                let idd_present_for_this_op = br.read_bits(1)? == 1;
169                if idd_present_for_this_op {
170                    let _initial_display_delay_minus_1 = br.read_bits(4)?;
171                }
172            }
173        }
174    }
175    let frame_width_bits_minus_1 = br.read_bits(4)? as usize;
176    let frame_height_bits_minus_1 = br.read_bits(4)? as usize;
177    let max_frame_width_minus1 = br.read_bits(frame_width_bits_minus_1 + 1)?;
178    let max_frame_height_minus1 = br.read_bits(frame_height_bits_minus_1 + 1)?;
179
180    let frame_id_numbers_present_flag = if reduced_still_picture_header {
181        false
182    } else {
183        br.read_bits(1)? == 1
184    };
185    if frame_id_numbers_present_flag {
186        let _delta_frame_id_length_minus_2 = br.read_bits(4)?;
187        let _additional_frame_id_length_minus_1 = br.read_bits(3)?;
188    }
189    let use_128x128_superblock = br.read_bits(1)? == 1;
190    let enable_filter_intra = br.read_bits(1)? == 1;
191    let enable_intra_edge_filter = br.read_bits(1)? == 1;
192    let mut enable_interintra_compound = false;
193    let mut enable_masked_compound = false;
194    let mut enable_warped_motion = false;
195    let mut enable_dual_filter = false;
196    let mut enable_jnt_comp = false;
197    let mut enable_ref_frame_mvs = false;
198    let mut seq_force_screen_content_tools: u8 = 2; // SELECT when reduced_still_picture_header
199    let mut seq_force_integer_mv: u8 = 2;
200    if !reduced_still_picture_header {
201        enable_interintra_compound = br.read_bits(1)? == 1;
202        enable_masked_compound = br.read_bits(1)? == 1;
203        enable_warped_motion = br.read_bits(1)? == 1;
204        enable_dual_filter = br.read_bits(1)? == 1;
205        enable_order_hint = br.read_bits(1)? == 1;
206        if enable_order_hint {
207            enable_jnt_comp = br.read_bits(1)? == 1;
208            enable_ref_frame_mvs = br.read_bits(1)? == 1;
209        }
210        let seq_choose_screen_content_tools = br.read_bits(1)? == 1;
211        seq_force_screen_content_tools = if seq_choose_screen_content_tools {
212            2u8
213        } else {
214            br.read_bits(1)? as u8
215        };
216        if seq_force_screen_content_tools > 0 {
217            let seq_choose_integer_mv = br.read_bits(1)? == 1;
218            seq_force_integer_mv = if seq_choose_integer_mv {
219                2u8
220            } else {
221                br.read_bits(1)? as u8
222            };
223        }
224        if enable_order_hint {
225            order_hint_bits = br.read_bits(3)? as u8 + 1;
226        }
227    }
228    let enable_superres = br.read_bits(1)? == 1;
229    let enable_cdef = br.read_bits(1)? == 1;
230    let enable_restoration = br.read_bits(1)? == 1;
231
232    // color_config(seq_profile)
233    let high_bitdepth = br.read_bits(1)? == 1;
234    let bit_depth = if seq_profile == 2 && high_bitdepth {
235        if br.read_bits(1)? == 1 { 12 } else { 10 }
236    } else if high_bitdepth {
237        10
238    } else {
239        8
240    };
241    let monochrome = if seq_profile == 1 {
242        false
243    } else {
244        br.read_bits(1)? == 1
245    };
246    let color_description_present_flag = br.read_bits(1)? == 1;
247    let (color_primaries, transfer_characteristics, matrix_coefficients) =
248        if color_description_present_flag {
249            (
250                br.read_bits(8)? as u8,
251                br.read_bits(8)? as u8,
252                br.read_bits(8)? as u8,
253            )
254        } else {
255            (2u8, 2u8, 2u8) // unspecified
256        };
257    let color_range;
258    let (subx, suby);
259    let mut separate_uv_delta_q = false;
260    if monochrome {
261        color_range = br.read_bits(1)? == 1;
262        subx = true;
263        suby = true;
264    } else if color_primaries == 1 && transfer_characteristics == 13 && matrix_coefficients == 0 {
265        color_range = true;
266        subx = false;
267        suby = false;
268    } else {
269        color_range = br.read_bits(1)? == 1;
270        match seq_profile {
271            0 => {
272                subx = true;
273                suby = true;
274            }
275            1 => {
276                subx = false;
277                suby = false;
278            }
279            2 => {
280                if bit_depth == 12 {
281                    subx = br.read_bits(1)? == 1;
282                    suby = if subx { br.read_bits(1)? == 1 } else { false };
283                } else {
284                    subx = true;
285                    suby = false;
286                }
287            }
288            _ => {
289                subx = true;
290                suby = true;
291            }
292        }
293        if subx && suby {
294            let _chroma_sample_position = br.read_bits(2)?;
295        }
296        separate_uv_delta_q = br.read_bits(1)? == 1;
297    }
298    let film_grain_params_present = br.read_bits(1)? == 1;
299
300    Some(Av1SequenceHeader {
301        seq_profile,
302        still_picture,
303        reduced_still_picture_header,
304        max_frame_width_minus1,
305        max_frame_height_minus1,
306        seq_level_idx_0,
307        seq_tier_0,
308        bit_depth,
309        monochrome,
310        color_primaries,
311        transfer_characteristics,
312        matrix_coefficients,
313        color_range,
314        chroma_subsampling_x: subx,
315        chroma_subsampling_y: suby,
316        film_grain_params_present,
317        enable_filter_intra,
318        enable_intra_edge_filter,
319        enable_interintra_compound,
320        enable_masked_compound,
321        enable_warped_motion,
322        enable_dual_filter,
323        enable_order_hint,
324        enable_jnt_comp,
325        enable_ref_frame_mvs,
326        enable_superres,
327        enable_cdef,
328        enable_restoration,
329        order_hint_bits,
330        seq_force_screen_content_tools,
331        seq_force_integer_mv,
332        frame_width_bits_minus_1: frame_width_bits_minus_1 as u8,
333        frame_height_bits_minus_1: frame_height_bits_minus_1 as u8,
334        use_128x128_superblock,
335        separate_uv_delta_q,
336    })
337}