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}