1#![forbid(unsafe_code)]
29#![allow(dead_code)]
30#![allow(clippy::doc_markdown)]
31#![allow(clippy::unused_self)]
32#![allow(clippy::cast_possible_truncation)]
33#![allow(clippy::trivially_copy_pass_by_ref)]
34#![allow(clippy::match_same_arms)]
35#![allow(clippy::struct_excessive_bools)]
36#![allow(clippy::struct_field_names)]
37#![allow(clippy::manual_div_ceil)]
38#![allow(clippy::cast_sign_loss)]
39#![allow(clippy::missing_errors_doc)]
40#![allow(clippy::unnecessary_cast)]
41#![allow(clippy::identity_op)]
42#![allow(clippy::if_not_else)]
43
44use super::cdef::CdefParams;
45use super::loop_filter::LoopFilterParams;
46use super::quantization::QuantizationParams;
47use super::sequence::SequenceHeader;
48use super::tile::TileInfo;
49use crate::error::{CodecError, CodecResult};
50use oximedia_io::BitReader;
51
52pub const NUM_REF_FRAMES: usize = 8;
58
59pub const REFS_PER_FRAME: usize = 7;
61
62pub const MAX_SEGMENTS: usize = 8;
64
65pub const SEG_LVL_MAX: usize = 8;
67
68pub const PRIMARY_REF_NONE: u8 = 7;
70
71pub const SUPERRES_DENOM_BITS: u8 = 3;
73
74pub const SUPERRES_DENOM_MIN: u32 = 9;
76
77pub const SUPERRES_NUM: u32 = 8;
79
80pub const LAST_FRAME: usize = 1;
82pub const LAST2_FRAME: usize = 2;
83pub const LAST3_FRAME: usize = 3;
84pub const GOLDEN_FRAME: usize = 4;
85pub const BWDREF_FRAME: usize = 5;
86pub const ALTREF2_FRAME: usize = 6;
87pub const ALTREF_FRAME: usize = 7;
88
89pub const INTERP_FILTER_EIGHTTAP: u8 = 0;
91pub const INTERP_FILTER_EIGHTTAP_SMOOTH: u8 = 1;
92pub const INTERP_FILTER_EIGHTTAP_SHARP: u8 = 2;
93pub const INTERP_FILTER_BILINEAR: u8 = 3;
94pub const INTERP_FILTER_SWITCHABLE: u8 = 4;
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
102#[repr(u8)]
103pub enum FrameType {
104 #[default]
106 KeyFrame = 0,
107 InterFrame = 1,
109 IntraOnlyFrame = 2,
111 SwitchFrame = 3,
113}
114
115impl FrameType {
116 #[must_use]
118 pub const fn is_intra(self) -> bool {
119 matches!(self, Self::KeyFrame | Self::IntraOnlyFrame)
120 }
121
122 #[must_use]
124 pub const fn is_key(self) -> bool {
125 matches!(self, Self::KeyFrame)
126 }
127
128 #[must_use]
130 pub const fn is_inter(self) -> bool {
131 matches!(self, Self::InterFrame | Self::SwitchFrame)
132 }
133}
134
135impl From<u8> for FrameType {
136 fn from(value: u8) -> Self {
137 match value {
138 0 => Self::KeyFrame,
139 1 => Self::InterFrame,
140 2 => Self::IntraOnlyFrame,
141 3 => Self::SwitchFrame,
142 _ => Self::KeyFrame, }
144 }
145}
146
147impl From<FrameType> for u8 {
148 fn from(ft: FrameType) -> Self {
149 ft as u8
150 }
151}
152
153#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
155#[repr(u8)]
156pub enum InterpolationFilter {
157 #[default]
159 Eighttap = 0,
160 EighttapSmooth = 1,
162 EighttapSharp = 2,
164 Bilinear = 3,
166 Switchable = 4,
168}
169
170impl From<u8> for InterpolationFilter {
171 fn from(value: u8) -> Self {
172 match value {
173 0 => Self::Eighttap,
174 1 => Self::EighttapSmooth,
175 2 => Self::EighttapSharp,
176 3 => Self::Bilinear,
177 _ => Self::Switchable,
178 }
179 }
180}
181
182impl From<InterpolationFilter> for u8 {
183 fn from(f: InterpolationFilter) -> Self {
184 f as u8
185 }
186}
187
188#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
190#[repr(u8)]
191pub enum MotionMode {
192 #[default]
194 Simple = 0,
195 ObstructedMotion = 1,
197 LocalWarp = 2,
199}
200
201impl From<u8> for MotionMode {
202 fn from(value: u8) -> Self {
203 match value {
204 0 => Self::Simple,
205 1 => Self::ObstructedMotion,
206 2 => Self::LocalWarp,
207 _ => Self::Simple,
208 }
209 }
210}
211
212#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
214#[repr(u8)]
215pub enum ReferenceMode {
216 #[default]
218 SingleReference = 0,
219 CompoundReference = 1,
221 ReferenceModeSelect = 2,
223}
224
225impl From<u8> for ReferenceMode {
226 fn from(value: u8) -> Self {
227 match value {
228 0 => Self::SingleReference,
229 1 => Self::CompoundReference,
230 _ => Self::ReferenceModeSelect,
231 }
232 }
233}
234
235#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
237#[repr(u8)]
238pub enum TxMode {
239 Only4x4 = 0,
241 #[default]
243 Largest = 1,
244 Select = 2,
246}
247
248impl From<u8> for TxMode {
249 fn from(value: u8) -> Self {
250 match value {
251 0 => Self::Only4x4,
252 1 => Self::Largest,
253 _ => Self::Select,
254 }
255 }
256}
257
258#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
260#[repr(u8)]
261pub enum RestorationType {
262 #[default]
264 None = 0,
265 Wiener = 1,
267 SgrProj = 2,
269 Switchable = 3,
271}
272
273impl From<u8> for RestorationType {
274 fn from(value: u8) -> Self {
275 match value {
276 0 => Self::None,
277 1 => Self::Wiener,
278 2 => Self::SgrProj,
279 _ => Self::Switchable,
280 }
281 }
282}
283
284#[derive(Clone, Debug, Default)]
290pub struct FrameSize {
291 pub frame_width: u32,
293 pub frame_height: u32,
295 pub upscaled_width: u32,
297 pub superres_denom: u32,
299 pub use_superres: bool,
301 pub mi_cols: u32,
303 pub mi_rows: u32,
305}
306
307impl FrameSize {
308 #[must_use]
310 pub const fn size_to_mi(size: u32) -> u32 {
311 (size + 3) >> 2
312 }
313
314 #[must_use]
316 pub const fn size_to_sb(size: u32, sb_size: u32) -> u32 {
317 (size + sb_size - 1) / sb_size
318 }
319
320 #[must_use]
322 pub fn sb_cols(&self, sb_size: u32) -> u32 {
323 Self::size_to_sb(self.upscaled_width, sb_size)
324 }
325
326 #[must_use]
328 pub fn sb_rows(&self, sb_size: u32) -> u32 {
329 Self::size_to_sb(self.frame_height, sb_size)
330 }
331}
332
333#[derive(Clone, Debug, Default)]
335pub struct RenderSize {
336 pub render_width: u32,
338 pub render_height: u32,
340 pub render_and_frame_size_different: bool,
342}
343
344#[derive(Clone, Debug, Default)]
346pub struct RefFrameInfo {
347 pub ref_frame_idx: [u8; REFS_PER_FRAME],
350 pub ref_order_hint: [u8; NUM_REF_FRAMES],
352 pub ref_frame_sign_bias: [bool; NUM_REF_FRAMES],
354}
355
356#[derive(Clone, Debug, Default)]
358pub struct SegmentationParams {
359 pub enabled: bool,
361 pub update_map: bool,
363 pub temporal_update: bool,
365 pub update_data: bool,
367 pub feature_enabled: [[bool; SEG_LVL_MAX]; MAX_SEGMENTS],
369 pub feature_data: [[i16; SEG_LVL_MAX]; MAX_SEGMENTS],
371 pub last_active_seg_id: u8,
373}
374
375impl SegmentationParams {
376 pub const SEG_LVL_ALT_Q: usize = 0;
378 pub const SEG_LVL_ALT_LF_Y_V: usize = 1;
380 pub const SEG_LVL_ALT_LF_Y_H: usize = 2;
382 pub const SEG_LVL_ALT_LF_U: usize = 3;
384 pub const SEG_LVL_ALT_LF_V: usize = 4;
386 pub const SEG_LVL_REF_FRAME: usize = 5;
388 pub const SEG_LVL_SKIP: usize = 6;
390 pub const SEG_LVL_GLOBALMV: usize = 7;
392
393 pub const SEG_FEATURE_DATA_MAX: [i16; SEG_LVL_MAX] = [255, 63, 63, 63, 63, 7, 0, 0];
395
396 pub const SEG_FEATURE_DATA_SIGNED: [bool; SEG_LVL_MAX] =
398 [true, true, true, true, true, false, false, false];
399
400 pub const SEG_FEATURE_BITS: [u8; SEG_LVL_MAX] = [8, 6, 6, 6, 6, 3, 0, 0];
402
403 #[must_use]
405 pub fn get_feature(&self, segment_id: usize, feature: usize) -> i16 {
406 if segment_id < MAX_SEGMENTS
407 && feature < SEG_LVL_MAX
408 && self.feature_enabled[segment_id][feature]
409 {
410 self.feature_data[segment_id][feature]
411 } else {
412 0
413 }
414 }
415
416 #[must_use]
418 pub fn is_feature_enabled(&self, segment_id: usize, feature: usize) -> bool {
419 segment_id < MAX_SEGMENTS
420 && feature < SEG_LVL_MAX
421 && self.feature_enabled[segment_id][feature]
422 }
423}
424
425#[derive(Clone, Debug, Default)]
427pub struct LoopRestorationParams {
428 pub frame_restoration_type: [RestorationType; 3],
430 pub loop_restoration_size: [u8; 3],
432 pub uses_lr: bool,
434}
435
436#[derive(Clone, Debug, Default)]
438pub struct GlobalMotionParams {
439 pub gm_type: u8,
441 pub gm_params: [i32; 6],
443}
444
445#[derive(Clone, Debug)]
447pub struct GlobalMotion {
448 pub params: [GlobalMotionParams; NUM_REF_FRAMES],
450}
451
452impl Default for GlobalMotion {
453 fn default() -> Self {
454 Self {
455 params: std::array::from_fn(|_| GlobalMotionParams::default()),
456 }
457 }
458}
459
460#[derive(Clone, Debug, Default)]
462pub struct FilmGrainParams {
463 pub apply_grain: bool,
465 pub grain_seed: u16,
467 pub update_grain: bool,
469 pub num_y_points: u8,
471 pub point_y_value: [u8; 14],
473 pub point_y_scaling: [u8; 14],
475 pub chroma_scaling_from_luma: bool,
477 pub num_cb_points: u8,
479 pub point_cb_value: [u8; 10],
481 pub point_cb_scaling: [u8; 10],
483 pub num_cr_points: u8,
485 pub point_cr_value: [u8; 10],
487 pub point_cr_scaling: [u8; 10],
489 pub grain_scaling_minus_8: u8,
491 pub ar_coeff_lag: u8,
493 pub ar_coeffs_y_plus_128: [u8; 24],
495 pub ar_coeffs_cb_plus_128: [u8; 25],
497 pub ar_coeffs_cr_plus_128: [u8; 25],
499 pub ar_coeff_shift_minus_6: u8,
501 pub grain_scale_shift: u8,
503 pub cb_mult: u8,
505 pub cb_luma_mult: u8,
507 pub cb_offset: u16,
509 pub cr_mult: u8,
511 pub cr_luma_mult: u8,
513 pub cr_offset: u16,
515 pub overlap_flag: bool,
517 pub clip_to_restricted_range: bool,
519}
520
521#[derive(Clone, Debug, Default)]
523#[allow(clippy::struct_excessive_bools)]
524pub struct FrameHeader {
525 pub frame_type: FrameType,
528 pub show_frame: bool,
530 pub showable_frame: bool,
532 pub show_existing_frame: bool,
534 pub frame_to_show_map_idx: u8,
536 pub error_resilient_mode: bool,
538
539 pub current_frame_id: u32,
542 pub order_hint: u8,
544 pub primary_ref_frame: u8,
546 pub refresh_frame_flags: u8,
548
549 pub frame_size: FrameSize,
552 pub render_size: RenderSize,
554
555 pub allow_high_precision_mv: bool,
558 pub interpolation_filter: InterpolationFilter,
560 pub is_filter_switchable: bool,
562 pub allow_intrabc: bool,
564
565 pub ref_frame_info: RefFrameInfo,
568 pub allow_screen_content_tools: bool,
570 pub force_integer_mv: bool,
572
573 pub is_motion_mode_switchable: bool,
576 pub use_ref_frame_mvs: bool,
578 pub reference_mode: ReferenceMode,
580 pub skip_mode_frame: [u8; 2],
582 pub skip_mode_allowed: bool,
584 pub skip_mode_present: bool,
586
587 pub compound_reference_allowed: bool,
590
591 pub tx_mode: TxMode,
594 pub reduced_tx_set: bool,
596
597 pub allow_warped_motion: bool,
600
601 pub quantization: QuantizationParams,
604
605 pub segmentation: SegmentationParams,
608
609 pub loop_filter: LoopFilterParams,
612
613 pub cdef: CdefParams,
616
617 pub loop_restoration: LoopRestorationParams,
620
621 pub tile_info: TileInfo,
624
625 pub global_motion: GlobalMotion,
628
629 pub film_grain: FilmGrainParams,
632
633 pub frame_is_intra: bool,
636 pub lossless_array: [bool; MAX_SEGMENTS],
638 pub coded_lossless: bool,
640 pub all_lossless: bool,
642}
643
644impl FrameHeader {
645 #[must_use]
647 pub fn new() -> Self {
648 Self::default()
649 }
650
651 #[allow(clippy::too_many_lines, clippy::cast_possible_truncation)]
657 pub fn parse(data: &[u8], seq: &SequenceHeader) -> CodecResult<Self> {
658 let mut reader = BitReader::new(data);
659 let mut header = Self::new();
660
661 header.parse_uncompressed_header(&mut reader, seq)?;
663
664 Ok(header)
665 }
666
667 #[allow(clippy::too_many_lines, clippy::cast_possible_truncation)]
669 fn parse_uncompressed_header(
670 &mut self,
671 reader: &mut BitReader<'_>,
672 seq: &SequenceHeader,
673 ) -> CodecResult<()> {
674 let frame_id_length = if seq.reduced_still_picture_header {
676 0
677 } else {
678 15
680 };
681
682 if seq.reduced_still_picture_header {
684 self.show_existing_frame = false;
685 self.frame_type = FrameType::KeyFrame;
686 self.show_frame = true;
687 self.showable_frame = false;
688 } else {
689 self.show_existing_frame = reader.read_bit().map_err(CodecError::Core)? != 0;
690
691 if self.show_existing_frame {
692 self.frame_to_show_map_idx = reader.read_bits(3).map_err(CodecError::Core)? as u8;
693
694 if frame_id_length > 0 {
695 self.current_frame_id = reader
696 .read_bits(frame_id_length)
697 .map_err(CodecError::Core)?
698 as u32;
699 }
700
701 return Ok(());
703 }
704
705 self.frame_type = FrameType::from(reader.read_bits(2).map_err(CodecError::Core)? as u8);
706 self.show_frame = reader.read_bit().map_err(CodecError::Core)? != 0;
707
708 if self.show_frame && !seq.reduced_still_picture_header {
709 }
711
712 self.showable_frame = if self.show_frame {
713 !self.frame_type.is_key()
714 } else {
715 reader.read_bit().map_err(CodecError::Core)? != 0
716 };
717
718 self.error_resilient_mode =
719 if self.frame_type == FrameType::SwitchFrame || self.frame_type.is_key() {
720 true
721 } else {
722 reader.read_bit().map_err(CodecError::Core)? != 0
723 };
724 }
725
726 self.frame_is_intra = self.frame_type.is_intra();
727
728 if !seq.reduced_still_picture_header {
730 let _disable_cdf_update = reader.read_bit().map_err(CodecError::Core)?;
731 }
732
733 self.allow_screen_content_tools = if seq.reduced_still_picture_header {
735 false
736 } else {
737 reader.read_bit().map_err(CodecError::Core)? != 0
739 };
740
741 self.force_integer_mv = if self.allow_screen_content_tools {
743 if !seq.reduced_still_picture_header {
744 reader.read_bit().map_err(CodecError::Core)? != 0
745 } else {
746 false
747 }
748 } else {
749 false
750 };
751
752 if frame_id_length > 0 {
754 self.current_frame_id = reader
755 .read_bits(frame_id_length)
756 .map_err(CodecError::Core)? as u32;
757 }
758
759 self.parse_frame_size(reader, seq)?;
761
762 self.parse_render_size(reader)?;
764
765 if seq.enable_superres && !self.frame_size.use_superres {
767 self.frame_size.use_superres = reader.read_bit().map_err(CodecError::Core)? != 0;
768 if self.frame_size.use_superres {
769 let coded_denom = reader
770 .read_bits(SUPERRES_DENOM_BITS)
771 .map_err(CodecError::Core)? as u32
772 + SUPERRES_DENOM_MIN;
773 self.frame_size.superres_denom = coded_denom;
774 self.frame_size.upscaled_width = self.frame_size.frame_width;
775 self.frame_size.frame_width =
776 (self.frame_size.upscaled_width * SUPERRES_NUM + coded_denom / 2) / coded_denom;
777 }
778 }
779
780 if !self.frame_is_intra {
781 self.parse_inter_frame_params(reader, seq)?;
783 }
784
785 self.quantization = QuantizationParams::parse(reader, seq)?;
787
788 self.parse_segmentation(reader)?;
790
791 self.compute_lossless(seq);
793
794 if !self.coded_lossless {
796 self.loop_filter = LoopFilterParams::parse(reader, seq, self.frame_is_intra)?;
797 }
798
799 if seq.enable_cdef && !self.coded_lossless && !self.allow_intrabc {
801 self.cdef = CdefParams::parse(reader, seq)?;
802 }
803
804 if seq.enable_restoration && !self.all_lossless && !self.allow_intrabc {
806 self.parse_loop_restoration(reader, seq)?;
807 }
808
809 self.parse_tx_mode(reader)?;
811
812 if !self.frame_is_intra {
814 self.reference_mode = if reader.read_bit().map_err(CodecError::Core)? != 0 {
815 ReferenceMode::ReferenceModeSelect
816 } else {
817 ReferenceMode::SingleReference
818 };
819 }
820
821 self.parse_skip_mode(reader, seq)?;
823
824 if !self.frame_is_intra && !self.error_resilient_mode {
826 self.allow_warped_motion = reader.read_bit().map_err(CodecError::Core)? != 0;
827 }
828
829 self.reduced_tx_set = reader.read_bit().map_err(CodecError::Core)? != 0;
831
832 if !self.frame_is_intra {
834 self.parse_global_motion(reader)?;
835 }
836
837 if seq.film_grain_params_present && (self.show_frame || self.showable_frame) {
839 self.parse_film_grain(reader, seq)?;
840 }
841
842 self.tile_info = TileInfo::parse(reader, seq, &self.frame_size)?;
844
845 Ok(())
846 }
847
848 #[allow(clippy::cast_possible_truncation)]
850 fn parse_frame_size(
851 &mut self,
852 reader: &mut BitReader<'_>,
853 seq: &SequenceHeader,
854 ) -> CodecResult<()> {
855 if self.frame_type == FrameType::SwitchFrame {
856 self.frame_size.frame_width = seq.max_frame_width();
858 self.frame_size.frame_height = seq.max_frame_height();
859 } else {
860 let frame_size_override = if seq.reduced_still_picture_header {
861 false
862 } else {
863 reader.read_bit().map_err(CodecError::Core)? != 0
864 };
865
866 if frame_size_override {
867 let frame_width_bits = 16; let frame_height_bits = 16;
870 self.frame_size.frame_width = reader
871 .read_bits(frame_width_bits)
872 .map_err(CodecError::Core)?
873 as u32
874 + 1;
875 self.frame_size.frame_height = reader
876 .read_bits(frame_height_bits)
877 .map_err(CodecError::Core)?
878 as u32
879 + 1;
880 } else {
881 self.frame_size.frame_width = seq.max_frame_width();
882 self.frame_size.frame_height = seq.max_frame_height();
883 }
884 }
885
886 self.frame_size.upscaled_width = self.frame_size.frame_width;
887 self.frame_size.superres_denom = SUPERRES_NUM;
888 self.frame_size.mi_cols = FrameSize::size_to_mi(self.frame_size.upscaled_width);
889 self.frame_size.mi_rows = FrameSize::size_to_mi(self.frame_size.frame_height);
890
891 Ok(())
892 }
893
894 fn parse_render_size(&mut self, reader: &mut BitReader<'_>) -> CodecResult<()> {
896 self.render_size.render_and_frame_size_different =
897 reader.read_bit().map_err(CodecError::Core)? != 0;
898
899 if self.render_size.render_and_frame_size_different {
900 let render_width_minus_1 = reader.read_bits(16).map_err(CodecError::Core)? as u32;
901 let render_height_minus_1 = reader.read_bits(16).map_err(CodecError::Core)? as u32;
902 self.render_size.render_width = render_width_minus_1 + 1;
903 self.render_size.render_height = render_height_minus_1 + 1;
904 } else {
905 self.render_size.render_width = self.frame_size.upscaled_width;
906 self.render_size.render_height = self.frame_size.frame_height;
907 }
908
909 Ok(())
910 }
911
912 #[allow(clippy::cast_possible_truncation)]
914 fn parse_inter_frame_params(
915 &mut self,
916 reader: &mut BitReader<'_>,
917 seq: &SequenceHeader,
918 ) -> CodecResult<()> {
919 for i in 0..REFS_PER_FRAME {
921 self.ref_frame_info.ref_frame_idx[i] =
922 reader.read_bits(3).map_err(CodecError::Core)? as u8;
923 }
924
925 if !self.error_resilient_mode && self.frame_type != FrameType::SwitchFrame {
927 }
929
930 self.allow_high_precision_mv = if self.force_integer_mv {
932 false
933 } else {
934 reader.read_bit().map_err(CodecError::Core)? != 0
935 };
936
937 self.is_filter_switchable = reader.read_bit().map_err(CodecError::Core)? != 0;
939 self.interpolation_filter = if self.is_filter_switchable {
940 InterpolationFilter::Switchable
941 } else {
942 InterpolationFilter::from(reader.read_bits(2).map_err(CodecError::Core)? as u8)
943 };
944
945 self.is_motion_mode_switchable = reader.read_bit().map_err(CodecError::Core)? != 0;
947
948 if !self.error_resilient_mode && seq.enable_order_hint {
950 self.use_ref_frame_mvs = reader.read_bit().map_err(CodecError::Core)? != 0;
951 }
952
953 Ok(())
954 }
955
956 #[allow(clippy::cast_possible_truncation)]
958 fn parse_segmentation(&mut self, reader: &mut BitReader<'_>) -> CodecResult<()> {
959 self.segmentation.enabled = reader.read_bit().map_err(CodecError::Core)? != 0;
960
961 if !self.segmentation.enabled {
962 return Ok(());
963 }
964
965 if self.primary_ref_frame == PRIMARY_REF_NONE {
966 self.segmentation.update_map = true;
967 self.segmentation.temporal_update = false;
968 self.segmentation.update_data = true;
969 } else {
970 self.segmentation.update_map = reader.read_bit().map_err(CodecError::Core)? != 0;
971 if self.segmentation.update_map {
972 self.segmentation.temporal_update =
973 reader.read_bit().map_err(CodecError::Core)? != 0;
974 }
975 self.segmentation.update_data = reader.read_bit().map_err(CodecError::Core)? != 0;
976 }
977
978 if self.segmentation.update_data {
979 for i in 0..MAX_SEGMENTS {
980 for j in 0..SEG_LVL_MAX {
981 self.segmentation.feature_enabled[i][j] =
982 reader.read_bit().map_err(CodecError::Core)? != 0;
983
984 if self.segmentation.feature_enabled[i][j] {
985 let bits = SegmentationParams::SEG_FEATURE_BITS[j];
986 let max = SegmentationParams::SEG_FEATURE_DATA_MAX[j];
987
988 if SegmentationParams::SEG_FEATURE_DATA_SIGNED[j] {
989 let value =
990 reader.read_bits(bits + 1).map_err(CodecError::Core)? as i16;
991 let sign = if value & (1 << bits) != 0 { -1 } else { 1 };
992 let magnitude = value & ((1 << bits) - 1);
993 self.segmentation.feature_data[i][j] =
994 (sign * magnitude).clamp(-max, max);
995 } else {
996 self.segmentation.feature_data[i][j] =
997 (reader.read_bits(bits).map_err(CodecError::Core)? as i16).min(max);
998 }
999 }
1000 }
1001 }
1002 }
1003
1004 self.segmentation.last_active_seg_id = 0;
1006 for i in 0..MAX_SEGMENTS {
1007 for j in 0..SEG_LVL_MAX {
1008 if self.segmentation.feature_enabled[i][j] {
1009 self.segmentation.last_active_seg_id = i as u8;
1010 }
1011 }
1012 }
1013
1014 Ok(())
1015 }
1016
1017 fn compute_lossless(&mut self, seq: &SequenceHeader) {
1019 for seg_id in 0..MAX_SEGMENTS {
1020 let qindex = self.get_qindex(seg_id);
1021 let lossless = qindex == 0
1022 && self.quantization.delta_q_y_dc == 0
1023 && self.quantization.delta_q_u_ac == 0
1024 && self.quantization.delta_q_u_dc == 0
1025 && self.quantization.delta_q_v_ac == 0
1026 && self.quantization.delta_q_v_dc == 0;
1027 self.lossless_array[seg_id] = lossless;
1028 }
1029
1030 self.coded_lossless = self.lossless_array.iter().all(|&l| l);
1031 self.all_lossless =
1032 self.coded_lossless && self.frame_size.frame_width == self.frame_size.upscaled_width;
1033
1034 if self.coded_lossless {
1036 self.loop_filter.level[0] = 0;
1037 self.loop_filter.level[1] = 0;
1038 }
1039
1040 if seq.color_config.mono_chrome {
1042 self.quantization.delta_q_u_dc = 0;
1043 self.quantization.delta_q_u_ac = 0;
1044 self.quantization.delta_q_v_dc = 0;
1045 self.quantization.delta_q_v_ac = 0;
1046 }
1047 }
1048
1049 #[must_use]
1051 pub fn get_qindex(&self, seg_id: usize) -> u8 {
1052 let base_q = self.quantization.base_q_idx;
1053 if self.segmentation.enabled
1054 && self
1055 .segmentation
1056 .is_feature_enabled(seg_id, SegmentationParams::SEG_LVL_ALT_Q)
1057 {
1058 let delta = self
1059 .segmentation
1060 .get_feature(seg_id, SegmentationParams::SEG_LVL_ALT_Q);
1061 let q = i32::from(base_q) + i32::from(delta);
1062 q.clamp(0, 255) as u8
1063 } else {
1064 base_q
1065 }
1066 }
1067
1068 #[allow(clippy::cast_possible_truncation)]
1070 fn parse_loop_restoration(
1071 &mut self,
1072 reader: &mut BitReader<'_>,
1073 seq: &SequenceHeader,
1074 ) -> CodecResult<()> {
1075 let num_planes = if seq.color_config.mono_chrome { 1 } else { 3 };
1076 let mut uses_lr = false;
1077 let mut uses_chroma_lr = false;
1078
1079 for plane in 0..num_planes {
1080 let lr_type = reader.read_bits(2).map_err(CodecError::Core)? as u8;
1081 self.loop_restoration.frame_restoration_type[plane] = RestorationType::from(lr_type);
1082 if lr_type != 0 {
1083 uses_lr = true;
1084 if plane > 0 {
1085 uses_chroma_lr = true;
1086 }
1087 }
1088 }
1089
1090 self.loop_restoration.uses_lr = uses_lr;
1091
1092 if uses_lr {
1093 let lr_unit_shift = if seq.enable_superres {
1095 reader.read_bit().map_err(CodecError::Core)? as u8
1096 } else {
1097 1
1098 };
1099
1100 let lr_unit_extra_shift = if lr_unit_shift != 0 && !seq.enable_superres {
1101 reader.read_bit().map_err(CodecError::Core)? as u8
1102 } else {
1103 0
1104 };
1105
1106 let sb_size = 64; let lr_size_base = 6 + lr_unit_shift + lr_unit_extra_shift;
1109 self.loop_restoration.loop_restoration_size[0] = lr_size_base.min(sb_size);
1110
1111 if uses_chroma_lr && !seq.color_config.is_420() {
1112 let uv_shift = reader.read_bit().map_err(CodecError::Core)? as u8;
1113 self.loop_restoration.loop_restoration_size[1] = lr_size_base - uv_shift;
1114 self.loop_restoration.loop_restoration_size[2] = lr_size_base - uv_shift;
1115 } else {
1116 self.loop_restoration.loop_restoration_size[1] = lr_size_base;
1117 self.loop_restoration.loop_restoration_size[2] = lr_size_base;
1118 }
1119 }
1120
1121 Ok(())
1122 }
1123
1124 fn parse_tx_mode(&mut self, reader: &mut BitReader<'_>) -> CodecResult<()> {
1126 if self.coded_lossless {
1127 self.tx_mode = TxMode::Only4x4;
1128 } else {
1129 let tx_mode_select = reader.read_bit().map_err(CodecError::Core)? != 0;
1130 self.tx_mode = if tx_mode_select {
1131 TxMode::Select
1132 } else {
1133 TxMode::Largest
1134 };
1135 }
1136 Ok(())
1137 }
1138
1139 #[allow(clippy::cast_possible_truncation)]
1141 fn parse_skip_mode(
1142 &mut self,
1143 reader: &mut BitReader<'_>,
1144 seq: &SequenceHeader,
1145 ) -> CodecResult<()> {
1146 if self.frame_is_intra
1147 || !self.reference_mode.eq(&ReferenceMode::ReferenceModeSelect)
1148 || !seq.enable_order_hint
1149 {
1150 self.skip_mode_allowed = false;
1151 } else {
1152 self.skip_mode_allowed = true;
1154 }
1155
1156 if self.skip_mode_allowed {
1157 self.skip_mode_present = reader.read_bit().map_err(CodecError::Core)? != 0;
1158 } else {
1159 self.skip_mode_present = false;
1160 }
1161
1162 Ok(())
1163 }
1164
1165 #[allow(clippy::cast_possible_truncation)]
1167 fn parse_global_motion(&mut self, reader: &mut BitReader<'_>) -> CodecResult<()> {
1168 for ref_frame in LAST_FRAME..=ALTREF_FRAME {
1169 let is_global = reader.read_bit().map_err(CodecError::Core)? != 0;
1171 if is_global {
1172 let is_rot_zoom = reader.read_bit().map_err(CodecError::Core)? != 0;
1173 if is_rot_zoom {
1174 self.global_motion.params[ref_frame].gm_type = 2; } else {
1176 let is_translation = reader.read_bit().map_err(CodecError::Core)? != 0;
1177 self.global_motion.params[ref_frame].gm_type = if is_translation {
1178 1 } else {
1180 3 };
1182 }
1183
1184 } else {
1187 self.global_motion.params[ref_frame].gm_type = 0; }
1189 }
1190
1191 Ok(())
1192 }
1193
1194 #[allow(clippy::cast_possible_truncation)]
1196 fn parse_film_grain(
1197 &mut self,
1198 reader: &mut BitReader<'_>,
1199 seq: &SequenceHeader,
1200 ) -> CodecResult<()> {
1201 self.film_grain.apply_grain = reader.read_bit().map_err(CodecError::Core)? != 0;
1202
1203 if !self.film_grain.apply_grain {
1204 return Ok(());
1205 }
1206
1207 self.film_grain.grain_seed = reader.read_bits(16).map_err(CodecError::Core)? as u16;
1208
1209 if self.frame_type == FrameType::InterFrame {
1210 self.film_grain.update_grain = reader.read_bit().map_err(CodecError::Core)? != 0;
1211 } else {
1212 self.film_grain.update_grain = true;
1213 }
1214
1215 if !self.film_grain.update_grain {
1216 let _film_grain_params_ref_idx = reader.read_bits(3).map_err(CodecError::Core)?;
1218 return Ok(());
1219 }
1220
1221 self.film_grain.num_y_points = reader.read_bits(4).map_err(CodecError::Core)? as u8;
1223 for i in 0..self.film_grain.num_y_points as usize {
1224 self.film_grain.point_y_value[i] = reader.read_bits(8).map_err(CodecError::Core)? as u8;
1225 self.film_grain.point_y_scaling[i] =
1226 reader.read_bits(8).map_err(CodecError::Core)? as u8;
1227 }
1228
1229 self.film_grain.chroma_scaling_from_luma = if !seq.color_config.mono_chrome {
1231 reader.read_bit().map_err(CodecError::Core)? != 0
1232 } else {
1233 false
1234 };
1235
1236 if seq.color_config.mono_chrome
1238 || self.film_grain.chroma_scaling_from_luma
1239 || (seq.color_config.is_420() && self.film_grain.num_y_points == 0)
1240 {
1241 self.film_grain.num_cb_points = 0;
1242 self.film_grain.num_cr_points = 0;
1243 } else {
1244 self.film_grain.num_cb_points = reader.read_bits(4).map_err(CodecError::Core)? as u8;
1245 for i in 0..self.film_grain.num_cb_points as usize {
1246 self.film_grain.point_cb_value[i] =
1247 reader.read_bits(8).map_err(CodecError::Core)? as u8;
1248 self.film_grain.point_cb_scaling[i] =
1249 reader.read_bits(8).map_err(CodecError::Core)? as u8;
1250 }
1251
1252 self.film_grain.num_cr_points = reader.read_bits(4).map_err(CodecError::Core)? as u8;
1253 for i in 0..self.film_grain.num_cr_points as usize {
1254 self.film_grain.point_cr_value[i] =
1255 reader.read_bits(8).map_err(CodecError::Core)? as u8;
1256 self.film_grain.point_cr_scaling[i] =
1257 reader.read_bits(8).map_err(CodecError::Core)? as u8;
1258 }
1259 }
1260
1261 self.film_grain.grain_scaling_minus_8 =
1263 reader.read_bits(2).map_err(CodecError::Core)? as u8;
1264 self.film_grain.ar_coeff_lag = reader.read_bits(2).map_err(CodecError::Core)? as u8;
1265
1266 let num_pos_luma = 2 * self.film_grain.ar_coeff_lag * (self.film_grain.ar_coeff_lag + 1);
1268 for i in 0..num_pos_luma as usize {
1269 if self.film_grain.num_y_points > 0 && i < 24 {
1270 self.film_grain.ar_coeffs_y_plus_128[i] =
1271 reader.read_bits(8).map_err(CodecError::Core)? as u8;
1272 }
1273 }
1274
1275 self.film_grain.ar_coeff_shift_minus_6 =
1276 reader.read_bits(2).map_err(CodecError::Core)? as u8;
1277 self.film_grain.grain_scale_shift = reader.read_bits(2).map_err(CodecError::Core)? as u8;
1278
1279 if self.film_grain.num_cb_points > 0 {
1281 self.film_grain.cb_mult = reader.read_bits(8).map_err(CodecError::Core)? as u8;
1282 self.film_grain.cb_luma_mult = reader.read_bits(8).map_err(CodecError::Core)? as u8;
1283 self.film_grain.cb_offset = reader.read_bits(9).map_err(CodecError::Core)? as u16;
1284 }
1285
1286 if self.film_grain.num_cr_points > 0 {
1287 self.film_grain.cr_mult = reader.read_bits(8).map_err(CodecError::Core)? as u8;
1288 self.film_grain.cr_luma_mult = reader.read_bits(8).map_err(CodecError::Core)? as u8;
1289 self.film_grain.cr_offset = reader.read_bits(9).map_err(CodecError::Core)? as u16;
1290 }
1291
1292 self.film_grain.overlap_flag = reader.read_bit().map_err(CodecError::Core)? != 0;
1293 self.film_grain.clip_to_restricted_range =
1294 reader.read_bit().map_err(CodecError::Core)? != 0;
1295
1296 Ok(())
1297 }
1298
1299 #[must_use]
1301 pub const fn is_key_frame(&self) -> bool {
1302 matches!(self.frame_type, FrameType::KeyFrame)
1303 }
1304
1305 #[must_use]
1307 pub const fn is_inter_frame(&self) -> bool {
1308 matches!(
1309 self.frame_type,
1310 FrameType::InterFrame | FrameType::SwitchFrame
1311 )
1312 }
1313
1314 #[must_use]
1316 pub const fn display_order(&self) -> u8 {
1317 self.order_hint
1318 }
1319}
1320
1321#[cfg(test)]
1326mod tests {
1327 use super::*;
1328
1329 #[test]
1330 fn test_frame_type_conversions() {
1331 assert_eq!(FrameType::from(0), FrameType::KeyFrame);
1332 assert_eq!(FrameType::from(1), FrameType::InterFrame);
1333 assert_eq!(FrameType::from(2), FrameType::IntraOnlyFrame);
1334 assert_eq!(FrameType::from(3), FrameType::SwitchFrame);
1335 assert_eq!(FrameType::from(4), FrameType::KeyFrame);
1336
1337 assert_eq!(u8::from(FrameType::KeyFrame), 0);
1338 assert_eq!(u8::from(FrameType::InterFrame), 1);
1339 }
1340
1341 #[test]
1342 fn test_frame_type_properties() {
1343 assert!(FrameType::KeyFrame.is_intra());
1344 assert!(FrameType::KeyFrame.is_key());
1345 assert!(!FrameType::KeyFrame.is_inter());
1346
1347 assert!(!FrameType::InterFrame.is_intra());
1348 assert!(!FrameType::InterFrame.is_key());
1349 assert!(FrameType::InterFrame.is_inter());
1350
1351 assert!(FrameType::IntraOnlyFrame.is_intra());
1352 assert!(!FrameType::IntraOnlyFrame.is_key());
1353 assert!(!FrameType::IntraOnlyFrame.is_inter());
1354
1355 assert!(!FrameType::SwitchFrame.is_intra());
1356 assert!(!FrameType::SwitchFrame.is_key());
1357 assert!(FrameType::SwitchFrame.is_inter());
1358 }
1359
1360 #[test]
1361 fn test_interpolation_filter_conversions() {
1362 assert_eq!(InterpolationFilter::from(0), InterpolationFilter::Eighttap);
1363 assert_eq!(
1364 InterpolationFilter::from(1),
1365 InterpolationFilter::EighttapSmooth
1366 );
1367 assert_eq!(
1368 InterpolationFilter::from(2),
1369 InterpolationFilter::EighttapSharp
1370 );
1371 assert_eq!(InterpolationFilter::from(3), InterpolationFilter::Bilinear);
1372 assert_eq!(
1373 InterpolationFilter::from(4),
1374 InterpolationFilter::Switchable
1375 );
1376 }
1377
1378 #[test]
1379 fn test_frame_size_calculations() {
1380 assert_eq!(FrameSize::size_to_mi(1920), 480);
1381 assert_eq!(FrameSize::size_to_mi(1080), 270);
1382 assert_eq!(FrameSize::size_to_mi(1), 1);
1383 assert_eq!(FrameSize::size_to_mi(4), 1);
1384 assert_eq!(FrameSize::size_to_mi(5), 2);
1385
1386 assert_eq!(FrameSize::size_to_sb(1920, 64), 30);
1387 assert_eq!(FrameSize::size_to_sb(1920, 128), 15);
1388 assert_eq!(FrameSize::size_to_sb(1080, 64), 17);
1389 }
1390
1391 #[test]
1392 fn test_frame_size_sb_calculations() {
1393 let frame_size = FrameSize {
1394 frame_width: 1920,
1395 frame_height: 1080,
1396 upscaled_width: 1920,
1397 superres_denom: 8,
1398 use_superres: false,
1399 mi_cols: 480,
1400 mi_rows: 270,
1401 };
1402
1403 assert_eq!(frame_size.sb_cols(64), 30);
1404 assert_eq!(frame_size.sb_rows(64), 17);
1405 assert_eq!(frame_size.sb_cols(128), 15);
1406 assert_eq!(frame_size.sb_rows(128), 9);
1407 }
1408
1409 #[test]
1410 fn test_segmentation_features() {
1411 let mut seg = SegmentationParams::default();
1412 seg.enabled = true;
1413 seg.feature_enabled[0][SegmentationParams::SEG_LVL_ALT_Q] = true;
1414 seg.feature_data[0][SegmentationParams::SEG_LVL_ALT_Q] = 10;
1415
1416 assert!(seg.is_feature_enabled(0, SegmentationParams::SEG_LVL_ALT_Q));
1417 assert_eq!(seg.get_feature(0, SegmentationParams::SEG_LVL_ALT_Q), 10);
1418 assert_eq!(seg.get_feature(1, SegmentationParams::SEG_LVL_ALT_Q), 0);
1419 assert!(!seg.is_feature_enabled(0, SegmentationParams::SEG_LVL_SKIP));
1420 }
1421
1422 #[test]
1423 fn test_motion_mode_conversion() {
1424 assert_eq!(MotionMode::from(0), MotionMode::Simple);
1425 assert_eq!(MotionMode::from(1), MotionMode::ObstructedMotion);
1426 assert_eq!(MotionMode::from(2), MotionMode::LocalWarp);
1427 assert_eq!(MotionMode::from(99), MotionMode::Simple);
1428 }
1429
1430 #[test]
1431 fn test_reference_mode_conversion() {
1432 assert_eq!(ReferenceMode::from(0), ReferenceMode::SingleReference);
1433 assert_eq!(ReferenceMode::from(1), ReferenceMode::CompoundReference);
1434 assert_eq!(ReferenceMode::from(2), ReferenceMode::ReferenceModeSelect);
1435 }
1436
1437 #[test]
1438 fn test_tx_mode_conversion() {
1439 assert_eq!(TxMode::from(0), TxMode::Only4x4);
1440 assert_eq!(TxMode::from(1), TxMode::Largest);
1441 assert_eq!(TxMode::from(2), TxMode::Select);
1442 }
1443
1444 #[test]
1445 fn test_restoration_type_conversion() {
1446 assert_eq!(RestorationType::from(0), RestorationType::None);
1447 assert_eq!(RestorationType::from(1), RestorationType::Wiener);
1448 assert_eq!(RestorationType::from(2), RestorationType::SgrProj);
1449 assert_eq!(RestorationType::from(3), RestorationType::Switchable);
1450 }
1451
1452 #[test]
1453 fn test_frame_header_default() {
1454 let header = FrameHeader::new();
1455 assert_eq!(header.frame_type, FrameType::KeyFrame);
1456 assert!(!header.show_frame);
1457 assert!(!header.error_resilient_mode);
1458 assert!(!header.frame_is_intra);
1459 }
1460
1461 #[test]
1462 fn test_frame_header_queries() {
1463 let mut header = FrameHeader::new();
1464 header.frame_type = FrameType::KeyFrame;
1465 assert!(header.is_key_frame());
1466 assert!(!header.is_inter_frame());
1467
1468 header.frame_type = FrameType::InterFrame;
1469 assert!(!header.is_key_frame());
1470 assert!(header.is_inter_frame());
1471
1472 header.frame_type = FrameType::SwitchFrame;
1473 assert!(!header.is_key_frame());
1474 assert!(header.is_inter_frame());
1475
1476 header.order_hint = 42;
1477 assert_eq!(header.display_order(), 42);
1478 }
1479
1480 #[test]
1481 fn test_get_qindex() {
1482 let mut header = FrameHeader::new();
1483 header.quantization.base_q_idx = 100;
1484
1485 assert_eq!(header.get_qindex(0), 100);
1487
1488 header.segmentation.enabled = true;
1490 header.segmentation.feature_enabled[0][SegmentationParams::SEG_LVL_ALT_Q] = true;
1491 header.segmentation.feature_data[0][SegmentationParams::SEG_LVL_ALT_Q] = -20;
1492
1493 assert_eq!(header.get_qindex(0), 80);
1494 assert_eq!(header.get_qindex(1), 100);
1495 }
1496
1497 #[test]
1498 fn test_global_motion_default() {
1499 let gm = GlobalMotion::default();
1500 for i in 0..NUM_REF_FRAMES {
1501 assert_eq!(gm.params[i].gm_type, 0);
1502 }
1503 }
1504
1505 #[test]
1506 fn test_film_grain_defaults() {
1507 let fg = FilmGrainParams::default();
1508 assert!(!fg.apply_grain);
1509 assert_eq!(fg.grain_seed, 0);
1510 assert_eq!(fg.num_y_points, 0);
1511 }
1512
1513 #[test]
1514 fn test_render_size_defaults() {
1515 let rs = RenderSize::default();
1516 assert_eq!(rs.render_width, 0);
1517 assert_eq!(rs.render_height, 0);
1518 assert!(!rs.render_and_frame_size_different);
1519 }
1520
1521 #[test]
1522 fn test_loop_restoration_params_defaults() {
1523 let lr = LoopRestorationParams::default();
1524 assert!(!lr.uses_lr);
1525 assert_eq!(lr.frame_restoration_type[0], RestorationType::None);
1526 }
1527
1528 #[test]
1529 fn test_ref_frame_info_defaults() {
1530 let rfi = RefFrameInfo::default();
1531 for i in 0..REFS_PER_FRAME {
1532 assert_eq!(rfi.ref_frame_idx[i], 0);
1533 }
1534 for i in 0..NUM_REF_FRAMES {
1535 assert_eq!(rfi.ref_order_hint[i], 0);
1536 assert!(!rfi.ref_frame_sign_bias[i]);
1537 }
1538 }
1539}