1use super::bits::BitReader;
17use otf_pixels_core::{PixelsError, Result};
18
19const SELECT: u8 = 2;
22
23const CP_BT_709: u8 = 1;
25const TC_SRGB: u8 = 13;
26const MC_IDENTITY: u8 = 0;
27const CP_UNSPECIFIED: u8 = 2;
28const TC_UNSPECIFIED: u8 = 2;
29const MC_UNSPECIFIED: u8 = 2;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct OperatingPoint {
34 pub idc: u16,
36 pub seq_level_idx: u8,
38 pub seq_tier: u8,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct ColorConfig {
45 pub bit_depth: u8,
47 pub mono_chrome: bool,
49 pub num_planes: u8,
51 pub color_primaries: u8,
53 pub transfer_characteristics: u8,
55 pub matrix_coefficients: u8,
57 pub color_range: bool,
59 pub subsampling_x: u8,
61 pub subsampling_y: u8,
63 pub chroma_sample_position: u8,
65 pub separate_uv_delta_q: bool,
67}
68
69#[derive(Debug, Clone)]
71pub struct SequenceHeader {
72 pub seq_profile: u8,
74 pub still_picture: bool,
76 pub reduced_still_picture_header: bool,
78 pub operating_points: Vec<OperatingPoint>,
80 pub operating_point_idc: u16,
83 pub frame_width_bits: u32,
85 pub frame_height_bits: u32,
87 pub max_frame_width: u32,
89 pub max_frame_height: u32,
91 pub frame_id_numbers_present: bool,
93 pub delta_frame_id_length: u32,
95 pub additional_frame_id_length: u32,
97 pub use_128x128_superblock: bool,
99 pub enable_filter_intra: bool,
101 pub enable_intra_edge_filter: bool,
103 pub enable_interintra_compound: bool,
106 pub enable_masked_compound: bool,
108 pub enable_warped_motion: bool,
110 pub enable_dual_filter: bool,
112 pub enable_order_hint: bool,
114 pub enable_jnt_comp: bool,
116 pub enable_ref_frame_mvs: bool,
118 pub seq_force_screen_content_tools: u8,
120 pub seq_force_integer_mv: u8,
122 pub order_hint_bits: u32,
124 pub enable_superres: bool,
126 pub enable_cdef: bool,
128 pub enable_restoration: bool,
130 pub color: ColorConfig,
132 pub film_grain_params_present: bool,
134 pub decoder_model_info_present: bool,
136 pub buffer_delay_length: u32,
138 pub buffer_removal_time_length: u32,
140 pub frame_presentation_time_length: u32,
142 pub equal_picture_interval: bool,
144 pub timing_info_present: bool,
146}
147
148impl SequenceHeader {
149 pub fn parse(r: &mut BitReader<'_>) -> Result<Self> {
151 let seq_profile = r.f(3)? as u8;
152 if seq_profile > 2 {
153 return Err(PixelsError::malformed(
154 "avif",
155 "AV1 seq_profile above 2 is not a defined profile",
156 ));
157 }
158 let still_picture = r.flag()?;
159 let reduced_still_picture_header = r.flag()?;
160
161 let mut timing_info_present = false;
162 let mut decoder_model_info_present = false;
163 let mut buffer_delay_length = 0;
164 let mut buffer_removal_time_length = 0;
165 let mut frame_presentation_time_length = 0;
166 let mut equal_picture_interval = false;
167 let mut operating_points = Vec::new();
168
169 if reduced_still_picture_header {
170 let seq_level_idx = r.f(5)? as u8;
171 operating_points.push(OperatingPoint {
172 idc: 0,
173 seq_level_idx,
174 seq_tier: 0,
175 });
176 } else {
177 timing_info_present = r.flag()?;
178 if timing_info_present {
179 equal_picture_interval = parse_timing_info(r)?;
180 decoder_model_info_present = r.flag()?;
181 if decoder_model_info_present {
182 let model = parse_decoder_model_info(r)?;
183 buffer_delay_length = model.0;
184 buffer_removal_time_length = model.1;
185 frame_presentation_time_length = model.2;
186 }
187 }
188 let initial_display_delay_present = r.flag()?;
189 let operating_points_cnt = r.f(5)? + 1;
190 for _ in 0..operating_points_cnt {
191 let idc = r.f(12)? as u16;
192 let seq_level_idx = r.f(5)? as u8;
193 let seq_tier = if seq_level_idx > 7 { r.f(1)? as u8 } else { 0 };
194 if decoder_model_info_present {
195 let present_for_op = r.flag()?;
196 if present_for_op {
197 r.f(buffer_delay_length)?;
199 r.f(buffer_delay_length)?;
200 r.f(1)?;
201 }
202 }
203 if initial_display_delay_present {
204 let present_for_op = r.flag()?;
205 if present_for_op {
206 r.f(4)?;
207 }
208 }
209 operating_points.push(OperatingPoint {
210 idc,
211 seq_level_idx,
212 seq_tier,
213 });
214 }
215 }
216
217 let operating_point_idc = operating_points.first().map_or(0, |op| op.idc);
219
220 let frame_width_bits = r.f(4)? + 1;
221 let frame_height_bits = r.f(4)? + 1;
222 let max_frame_width = r.f(frame_width_bits)? + 1;
223 let max_frame_height = r.f(frame_height_bits)? + 1;
224
225 let frame_id_numbers_present = if reduced_still_picture_header {
226 false
227 } else {
228 r.flag()?
229 };
230 let mut delta_frame_id_length = 0;
231 let mut additional_frame_id_length = 0;
232 if frame_id_numbers_present {
233 delta_frame_id_length = r.f(4)? + 2;
234 additional_frame_id_length = r.f(3)? + 1;
235 }
236
237 let use_128x128_superblock = r.flag()?;
238 let enable_filter_intra = r.flag()?;
239 let enable_intra_edge_filter = r.flag()?;
240
241 let mut enable_interintra_compound = false;
242 let mut enable_masked_compound = false;
243 let mut enable_warped_motion = false;
244 let mut enable_dual_filter = false;
245 let mut enable_order_hint = false;
246 let mut enable_jnt_comp = false;
247 let mut enable_ref_frame_mvs = false;
248 let mut seq_force_screen_content_tools = SELECT;
249 let mut seq_force_integer_mv = SELECT;
250 let mut order_hint_bits = 0;
251
252 if !reduced_still_picture_header {
253 enable_interintra_compound = r.flag()?;
254 enable_masked_compound = r.flag()?;
255 enable_warped_motion = r.flag()?;
256 enable_dual_filter = r.flag()?;
257 enable_order_hint = r.flag()?;
258 if enable_order_hint {
259 enable_jnt_comp = r.flag()?;
260 enable_ref_frame_mvs = r.flag()?;
261 }
262 let seq_choose_screen_content_tools = r.flag()?;
263 seq_force_screen_content_tools = if seq_choose_screen_content_tools {
264 SELECT
265 } else {
266 r.f(1)? as u8
267 };
268 if seq_force_screen_content_tools > 0 {
269 let seq_choose_integer_mv = r.flag()?;
270 seq_force_integer_mv = if seq_choose_integer_mv {
271 SELECT
272 } else {
273 r.f(1)? as u8
274 };
275 } else {
276 seq_force_integer_mv = SELECT;
277 }
278 if enable_order_hint {
279 order_hint_bits = r.f(3)? + 1;
280 }
281 }
282
283 let enable_superres = r.flag()?;
284 let enable_cdef = r.flag()?;
285 let enable_restoration = r.flag()?;
286 let color = parse_color_config(r, seq_profile)?;
287 let film_grain_params_present = r.flag()?;
288
289 Ok(Self {
290 seq_profile,
291 still_picture,
292 reduced_still_picture_header,
293 operating_points,
294 operating_point_idc,
295 frame_width_bits,
296 frame_height_bits,
297 max_frame_width,
298 max_frame_height,
299 frame_id_numbers_present,
300 delta_frame_id_length,
301 additional_frame_id_length,
302 use_128x128_superblock,
303 enable_filter_intra,
304 enable_intra_edge_filter,
305 enable_interintra_compound,
306 enable_masked_compound,
307 enable_warped_motion,
308 enable_dual_filter,
309 enable_order_hint,
310 enable_jnt_comp,
311 enable_ref_frame_mvs,
312 seq_force_screen_content_tools,
313 seq_force_integer_mv,
314 order_hint_bits,
315 enable_superres,
316 enable_cdef,
317 enable_restoration,
318 color,
319 film_grain_params_present,
320 decoder_model_info_present,
321 buffer_delay_length,
322 buffer_removal_time_length,
323 frame_presentation_time_length,
324 equal_picture_interval,
325 timing_info_present,
326 })
327 }
328}
329
330fn parse_timing_info(r: &mut BitReader<'_>) -> Result<bool> {
332 let _num_units_in_display_tick = r.f(32)?;
333 let _time_scale = r.f(32)?;
334 let equal_picture_interval = r.flag()?;
335 if equal_picture_interval {
336 let _num_ticks_per_picture_minus_1 = r.uvlc()?;
337 }
338 Ok(equal_picture_interval)
339}
340
341fn parse_decoder_model_info(r: &mut BitReader<'_>) -> Result<(u32, u32, u32)> {
344 let buffer_delay_length = r.f(5)? + 1;
345 let _num_units_in_decoding_tick = r.f(32)?;
346 let buffer_removal_time_length = r.f(5)? + 1;
347 let frame_presentation_time_length = r.f(5)? + 1;
348 Ok((
349 buffer_delay_length,
350 buffer_removal_time_length,
351 frame_presentation_time_length,
352 ))
353}
354
355fn parse_color_config(r: &mut BitReader<'_>, seq_profile: u8) -> Result<ColorConfig> {
357 let high_bitdepth = r.flag()?;
358 let bit_depth = if seq_profile == 2 && high_bitdepth {
359 if r.flag()? { 12 } else { 10 }
360 } else if high_bitdepth {
361 10
362 } else {
363 8
364 };
365
366 let mono_chrome = if seq_profile == 1 { false } else { r.flag()? };
367 let num_planes = if mono_chrome { 1 } else { 3 };
368
369 let color_description_present = r.flag()?;
370 let (color_primaries, transfer_characteristics, matrix_coefficients) =
371 if color_description_present {
372 (r.f(8)? as u8, r.f(8)? as u8, r.f(8)? as u8)
373 } else {
374 (CP_UNSPECIFIED, TC_UNSPECIFIED, MC_UNSPECIFIED)
375 };
376
377 if mono_chrome {
378 let color_range = r.flag()?;
379 return Ok(ColorConfig {
380 bit_depth,
381 mono_chrome,
382 num_planes,
383 color_primaries,
384 transfer_characteristics,
385 matrix_coefficients,
386 color_range,
387 subsampling_x: 1,
388 subsampling_y: 1,
389 chroma_sample_position: 0,
390 separate_uv_delta_q: false,
391 });
392 }
393
394 let (color_range, subsampling_x, subsampling_y);
395 if color_primaries == CP_BT_709
396 && transfer_characteristics == TC_SRGB
397 && matrix_coefficients == MC_IDENTITY
398 {
399 color_range = true;
401 subsampling_x = 0;
402 subsampling_y = 0;
403 } else {
404 color_range = r.flag()?;
405 match seq_profile {
406 0 => {
407 subsampling_x = 1;
408 subsampling_y = 1;
409 }
410 1 => {
411 subsampling_x = 0;
412 subsampling_y = 0;
413 }
414 _ => {
415 if bit_depth == 12 {
416 subsampling_x = r.f(1)? as u8;
417 subsampling_y = if subsampling_x != 0 { r.f(1)? as u8 } else { 0 };
418 } else {
419 subsampling_x = 1;
420 subsampling_y = 0;
421 }
422 }
423 }
424 }
425
426 let chroma_sample_position = if subsampling_x == 1 && subsampling_y == 1 {
427 r.f(2)? as u8
428 } else {
429 0
430 };
431 let separate_uv_delta_q = r.flag()?;
432
433 Ok(ColorConfig {
434 bit_depth,
435 mono_chrome,
436 num_planes,
437 color_primaries,
438 transfer_characteristics,
439 matrix_coefficients,
440 color_range,
441 subsampling_x,
442 subsampling_y,
443 chroma_sample_position,
444 separate_uv_delta_q,
445 })
446}
447
448#[cfg(test)]
449#[allow(
450 clippy::unwrap_used,
451 clippy::indexing_slicing,
452 clippy::panic,
453 reason = "tests operate on known-good values and assert shapes directly"
454)]
455mod tests {
456 use super::*;
457
458 struct SeqBuilder {
461 bits: Vec<u8>,
462 }
463 impl SeqBuilder {
464 fn new() -> Self {
465 Self { bits: Vec::new() }
466 }
467 fn put(&mut self, value: u32, n: u32) -> &mut Self {
468 for i in (0..n).rev() {
469 self.bits.push(((value >> i) & 1) as u8);
470 }
471 self
472 }
473 fn pack(&self) -> Vec<u8> {
474 let mut out = vec![0_u8; self.bits.len().div_ceil(8)];
475 for (i, &bit) in self.bits.iter().enumerate() {
476 if bit != 0 {
477 out[i / 8] |= 1 << (7 - (i % 8));
478 }
479 }
480 out
481 }
482 }
483
484 fn reduced_still(width: u32, height: u32) -> Vec<u8> {
487 let mut b = SeqBuilder::new();
488 b.put(0, 3); b.put(1, 1); b.put(1, 1); b.put(1, 5); b.put(15, 4); b.put(15, 4); b.put(width - 1, 16); b.put(height - 1, 16); b.put(0, 1); b.put(0, 1); b.put(0, 1); b.put(0, 1); b.put(0, 1); b.put(0, 1); b.put(0, 1); b.put(0, 1); b.put(0, 1); b.put(0, 1); b.put(0, 2); b.put(0, 1); b.put(0, 1); b.pack()
513 }
514
515 #[test]
516 fn parses_a_reduced_still_picture_header() {
517 let bytes = reduced_still(320, 240);
518 let mut r = BitReader::new(&bytes);
519 let seq = SequenceHeader::parse(&mut r).unwrap();
520 assert_eq!(seq.seq_profile, 0);
521 assert!(seq.still_picture);
522 assert!(seq.reduced_still_picture_header);
523 assert_eq!(seq.max_frame_width, 320);
524 assert_eq!(seq.max_frame_height, 240);
525 assert_eq!(seq.color.bit_depth, 8);
526 assert!(!seq.color.mono_chrome);
527 assert_eq!(seq.color.subsampling_x, 1);
528 assert_eq!(seq.color.subsampling_y, 1);
529 assert_eq!(seq.operating_points.len(), 1);
530 assert_eq!(seq.seq_force_screen_content_tools, SELECT);
532 assert_eq!(seq.seq_force_integer_mv, SELECT);
533 assert_eq!(seq.order_hint_bits, 0);
534 assert!(!seq.enable_order_hint);
535 }
536
537 #[test]
538 fn monochrome_forces_a_single_plane_and_subsampling() {
539 let mut b = SeqBuilder::new();
540 b.put(0, 3).put(1, 1).put(1, 1).put(1, 5);
541 b.put(7, 4).put(7, 4); b.put(99, 8).put(49, 8); b.put(0, 1).put(0, 1).put(0, 1); b.put(0, 1).put(0, 1).put(0, 1); b.put(0, 1); b.put(1, 1); b.put(0, 1); b.put(0, 1); b.put(0, 1); let bytes = b.pack();
551 let mut r = BitReader::new(&bytes);
552 let seq = SequenceHeader::parse(&mut r).unwrap();
553 assert!(seq.color.mono_chrome);
554 assert_eq!(seq.color.num_planes, 1);
555 assert_eq!(seq.color.subsampling_x, 1);
556 assert_eq!(seq.color.subsampling_y, 1);
557 assert_eq!(seq.max_frame_width, 100);
558 assert_eq!(seq.max_frame_height, 50);
559 }
560
561 #[test]
562 fn the_srgb_identity_path_is_full_range_444() {
563 let mut b = SeqBuilder::new();
564 b.put(1, 3); b.put(1, 1).put(1, 1).put(1, 5);
566 b.put(7, 4).put(7, 4).put(63, 8).put(63, 8); b.put(0, 1).put(0, 1).put(0, 1);
568 b.put(0, 1).put(0, 1).put(0, 1);
569 b.put(0, 1); b.put(1, 1); b.put(CP_BT_709 as u32, 8);
573 b.put(TC_SRGB as u32, 8);
574 b.put(MC_IDENTITY as u32, 8);
575 b.put(0, 1); b.put(0, 1); let bytes = b.pack();
580 let mut r = BitReader::new(&bytes);
581 let seq = SequenceHeader::parse(&mut r).unwrap();
582 assert_eq!(seq.color.subsampling_x, 0);
583 assert_eq!(seq.color.subsampling_y, 0);
584 assert!(seq.color.color_range);
585 assert_eq!(seq.color.matrix_coefficients, MC_IDENTITY);
586 }
587
588 #[test]
589 fn a_profile_above_two_is_rejected() {
590 let bytes = [0xE0]; let mut r = BitReader::new(&bytes);
592 assert!(SequenceHeader::parse(&mut r).is_err());
593 }
594}