scuffle_h264/sps/
sps_ext.rs

1use std::io;
2
3use scuffle_bytes_util::{BitReader, BitWriter, range_check};
4use scuffle_expgolomb::{BitReaderExpGolombExt, BitWriterExpGolombExt, size_of_exp_golomb, size_of_signed_exp_golomb};
5
6/// The Sequence Parameter Set extension.
7/// ISO/IEC-14496-10-2022 - 7.3.2
8#[derive(Debug, Clone, PartialEq)]
9pub struct SpsExtended {
10    /// The `chroma_format_idc` as a u8. This is the chroma sampling relative
11    /// to the luma sampling specified in subclause 6.2.
12    ///
13    /// The value of this ranges from \[0, 3\].
14    ///
15    /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
16    /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
17    /// The largest encoding would be for `3` which is encoded as `0 0100`, which is 5 bits.
18    /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
19    ///
20    /// For more information:
21    ///
22    /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
23    pub chroma_format_idc: u8,
24
25    /// The `separate_colour_plane_flag` is a single bit.
26    ///
27    /// 0 means the the color components aren't coded separately and `ChromaArrayType` is set to `chroma_format_idc`.
28    ///
29    /// 1 means the 3 color components of the 4:4:4 chroma format are coded separately and
30    /// `ChromaArrayType` is set to 0.
31    ///
32    /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
33    pub separate_color_plane_flag: bool,
34
35    /// The `bit_depth_luma_minus8` as a u8. This is the chroma sampling relative
36    /// to the luma sampling specified in subclause 6.2.
37    ///
38    /// The value of this ranges from \[0, 6\].
39    ///
40    /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
41    /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
42    /// The largest encoding would be for `6` which is encoded as `0 0111`, which is 5 bits.
43    /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
44    ///
45    /// For more information:
46    ///
47    /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
48    pub bit_depth_luma_minus8: u8,
49
50    /// The `bit_depth_chroma_minus8` as a u8. This is the chroma sampling
51    /// relative to the luma sampling specified in subclause 6.2.
52    ///
53    /// The value of this ranges from \[0, 6\].
54    ///
55    /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
56    /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
57    /// The largest encoding would be for `6` which is encoded as `0 0111`, which is 5 bits.
58    /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
59    ///
60    /// For more information:
61    ///
62    /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
63    pub bit_depth_chroma_minus8: u8,
64
65    /// The `qpprime_y_zero_transform_bypass_flag` is a single bit.
66    ///
67    /// 0 means the transform coefficient decoding and picture construction processes wont
68    /// use the transform bypass operation.
69    ///
70    /// 1 means that when QP'_Y is 0 then a transform bypass operation for the transform
71    /// coefficient decoding and picture construction processes will be applied before
72    /// the deblocking filter process from subclause 8.5.
73    ///
74    /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
75    pub qpprime_y_zero_transform_bypass_flag: bool,
76
77    /// The `scaling_matrix`. If the length is nonzero, then
78    /// `seq_scaling_matrix_present_flag` must have been set.
79    pub scaling_matrix: Vec<Vec<i64>>,
80}
81
82impl Default for SpsExtended {
83    fn default() -> Self {
84        Self::DEFAULT
85    }
86}
87
88impl SpsExtended {
89    // default values defined in 7.4.2.1.1
90    const DEFAULT: SpsExtended = SpsExtended {
91        chroma_format_idc: 1,
92        separate_color_plane_flag: false,
93        bit_depth_luma_minus8: 0,
94        bit_depth_chroma_minus8: 0,
95        qpprime_y_zero_transform_bypass_flag: false,
96        scaling_matrix: vec![],
97    };
98
99    /// Parses an extended SPS from a bitstream.
100    /// Returns an `SpsExtended` struct.
101    pub fn parse<T: io::Read>(reader: &mut BitReader<T>) -> io::Result<Self> {
102        let chroma_format_idc = reader.read_exp_golomb()?;
103        range_check!(chroma_format_idc, 0, 3)?;
104        let chroma_format_idc = chroma_format_idc as u8;
105        // Defaults to false: ISO/IEC-14496-10-2022 - 7.4.2.1.1
106        let mut separate_color_plane_flag = false;
107        if chroma_format_idc == 3 {
108            separate_color_plane_flag = reader.read_bit()?;
109        }
110
111        let bit_depth_luma_minus8 = reader.read_exp_golomb()?;
112        range_check!(bit_depth_luma_minus8, 0, 6)?;
113        let bit_depth_luma_minus8 = bit_depth_luma_minus8 as u8;
114
115        let bit_depth_chroma_minus8 = reader.read_exp_golomb()?;
116        range_check!(bit_depth_chroma_minus8, 0, 6)?;
117        let bit_depth_chroma_minus8 = bit_depth_chroma_minus8 as u8;
118
119        let qpprime_y_zero_transform_bypass_flag = reader.read_bit()?;
120        let seq_scaling_matrix_present_flag = reader.read_bit()?;
121        let mut scaling_matrix: Vec<Vec<i64>> = vec![];
122
123        if seq_scaling_matrix_present_flag {
124            // We need to read the scaling matrices here, but we don't need them
125            // for decoding, so we just skip them.
126            let count = if chroma_format_idc != 3 { 8 } else { 12 };
127            for i in 0..count {
128                let bit = reader.read_bit()?;
129                scaling_matrix.push(vec![]);
130                if bit {
131                    let size = if i < 6 { 16 } else { 64 };
132                    let mut next_scale = 8;
133                    for _ in 0..size {
134                        let delta_scale = reader.read_signed_exp_golomb()?;
135                        scaling_matrix[i].push(delta_scale);
136                        next_scale = (next_scale + delta_scale + 256) % 256;
137                        if next_scale == 0 {
138                            break;
139                        }
140                    }
141                }
142            }
143        }
144
145        Ok(SpsExtended {
146            chroma_format_idc,
147            separate_color_plane_flag,
148            bit_depth_luma_minus8,
149            bit_depth_chroma_minus8,
150            qpprime_y_zero_transform_bypass_flag,
151            scaling_matrix,
152        })
153    }
154
155    /// Builds the SPSExtended struct into a byte stream.
156    /// Returns a built byte stream.
157    pub fn build<T: io::Write>(&self, writer: &mut BitWriter<T>) -> io::Result<()> {
158        writer.write_exp_golomb(self.chroma_format_idc as u64)?;
159
160        if self.chroma_format_idc == 3 {
161            writer.write_bit(self.separate_color_plane_flag)?;
162        }
163
164        writer.write_exp_golomb(self.bit_depth_luma_minus8 as u64)?;
165        writer.write_exp_golomb(self.bit_depth_chroma_minus8 as u64)?;
166        writer.write_bit(self.qpprime_y_zero_transform_bypass_flag)?;
167
168        writer.write_bit(!self.scaling_matrix.is_empty())?;
169
170        for vec in &self.scaling_matrix {
171            writer.write_bit(!vec.is_empty())?;
172
173            for expg in vec {
174                writer.write_signed_exp_golomb(*expg)?;
175            }
176        }
177        Ok(())
178    }
179
180    /// Returns the total bits of the SpsExtended struct.
181    ///
182    /// Note that this isn't the bytesize since aligning it may cause some values to be different.
183    pub fn bitsize(&self) -> u64 {
184        size_of_exp_golomb(self.chroma_format_idc as u64) +
185        (self.chroma_format_idc == 3) as u64 +
186        size_of_exp_golomb(self.bit_depth_luma_minus8 as u64) +
187        size_of_exp_golomb(self.bit_depth_chroma_minus8 as u64) +
188        1 + // qpprime_y_zero_transform_bypass_flag
189        1 + // scaling_matrix.is_empty()
190        // scaling matrix
191        self.scaling_matrix.len() as u64 +
192        self.scaling_matrix.iter().flat_map(|inner| inner.iter()).map(|&x| size_of_signed_exp_golomb(x)).sum::<u64>()
193    }
194
195    /// Returns the total bytes of the SpsExtended struct.
196    ///
197    /// Note that this calls [`SpsExtended::bitsize()`] and calculates the number of bytes
198    /// including any necessary padding such that the bitstream is byte aligned.
199    pub fn bytesize(&self) -> u64 {
200        self.bitsize().div_ceil(8)
201    }
202}
203
204#[cfg(test)]
205#[cfg_attr(all(test, coverage_nightly), coverage(off))]
206mod tests {
207    use scuffle_bytes_util::{BitReader, BitWriter};
208    use scuffle_expgolomb::BitWriterExpGolombExt;
209
210    use crate::sps::SpsExtended;
211
212    #[test]
213    fn test_build_size_sps_ext_chroma_not_3_and_no_scaling_matrix_and_size() {
214        // create data bitstream for sps_ext
215        let mut data = Vec::new();
216        let mut writer = BitWriter::new(&mut data);
217
218        writer.write_exp_golomb(1).unwrap();
219        writer.write_exp_golomb(2).unwrap();
220        writer.write_exp_golomb(4).unwrap();
221        writer.write_bit(true).unwrap();
222        writer.write_bit(false).unwrap();
223
224        writer.finish().unwrap();
225
226        // parse bitstream
227        let mut reader = BitReader::new_from_slice(&mut data);
228        let sps_ext = SpsExtended::parse(&mut reader).unwrap();
229
230        // create a writer for the builder
231        let mut buf = Vec::new();
232        let mut writer2 = BitWriter::new(&mut buf);
233
234        // build from the example result
235        sps_ext.build(&mut writer2).unwrap();
236        writer2.finish().unwrap();
237
238        assert_eq!(buf, data);
239
240        // now we re-parse so we can compare the bit sizes.
241        // create a reader for the parser
242        let mut reader2 = BitReader::new_from_slice(buf);
243        let rebuilt_sps_ext = SpsExtended::parse(&mut reader2).unwrap();
244
245        // now we can check the size:
246        assert_eq!(rebuilt_sps_ext.bitsize(), sps_ext.bitsize());
247        assert_eq!(rebuilt_sps_ext.bytesize(), sps_ext.bytesize());
248    }
249
250    #[test]
251    fn test_build_size_sps_ext_chroma_3_and_scaling_matrix() {
252        // create bitstream for sps_ext
253        let mut data = Vec::new();
254        let mut writer = BitWriter::new(&mut data);
255
256        // set chroma_format_idc = 3
257        writer.write_exp_golomb(3).unwrap();
258        // separate_color_plane_flag since chroma_format_idc = 3
259        writer.write_bit(true).unwrap();
260        writer.write_exp_golomb(2).unwrap();
261        writer.write_exp_golomb(4).unwrap();
262        writer.write_bit(true).unwrap();
263        // set seq_scaling_matrix_present_flag
264        writer.write_bit(true).unwrap();
265
266        // scaling matrix loop happens 12 times since chroma_format_idc is 3
267        // loop 1 of 12
268        writer.write_bit(true).unwrap();
269        // subloop 1 of 64
270        // next_scale starts as 8, we add 1 so it's 9
271        writer.write_signed_exp_golomb(1).unwrap();
272        // subloop 2 of 64
273        // next_scale is 9, we add 2 so it's 11
274        writer.write_signed_exp_golomb(2).unwrap();
275        // subloop 3 of 64
276        // next_scale is 11, we add 3 so it's 14
277        writer.write_signed_exp_golomb(3).unwrap();
278        // subloop 4 of 64: we want to break out of the loop now
279        // next_scale is 14, we subtract 14 so it's 0, triggering a break
280        writer.write_signed_exp_golomb(-14).unwrap();
281
282        // loop 2 of 12
283        writer.write_bit(true).unwrap();
284        // subloop 1 of 64
285        // next_scale starts at 8, we add 3 so it's 11
286        writer.write_signed_exp_golomb(3).unwrap();
287        // subloop 2 of 64
288        // next_scale is 11, we add 5 so it's 16
289        writer.write_signed_exp_golomb(5).unwrap();
290        // subloop 3 of 64; we want to break out of the loop now
291        // next_scale is 16, we subtract 16 so it's 0, triggering a break
292        writer.write_signed_exp_golomb(-16).unwrap();
293
294        // loop 3 of 12
295        writer.write_bit(true).unwrap();
296        // subloop 1 of 64
297        // next_scale starts at 8, we add 1 so it's 9
298        writer.write_signed_exp_golomb(1).unwrap();
299        // subloop 2 of 64; we want to break out of the loop now
300        // next_scale is 9, we subtract 9 so it's 0, triggering a break
301        writer.write_signed_exp_golomb(-9).unwrap();
302
303        // loop 4 of 12
304        writer.write_bit(true).unwrap();
305        // subloop 1 of 64; we want to break out of the loop now
306        // next scale starts at 8, we subtract 8 so it's 0, triggering a break
307        writer.write_signed_exp_golomb(-8).unwrap();
308
309        // loop 5 thru 11: try writing nothing
310        writer.write_bits(0, 7).unwrap();
311
312        // loop 12 of 12: try writing something
313        writer.write_bit(true).unwrap();
314        // subloop 1 of 64; we want to break out of the loop now
315        // next scale starts at 8, we subtract 8 so it's 0, triggering a break
316        writer.write_signed_exp_golomb(-8).unwrap();
317
318        writer.finish().unwrap();
319
320        // parse bitstream
321        let mut reader = BitReader::new_from_slice(&mut data);
322        let sps_ext = SpsExtended::parse(&mut reader).unwrap();
323
324        // create a writer for the builder
325        let mut buf = Vec::new();
326        let mut writer2 = BitWriter::new(&mut buf);
327
328        // build from the example result
329        sps_ext.build(&mut writer2).unwrap();
330        writer2.finish().unwrap();
331
332        assert_eq!(buf, data);
333
334        // now we re-parse so we can compare the bit sizes.
335        // create a reader for the parser
336        let mut reader2 = BitReader::new_from_slice(buf);
337        let rebuilt_sps_ext = SpsExtended::parse(&mut reader2).unwrap();
338
339        // now we can check the size:
340        assert_eq!(rebuilt_sps_ext.bitsize(), sps_ext.bitsize());
341        assert_eq!(rebuilt_sps_ext.bytesize(), sps_ext.bytesize());
342    }
343}