scuffle_h265/sps/
sps_scc_extension.rs

1use std::io;
2
3use scuffle_bytes_util::BitReader;
4use scuffle_expgolomb::BitReaderExpGolombExt;
5
6use crate::range_check::range_check;
7
8/// Sequence parameter set screen content coding extension.
9///
10/// `sps_scc_extension()`
11///
12/// - ISO/IEC 23008-2 - 7.3.2.2.3
13/// - ISO/IEC 23008-2 - 7.4.3.2.3
14#[derive(Debug, Clone, PartialEq)]
15pub struct SpsSccExtension {
16    /// Equal to `true` specifies that a picture in the CVS may be included in a
17    /// reference picture list of a slice of the picture itself.
18    ///
19    /// Equal to `false` specifies that a picture in the CVS is never included in a
20    /// reference picture list of a slice of the picture itself.
21    pub sps_curr_pic_ref_enabled_flag: bool,
22    /// Palette mode information, if `palette_mode_enabled_flag` is `true`.
23    pub palette_mode: Option<SpsSccExtensionPaletteMode>,
24    /// Controls the presence and inference of the `use_integer_mv_flag`
25    /// that specifies the resolution of motion vectors for inter prediction.
26    ///
27    /// The value is in range \[0, 2\].
28    pub motion_vector_resolution_control_idc: u8,
29    /// Equal to `true` specifies that the intra boundary filtering process is
30    /// unconditionally disabled for intra prediction.
31    ///
32    /// Equal to `false` specifies that the intra boundary filtering process may be used.
33    pub intra_boundary_filtering_disabled_flag: bool,
34}
35
36impl SpsSccExtension {
37    pub(crate) fn parse<R: io::Read>(
38        bit_reader: &mut BitReader<R>,
39        chroma_format_idc: u8,
40        bit_depth_y: u8,
41        bit_depth_c: u8,
42    ) -> io::Result<Self> {
43        let sps_curr_pic_ref_enabled_flag = bit_reader.read_bit()?;
44
45        let mut palette_mode = None;
46        let palette_mode_enabled_flag = bit_reader.read_bit()?;
47        if palette_mode_enabled_flag {
48            let palette_max_size = bit_reader.read_exp_golomb()?;
49            let delta_palette_max_predictor_size = bit_reader.read_exp_golomb()?;
50
51            if palette_max_size == 0 && delta_palette_max_predictor_size != 0 {
52                return Err(io::Error::new(
53                    io::ErrorKind::InvalidData,
54                    "delta_palette_max_predictor_size must be 0 when palette_max_size is 0",
55                ));
56            }
57
58            let sps_palette_predictor_initializers_present_flag = bit_reader.read_bit()?;
59            if palette_max_size == 0 && !sps_palette_predictor_initializers_present_flag {
60                return Err(io::Error::new(
61                    io::ErrorKind::InvalidData,
62                    "sps_palette_predictor_initializers_present_flag must be 0 when palette_max_size is 0",
63                ));
64            }
65
66            let mut sps_palette_predictor_initializers = None;
67            if sps_palette_predictor_initializers_present_flag {
68                let sps_num_palette_predictor_initializers_minus1 = bit_reader.read_exp_golomb()?;
69
70                if sps_num_palette_predictor_initializers_minus1 >= palette_max_size {
71                    return Err(io::Error::new(
72                        io::ErrorKind::InvalidData,
73                        "sps_num_palette_predictor_initializers_minus1 + 1 must be less than or equal to palette_max_size",
74                    ));
75                }
76
77                let num_comps = if chroma_format_idc == 0 { 1 } else { 3 };
78
79                let mut initializers = vec![vec![0; sps_num_palette_predictor_initializers_minus1 as usize + 1]; num_comps];
80                for (comp, initializer) in initializers.iter_mut().enumerate().take(num_comps) {
81                    for sps_palette_predictor_initializer in initializer.iter_mut() {
82                        let bit_depth = if comp == 0 { bit_depth_y } else { bit_depth_c };
83                        *sps_palette_predictor_initializer = bit_reader.read_bits(bit_depth)?;
84                    }
85                }
86
87                sps_palette_predictor_initializers = Some(initializers);
88            }
89
90            palette_mode = Some(SpsSccExtensionPaletteMode {
91                palette_max_size,
92                delta_palette_max_predictor_size,
93                sps_palette_predictor_initializers,
94            });
95        }
96
97        let motion_vector_resolution_control_idc = bit_reader.read_bits(2)? as u8;
98        range_check!(motion_vector_resolution_control_idc, 0, 2)?; // 3 is reserved
99
100        let intra_boundary_filtering_disabled_flag = bit_reader.read_bit()?;
101
102        Ok(Self {
103            sps_curr_pic_ref_enabled_flag,
104            palette_mode,
105            motion_vector_resolution_control_idc,
106            intra_boundary_filtering_disabled_flag,
107        })
108    }
109}
110
111/// Directly part of [`SpsSccExtension`].
112#[derive(Debug, Clone, PartialEq)]
113pub struct SpsSccExtensionPaletteMode {
114    /// Specifies the maximum allowed palette size.
115    pub palette_max_size: u64,
116    /// Specifies the difference between the maximum allowed palette predictor size and the maximum allowed palette size.
117    ///
118    /// Defines [`PaletteMaxPredictorSize`](SpsSccExtensionPaletteMode::palette_max_predictor_size).
119    pub delta_palette_max_predictor_size: u64,
120    /// `sps_palette_predictor_initializer[comp][i]`, if `sps_palette_predictor_initializers_present_flag` is `true`.
121    ///
122    /// Specifies the value of the `comp`-th component of the `i`-th
123    /// palette entry in the SPS that is used to initialize the array PredictorPaletteEntries.
124    ///
125    /// The value of `sps_palette_predictor_initializer[0][i]` is in range \[0, `(1 << BitDepthY) − 1`\].
126    /// See [`BitDepthY`](crate::SpsRbsp::bit_depth_y).
127    ///
128    /// The values of `sps_palette_predictor_initializer[1][i]` and `sps_palette_predictor_initializer[2][i]`
129    /// is in range \[0, `(1 << BitDepthC) − 1`\].
130    /// See [`BitDepthC`](crate::SpsRbsp::bit_depth_c).
131    pub sps_palette_predictor_initializers: Option<Vec<Vec<u64>>>,
132}
133
134impl SpsSccExtensionPaletteMode {
135    /// `PaletteMaxPredictorSize = palette_max_size + delta_palette_max_predictor_size` (7-35)
136    ///
137    /// ISO/IEC 23008-2 - 7.4.3.2.3
138    pub fn palette_max_predictor_size(&self) -> u64 {
139        self.palette_max_size + self.delta_palette_max_predictor_size
140    }
141}
142
143#[cfg(test)]
144#[cfg_attr(all(test, coverage_nightly), coverage(off))]
145mod tests {
146    use byteorder::WriteBytesExt;
147    use scuffle_bytes_util::BitWriter;
148    use scuffle_expgolomb::BitWriterExpGolombExt;
149
150    #[test]
151    fn test_parse() {
152        let mut data = Vec::new();
153        let mut bit_writer = BitWriter::new(&mut data);
154
155        bit_writer.write_bit(true).unwrap(); // sps_curr_pic_ref_enabled_flag
156        bit_writer.write_bit(true).unwrap(); // palette_mode_enabled_flag
157
158        bit_writer.write_exp_golomb(5).unwrap(); // palette_max_size
159        bit_writer.write_exp_golomb(2).unwrap(); // delta_palette_max_predictor_size
160        bit_writer.write_bit(true).unwrap(); // sps_palette_predictor_initializers_present_flag
161
162        bit_writer.write_exp_golomb(1).unwrap(); // sps_num_palette_predictor_initializers_minus1
163
164        bit_writer.write_u8(1).unwrap(); // sps_palette_predictor_initializer[0][0]
165        bit_writer.write_u8(2).unwrap(); // sps_palette_predictor_initializer[0][1]
166        bit_writer.write_u8(3).unwrap(); // sps_palette_predictor_initializer[1][0]
167        bit_writer.write_u8(4).unwrap(); // sps_palette_predictor_initializer[1][1]
168        bit_writer.write_u8(5).unwrap(); // sps_palette_predictor_initializer[2][0]
169        bit_writer.write_u8(6).unwrap(); // sps_palette_predictor_initializer[2][1]
170
171        bit_writer.write_bits(0, 2).unwrap(); // motion_vector_resolution_control_idc
172        bit_writer.write_bit(false).unwrap(); // intra_boundary_filtering_disabled_flag
173
174        bit_writer.write_bits(0, 8).unwrap(); // fill the last byte
175
176        let scc_extension = super::SpsSccExtension::parse(
177            &mut scuffle_bytes_util::BitReader::new(&data[..]),
178            1, // chroma_format_idc
179            8, // bit_depth_y
180            8, // bit_depth_c
181        )
182        .unwrap();
183
184        assert!(scc_extension.sps_curr_pic_ref_enabled_flag);
185
186        assert!(scc_extension.palette_mode.is_some());
187        let palette_mode = scc_extension.palette_mode.unwrap();
188        assert_eq!(palette_mode.palette_max_size, 5);
189        assert_eq!(palette_mode.delta_palette_max_predictor_size, 2);
190        assert_eq!(palette_mode.palette_max_predictor_size(), 7);
191
192        assert!(palette_mode.sps_palette_predictor_initializers.is_some());
193        let initializers = palette_mode.sps_palette_predictor_initializers.unwrap();
194        assert_eq!(initializers.len(), 3);
195        assert_eq!(initializers[0].len(), 2);
196        assert_eq!(initializers[0][0], 1);
197        assert_eq!(initializers[0][1], 2);
198        assert_eq!(initializers[1].len(), 2);
199        assert_eq!(initializers[1][0], 3);
200        assert_eq!(initializers[1][1], 4);
201        assert_eq!(initializers[2].len(), 2);
202        assert_eq!(initializers[2][0], 5);
203        assert_eq!(initializers[2][1], 6);
204
205        assert_eq!(scc_extension.motion_vector_resolution_control_idc, 0);
206        assert!(!scc_extension.intra_boundary_filtering_disabled_flag);
207    }
208}