scuffle_h264/sps/sps_ext.rs
1use std::io;
2
3use scuffle_bytes_util::{BitReader, BitWriter};
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()? as u8;
103 // Defaults to false: ISO/IEC-14496-10-2022 - 7.4.2.1.1
104 let mut separate_color_plane_flag = false;
105 if chroma_format_idc == 3 {
106 separate_color_plane_flag = reader.read_bit()?;
107 }
108
109 let bit_depth_luma_minus8 = reader.read_exp_golomb()? as u8;
110 let bit_depth_chroma_minus8 = reader.read_exp_golomb()? as u8;
111 let qpprime_y_zero_transform_bypass_flag = reader.read_bit()?;
112 let seq_scaling_matrix_present_flag = reader.read_bit()?;
113 let mut scaling_matrix: Vec<Vec<i64>> = vec![];
114
115 if seq_scaling_matrix_present_flag {
116 // We need to read the scaling matrices here, but we don't need them
117 // for decoding, so we just skip them.
118 let count = if chroma_format_idc != 3 { 8 } else { 12 };
119 for i in 0..count {
120 let bit = reader.read_bit()?;
121 scaling_matrix.push(vec![]);
122 if bit {
123 let size = if i < 6 { 16 } else { 64 };
124 let mut next_scale = 8;
125 for _ in 0..size {
126 let delta_scale = reader.read_signed_exp_golomb()?;
127 scaling_matrix[i].push(delta_scale);
128 next_scale = (next_scale + delta_scale + 256) % 256;
129 if next_scale == 0 {
130 break;
131 }
132 }
133 }
134 }
135 }
136
137 Ok(SpsExtended {
138 chroma_format_idc,
139 separate_color_plane_flag,
140 bit_depth_luma_minus8,
141 bit_depth_chroma_minus8,
142 qpprime_y_zero_transform_bypass_flag,
143 scaling_matrix,
144 })
145 }
146
147 /// Builds the SPSExtended struct into a byte stream.
148 /// Returns a built byte stream.
149 pub fn build<T: io::Write>(&self, writer: &mut BitWriter<T>) -> io::Result<()> {
150 writer.write_exp_golomb(self.chroma_format_idc as u64)?;
151
152 if self.chroma_format_idc == 3 {
153 writer.write_bit(self.separate_color_plane_flag)?;
154 }
155
156 writer.write_exp_golomb(self.bit_depth_luma_minus8 as u64)?;
157 writer.write_exp_golomb(self.bit_depth_chroma_minus8 as u64)?;
158 writer.write_bit(self.qpprime_y_zero_transform_bypass_flag)?;
159
160 writer.write_bit(!self.scaling_matrix.is_empty())?;
161
162 for vec in &self.scaling_matrix {
163 writer.write_bit(!vec.is_empty())?;
164
165 for expg in vec {
166 writer.write_signed_exp_golomb(*expg)?;
167 }
168 }
169 Ok(())
170 }
171
172 /// Returns the total bits of the SpsExtended struct.
173 ///
174 /// Note that this isn't the bytesize since aligning it may cause some values to be different.
175 pub fn bitsize(&self) -> u64 {
176 size_of_exp_golomb(self.chroma_format_idc as u64) +
177 (self.chroma_format_idc == 3) as u64 +
178 size_of_exp_golomb(self.bit_depth_luma_minus8 as u64) +
179 size_of_exp_golomb(self.bit_depth_chroma_minus8 as u64) +
180 1 + // qpprime_y_zero_transform_bypass_flag
181 1 + // scaling_matrix.is_empty()
182 // scaling matrix
183 self.scaling_matrix.len() as u64 +
184 self.scaling_matrix.iter().flat_map(|inner| inner.iter()).map(|&x| size_of_signed_exp_golomb(x)).sum::<u64>()
185 }
186
187 /// Returns the total bytes of the SpsExtended struct.
188 ///
189 /// Note that this calls [`SpsExtended::bitsize()`] and calculates the number of bytes
190 /// including any necessary padding such that the bitstream is byte aligned.
191 pub fn bytesize(&self) -> u64 {
192 self.bitsize().div_ceil(8)
193 }
194}
195
196#[cfg(test)]
197#[cfg_attr(all(test, coverage_nightly), coverage(off))]
198mod tests {
199 use scuffle_bytes_util::{BitReader, BitWriter};
200 use scuffle_expgolomb::BitWriterExpGolombExt;
201
202 use crate::sps::SpsExtended;
203
204 #[test]
205 fn test_build_size_sps_ext_chroma_not_3_and_no_scaling_matrix_and_size() {
206 // create data bitstream for sps_ext
207 let mut data = Vec::new();
208 let mut writer = BitWriter::new(&mut data);
209
210 writer.write_exp_golomb(1).unwrap();
211 writer.write_exp_golomb(2).unwrap();
212 writer.write_exp_golomb(4).unwrap();
213 writer.write_bit(true).unwrap();
214 writer.write_bit(false).unwrap();
215
216 writer.finish().unwrap();
217
218 // parse bitstream
219 let mut reader = BitReader::new_from_slice(&mut data);
220 let sps_ext = SpsExtended::parse(&mut reader).unwrap();
221
222 // create a writer for the builder
223 let mut buf = Vec::new();
224 let mut writer2 = BitWriter::new(&mut buf);
225
226 // build from the example result
227 sps_ext.build(&mut writer2).unwrap();
228 writer2.finish().unwrap();
229
230 assert_eq!(buf, data);
231
232 // now we re-parse so we can compare the bit sizes.
233 // create a reader for the parser
234 let mut reader2 = BitReader::new_from_slice(buf);
235 let rebuilt_sps_ext = SpsExtended::parse(&mut reader2).unwrap();
236
237 // now we can check the size:
238 assert_eq!(rebuilt_sps_ext.bitsize(), sps_ext.bitsize());
239 assert_eq!(rebuilt_sps_ext.bytesize(), sps_ext.bytesize());
240 }
241
242 #[test]
243 fn test_build_size_sps_ext_chroma_3_and_scaling_matrix() {
244 // create bitstream for sps_ext
245 let mut data = Vec::new();
246 let mut writer = BitWriter::new(&mut data);
247
248 // set chroma_format_idc = 3
249 writer.write_exp_golomb(3).unwrap();
250 // separate_color_plane_flag since chroma_format_idc = 3
251 writer.write_bit(true).unwrap();
252 writer.write_exp_golomb(2).unwrap();
253 writer.write_exp_golomb(4).unwrap();
254 writer.write_bit(true).unwrap();
255 // set seq_scaling_matrix_present_flag
256 writer.write_bit(true).unwrap();
257
258 // scaling matrix loop happens 12 times since chroma_format_idc is 3
259 // loop 1 of 12
260 writer.write_bit(true).unwrap();
261 // subloop 1 of 64
262 // next_scale starts as 8, we add 1 so it's 9
263 writer.write_signed_exp_golomb(1).unwrap();
264 // subloop 2 of 64
265 // next_scale is 9, we add 2 so it's 11
266 writer.write_signed_exp_golomb(2).unwrap();
267 // subloop 3 of 64
268 // next_scale is 11, we add 3 so it's 14
269 writer.write_signed_exp_golomb(3).unwrap();
270 // subloop 4 of 64: we want to break out of the loop now
271 // next_scale is 14, we subtract 14 so it's 0, triggering a break
272 writer.write_signed_exp_golomb(-14).unwrap();
273
274 // loop 2 of 12
275 writer.write_bit(true).unwrap();
276 // subloop 1 of 64
277 // next_scale starts at 8, we add 3 so it's 11
278 writer.write_signed_exp_golomb(3).unwrap();
279 // subloop 2 of 64
280 // next_scale is 11, we add 5 so it's 16
281 writer.write_signed_exp_golomb(5).unwrap();
282 // subloop 3 of 64; we want to break out of the loop now
283 // next_scale is 16, we subtract 16 so it's 0, triggering a break
284 writer.write_signed_exp_golomb(-16).unwrap();
285
286 // loop 3 of 12
287 writer.write_bit(true).unwrap();
288 // subloop 1 of 64
289 // next_scale starts at 8, we add 1 so it's 9
290 writer.write_signed_exp_golomb(1).unwrap();
291 // subloop 2 of 64; we want to break out of the loop now
292 // next_scale is 9, we subtract 9 so it's 0, triggering a break
293 writer.write_signed_exp_golomb(-9).unwrap();
294
295 // loop 4 of 12
296 writer.write_bit(true).unwrap();
297 // subloop 1 of 64; we want to break out of the loop now
298 // next scale starts at 8, we subtract 8 so it's 0, triggering a break
299 writer.write_signed_exp_golomb(-8).unwrap();
300
301 // loop 5 thru 11: try writing nothing
302 writer.write_bits(0, 7).unwrap();
303
304 // loop 12 of 12: try writing something
305 writer.write_bit(true).unwrap();
306 // subloop 1 of 64; we want to break out of the loop now
307 // next scale starts at 8, we subtract 8 so it's 0, triggering a break
308 writer.write_signed_exp_golomb(-8).unwrap();
309
310 writer.finish().unwrap();
311
312 // parse bitstream
313 let mut reader = BitReader::new_from_slice(&mut data);
314 let sps_ext = SpsExtended::parse(&mut reader).unwrap();
315
316 // create a writer for the builder
317 let mut buf = Vec::new();
318 let mut writer2 = BitWriter::new(&mut buf);
319
320 // build from the example result
321 sps_ext.build(&mut writer2).unwrap();
322 writer2.finish().unwrap();
323
324 assert_eq!(buf, data);
325
326 // now we re-parse so we can compare the bit sizes.
327 // create a reader for the parser
328 let mut reader2 = BitReader::new_from_slice(buf);
329 let rebuilt_sps_ext = SpsExtended::parse(&mut reader2).unwrap();
330
331 // now we can check the size:
332 assert_eq!(rebuilt_sps_ext.bitsize(), sps_ext.bitsize());
333 assert_eq!(rebuilt_sps_ext.bytesize(), sps_ext.bytesize());
334 }
335}