scuffle_h264/sps/mod.rs
1mod chroma_sample_loc;
2use self::chroma_sample_loc::ChromaSampleLoc;
3
4mod color_config;
5use self::color_config::ColorConfig;
6
7mod frame_crop_info;
8use self::frame_crop_info::FrameCropInfo;
9
10mod pic_order_count_type1;
11use self::pic_order_count_type1::PicOrderCountType1;
12
13mod sample_aspect_ratio;
14use self::sample_aspect_ratio::SarDimensions;
15
16mod sps_ext;
17pub use self::sps_ext::SpsExtended;
18
19mod timing_info;
20use std::io;
21
22use byteorder::ReadBytesExt;
23use scuffle_bytes_util::{BitReader, BitWriter, EmulationPreventionIo, range_check};
24use scuffle_expgolomb::{BitReaderExpGolombExt, BitWriterExpGolombExt, size_of_exp_golomb};
25
26pub use self::timing_info::TimingInfo;
27use crate::NALUnitType;
28
29/// The Sequence Parameter Set.
30/// ISO/IEC-14496-10-2022 - 7.3.2
31#[derive(Debug, Clone, PartialEq)]
32pub struct Sps {
33 /// The `nal_ref_idc` is comprised of 2 bits.
34 ///
35 /// A nonzero value means the NAL unit has any of the following: SPS, SPS extension,
36 /// subset SPS, PPS, slice of a reference picture, slice of a data partition of a reference picture,
37 /// or a prefix NAL unit preceeding a slice of a reference picture.
38 ///
39 /// 0 means that the stream is decoded using the process from Clauses 2-9 (ISO/IEC-14496-10-2022)
40 /// that the slice or slice data partition is part of a non-reference picture.
41 /// Additionally, if `nal_ref_idc` is 0 for a NAL unit with `nal_unit_type`
42 /// ranging from \[1, 4\] then `nal_ref_idc` must be 0 for all NAL units with `nal_unit_type` between [1, 4].
43 ///
44 /// If the `nal_unit_type` is 5, then the `nal_ref_idc` cannot be 0.
45 ///
46 /// If `nal_unit_type` is 6, 9, 10, 11, or 12, then the `nal_ref_idc` must be 0.
47 ///
48 /// ISO/IEC-14496-10-2022 - 7.4.1
49 pub nal_ref_idc: u8,
50
51 /// The `nal_unit_type` is comprised of 5 bits. See the NALUnitType nutype enum for more info.
52 pub nal_unit_type: NALUnitType,
53
54 /// The `profile_idc` of the coded video sequence as a u8.
55 ///
56 /// It is comprised of 8 bits or 1 byte. ISO/IEC-14496-10-2022 - 7.4.2.1.1
57 pub profile_idc: u8,
58
59 /// `constraint_set0_flag`: `1` if it abides by the constraints in A.2.1, `0` if unsure or otherwise.
60 ///
61 /// If `profile_idc` is 44, 100, 110, 122, or 244, this is automatically set to false.
62 ///
63 /// It is a single bit. ISO/IEC-14496-10-2022 - 7.4.2.1.1
64 pub constraint_set0_flag: bool,
65
66 /// `constraint_set1_flag`: `1` if it abides by the constraints in A.2.2, `0` if unsure or otherwise.
67 ///
68 /// If `profile_idc` is 44, 100, 110, 122, or 244, this is automatically set to false.
69 ///
70 /// It is a single bit. ISO/IEC-14496-10-2022 - 7.4.2.1.1
71 pub constraint_set1_flag: bool,
72
73 /// `constraint_set2_flag`: `1` if it abides by the constraints in A.2.3, `0` if unsure or otherwise.
74 ///
75 /// If `profile_idc` is 44, 100, 110, 122, or 244, this is automatically set to false.
76 ///
77 /// It is a single bit. ISO/IEC-14496-10-2022 - 7.4.2.1.1
78 pub constraint_set2_flag: bool,
79
80 /// `constraint_set3_flag`:
81 /// ```text
82 /// if (profile_idc == 66, 77, or 88) AND (level_idc == 11):
83 /// 1 if it abides by the constraints in Annex A for level 1b
84 /// 0 if it abides by the constraints in Annex A for level 1.1
85 /// elif profile_idc == 100 or 110:
86 /// 1 if it abides by the constraints for the "High 10 Intra profile"
87 /// 0 if unsure or otherwise
88 /// elif profile_idc == 122:
89 /// 1 if it abides by the constraints in Annex A for the "High 4:2:2 Intra profile"
90 /// 0 if unsure or otherwise
91 /// elif profile_idc == 44:
92 /// 1 by default
93 /// 0 is not possible.
94 /// elif profile_idc == 244:
95 /// 1 if it abides by the constraints in Annex A for the "High 4:4:4 Intra profile"
96 /// 0 if unsure or otherwise
97 /// else:
98 /// 1 is reserved for future use
99 /// 0 otherwise
100 /// ```
101 ///
102 /// It is a single bit. ISO/IEC-14496-10-2022 - 7.4.2.1.1
103 pub constraint_set3_flag: bool,
104
105 /// `constraint_set4_flag`:
106 /// ```text
107 /// if (profile_idc == 77, 88, 100, or 110):
108 /// 1 if frame_mbs_only_flag == 1
109 /// 0 if unsure or otherwise
110 /// elif (profile_idc == 118, 128, or 134):
111 /// 1 if it abides by the constraints in G.6.1.1
112 /// 0 if unsure or otherwise
113 /// else:
114 /// 1 is reserved for future use
115 /// 0 otherwise
116 /// ```
117 ///
118 /// It is a single bit. ISO/IEC-14496-10-2022 - 7.4.2.1.1
119 pub constraint_set4_flag: bool,
120
121 /// `constraint_set5_flag`:
122 /// ```text
123 /// if (profile_idc == 77, 88, or 100):
124 /// 1 if there are no B slice types
125 /// 0 if unsure or otherwise
126 /// elif profile_idc == 118:
127 /// 1 if it abides by the constraints in G.6.1.2
128 /// 0 if unsure or otherwise
129 /// else:
130 /// 1 is reserved for future use
131 /// 0 otherwise
132 /// ```
133 ///
134 /// It is a single bit. ISO/IEC-14496-10-2022 - 7.4.2.1.1
135 pub constraint_set5_flag: bool,
136
137 /// The `level_idc` of the coded video sequence as a u8.
138 ///
139 /// It is comprised of 8 bits or 1 byte. ISO/IEC-14496-10-2022 - 7.4.2.1.1
140 pub level_idc: u8,
141
142 /// The `seq_parameter_set_id` is the id of the SPS referred to by the PPS (picture parameter set).
143 ///
144 /// The value of this ranges from \[0, 31\].
145 ///
146 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
147 /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
148 /// The largest encoding would be for `31` which is encoded as `000 0010 0000`, which is 11 bits.
149 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
150 ///
151 /// For more information:
152 ///
153 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
154 pub seq_parameter_set_id: u16,
155
156 /// An optional `SpsExtended`. Refer to the SpsExtended struct for more info.
157 ///
158 /// This will be parsed if `profile_idc` is equal to any of the following values:
159 /// 44, 83, 86, 100, 110, 118, 122, 128, 134, 135, 138, 139, or 244.
160 pub ext: Option<SpsExtended>,
161
162 /// The `log2_max_frame_num_minus4` is the value used when deriving MaxFrameNum from the equation:
163 /// `MaxFrameNum` = 2^(`log2_max_frame_num_minus4` + 4)
164 ///
165 /// The value of this ranges from \[0, 12\].
166 ///
167 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
168 /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
169 /// The largest encoding would be for `12` which is encoded as `000 1101`, which is 7 bits.
170 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
171 ///
172 /// For more information:
173 ///
174 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
175 pub log2_max_frame_num_minus4: u8,
176
177 /// The `pic_order_cnt_type` specifies how to decode the picture order count in subclause 8.2.1.
178 ///
179 /// The value of this ranges from \[0, 2\].
180 ///
181 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
182 /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
183 /// The largest encoding would be for `2` which is encoded as `011`, which is 3 bits.
184 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
185 ///
186 /// For more information:
187 ///
188 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
189 ///
190 /// There are a few subsequent fields that are read if `pic_order_cnt_type` is 0 or 1.
191 ///
192 /// In the case of 0, `log2_max_pic_order_cnt_lsb_minus4` is read as an exp golomb (unsigned).
193 ///
194 /// In the case of 1, `delta_pic_order_always_zero_flag`, `offset_for_non_ref_pic`,
195 /// `offset_for_top_to_bottom_field`, `num_ref_frames_in_pic_order_cnt_cycle` and
196 /// `offset_for_ref_frame` will be read and stored in pic_order_cnt_type1.
197 ///
198 /// Refer to the PicOrderCountType1 struct for more info.
199 pub pic_order_cnt_type: u8,
200
201 /// The `log2_max_pic_order_cnt_lsb_minus4` is the value used when deriving MaxFrameNum from the equation:
202 /// `MaxPicOrderCntLsb` = 2^(`log2_max_frame_num_minus4` + 4) from subclause 8.2.1.
203 ///
204 /// This is an `Option<u8>` because the value is only set if `pic_order_cnt_type == 0`.
205 ///
206 /// The value of this ranges from \[0, 12\].
207 ///
208 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
209 /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
210 /// The largest encoding would be for `12` which is encoded as `000 1101`, which is 7 bits.
211 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
212 ///
213 /// For more information:
214 ///
215 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
216 pub log2_max_pic_order_cnt_lsb_minus4: Option<u8>,
217
218 /// An optional `PicOrderCountType1`. This is computed from other fields, and isn't directly set.
219 ///
220 /// If `pic_order_cnt_type == 1`, then the `PicOrderCountType1` will be computed.
221 ///
222 /// Refer to the PicOrderCountType1 struct for more info.
223 pub pic_order_cnt_type1: Option<PicOrderCountType1>,
224
225 /// The `max_num_ref_frames` is the max short-term and long-term reference frames,
226 /// complementary reference field pairs, and non-paired reference fields that
227 /// can be used by the decoder for inter-prediction of pictures in the coded video.
228 ///
229 /// The value of this ranges from \[0, `MaxDpbFrames`\], which is specified in subclause A.3.1 or A.3.2.
230 ///
231 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
232 /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
233 /// The largest encoding would be for `14` which is encoded as `000 1111`, which is 7 bits.
234 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
235 ///
236 /// For more information:
237 ///
238 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
239 pub max_num_ref_frames: u8,
240
241 /// The `gaps_in_frame_num_value_allowed_flag` is a single bit.
242 ///
243 /// The value specifies the allowed values of `frame_num` from subclause 7.4.3 and the decoding process
244 /// if there is an inferred gap between the values of `frame_num` from subclause 8.2.5.2.
245 pub gaps_in_frame_num_value_allowed_flag: bool,
246
247 /// The `pic_width_in_mbs_minus1` is the width of each decoded picture in macroblocks as a u64.
248 ///
249 /// We then use this (along with the left and right frame crop offsets) to calculate the width as:
250 ///
251 /// `width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_right_offset * 2 - frame_crop_left_offset * 2`
252 ///
253 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
254 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
255 ///
256 /// For more information:
257 ///
258 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
259 pub pic_width_in_mbs_minus1: u64,
260
261 /// The `pic_height_in_map_units_minus1` is the height of each decoded frame in slice group map units as a u64.
262 ///
263 /// We then use this (along with the bottom and top frame crop offsets) to calculate the height as:
264 ///
265 /// `height = ((2 - frame_mbs_only_flag as u64) * (pic_height_in_map_units_minus1 + 1) * 16) -
266 /// frame_crop_bottom_offset * 2 - frame_crop_top_offset * 2`
267 ///
268 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
269 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
270 ///
271 /// For more information:
272 ///
273 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
274 pub pic_height_in_map_units_minus1: u64,
275
276 /// The `mb_adaptive_frame_field_flag` is a single bit.
277 ///
278 /// If `frame_mbs_only_flag` is NOT set then this field is read and stored.
279 ///
280 /// 0 means there is no switching between frame and field macroblocks in a picture.
281 ///
282 /// 1 means the might be switching between frame and field macroblocks in a picture.
283 ///
284 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
285 pub mb_adaptive_frame_field_flag: Option<bool>,
286
287 /// The `direct_8x8_inference_flag` specifies the method used to derive the luma motion
288 /// vectors for B_Skip, B_Direct_8x8 and B_Direct_16x16 from subclause 8.4.1.2, and is a single bit.
289 ///
290 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
291 pub direct_8x8_inference_flag: bool,
292
293 /// An optional `frame_crop_info` struct. This is computed by other fields, and isn't directly set.
294 ///
295 /// If the `frame_cropping_flag` is set, then `frame_crop_left_offset`, `frame_crop_right_offset`,
296 /// `frame_crop_top_offset`, and `frame_crop_bottom_offset` will be read and stored.
297 ///
298 /// Refer to the FrameCropInfo struct for more info.
299 pub frame_crop_info: Option<FrameCropInfo>,
300
301 /// An optional `SarDimensions` struct. This is computed by other fields, and isn't directly set.
302 ///
303 /// If the `aspect_ratio_info_present_flag` is set, then the `aspect_ratio_idc` will be read and stored.
304 ///
305 /// If the `aspect_ratio_idc` is 255, then the `sar_width` and `sar_height` will be read and stored.
306 ///
307 /// Also known as `sample_aspect_ratio` in the spec.
308 ///
309 /// The default values are set to 0 for the `aspect_ratio_idc`, `sar_width`, and `sar_height`.
310 /// Therefore, this will always be returned by the parse function.
311 /// ISO/IEC-14496-10-2022 - E.2.1
312 ///
313 /// Refer to the SarDimensions struct for more info.
314 pub sample_aspect_ratio: Option<SarDimensions>,
315
316 /// An optional `overscan_appropriate_flag` is a single bit.
317 ///
318 /// If the `overscan_info_present_flag` is set, then this field will be read and stored.
319 ///
320 /// 0 means the overscan should not be used. (ex: screensharing or security cameras)
321 ///
322 /// 1 means the overscan can be used. (ex: entertainment TV programming or live video conference)
323 ///
324 /// ISO/IEC-14496-10-2022 - E.2.1
325 pub overscan_appropriate_flag: Option<bool>,
326
327 /// An optional `ColorConfig`. This is computed from other fields, and isn't directly set.
328 ///
329 /// If `video_signal_type_present_flag` is set, then the `ColorConfig` will be computed, and
330 /// if the `color_description_present_flag` is set, then the `ColorConfig` will be
331 /// comprised of the `video_full_range_flag` (1 bit), `color_primaries` (1 byte as a u8),
332 /// `transfer_characteristics` (1 byte as a u8), and `matrix_coefficients` (1 byte as a u8).
333 ///
334 /// Otherwise, `color_primaries`, `transfer_characteristics`, and `matrix_coefficients` are set
335 /// to 2 (unspecified) by default.
336 ///
337 /// Refer to the ColorConfig struct for more info.
338 pub color_config: Option<ColorConfig>,
339
340 /// An optional `ChromaSampleLoc`. This is computed from other fields, and isn't directly set.
341 ///
342 /// If `chrome_loc_info_present_flag` is set, then the `ChromaSampleLoc` will be computed, and
343 /// is comprised of `chroma_sample_loc_type_top_field` and `chroma_sample_loc_type_bottom_field`.
344 ///
345 /// Refer to the ChromaSampleLoc struct for more info.
346 pub chroma_sample_loc: Option<ChromaSampleLoc>,
347
348 /// An optional `TimingInfo`. This is computed from other fields, and isn't directly set.
349 ///
350 /// If `timing_info_present_flag` is set, then the `TimingInfo` will be computed, and
351 /// is comprised of `num_units_in_tick` and `time_scale`.
352 ///
353 /// Refer to the TimingInfo struct for more info.
354 pub timing_info: Option<TimingInfo>,
355}
356
357impl Sps {
358 /// Parses an Sps from the input bytes.
359 ///
360 /// Returns an `Sps` struct.
361 pub fn parse(reader: impl io::Read) -> io::Result<Self> {
362 let mut bit_reader = BitReader::new(reader);
363
364 let forbidden_zero_bit = bit_reader.read_bit()?;
365 if forbidden_zero_bit {
366 return Err(io::Error::new(io::ErrorKind::InvalidData, "Forbidden zero bit is set"));
367 }
368
369 let nal_ref_idc = bit_reader.read_bits(2)? as u8;
370 let nal_unit_type = bit_reader.read_bits(5)? as u8;
371 if NALUnitType(nal_unit_type) != NALUnitType::SPS {
372 return Err(io::Error::new(io::ErrorKind::InvalidData, "NAL unit type is not SPS"));
373 }
374
375 let profile_idc = bit_reader.read_u8()?;
376
377 let constraint_set0_flag;
378 let constraint_set1_flag;
379 let constraint_set2_flag;
380
381 match profile_idc {
382 // 7.4.2.1.1
383 44 | 100 | 110 | 122 | 244 => {
384 // constraint_set0 thru 2 must be false in this case
385 bit_reader.read_bits(3)?;
386 constraint_set0_flag = false;
387 constraint_set1_flag = false;
388 constraint_set2_flag = false;
389 }
390 _ => {
391 // otherwise we parse the bits as expected
392 constraint_set0_flag = bit_reader.read_bit()?;
393 constraint_set1_flag = bit_reader.read_bit()?;
394 constraint_set2_flag = bit_reader.read_bit()?;
395 }
396 }
397
398 let constraint_set3_flag = if profile_idc == 44 {
399 bit_reader.read_bit()?;
400 false
401 } else {
402 bit_reader.read_bit()?
403 };
404
405 let constraint_set4_flag = match profile_idc {
406 // 7.4.2.1.1
407 77 | 88 | 100 | 118 | 128 | 134 => bit_reader.read_bit()?,
408 _ => {
409 bit_reader.read_bit()?;
410 false
411 }
412 };
413
414 let constraint_set5_flag = match profile_idc {
415 77 | 88 | 100 | 118 => bit_reader.read_bit()?,
416 _ => {
417 bit_reader.read_bit()?;
418 false
419 }
420 };
421 // reserved_zero_2bits
422 bit_reader.read_bits(2)?;
423
424 let level_idc = bit_reader.read_u8()?;
425 let seq_parameter_set_id = bit_reader.read_exp_golomb()?;
426 range_check!(seq_parameter_set_id, 0, 31)?;
427 let seq_parameter_set_id = seq_parameter_set_id as u16;
428
429 let sps_ext = match profile_idc {
430 100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135 => {
431 Some(SpsExtended::parse(&mut bit_reader)?)
432 }
433 _ => None,
434 };
435
436 let log2_max_frame_num_minus4 = bit_reader.read_exp_golomb()?;
437 range_check!(log2_max_frame_num_minus4, 0, 12)?;
438 let log2_max_frame_num_minus4 = log2_max_frame_num_minus4 as u8;
439
440 let pic_order_cnt_type = bit_reader.read_exp_golomb()?;
441 range_check!(pic_order_cnt_type, 0, 2)?;
442 let pic_order_cnt_type = pic_order_cnt_type as u8;
443
444 let mut log2_max_pic_order_cnt_lsb_minus4 = None;
445 let mut pic_order_cnt_type1 = None;
446
447 if pic_order_cnt_type == 0 {
448 let log2_max_pic_order_cnt_lsb_minus4_value = bit_reader.read_exp_golomb()?;
449 range_check!(log2_max_pic_order_cnt_lsb_minus4_value, 0, 12)?;
450 log2_max_pic_order_cnt_lsb_minus4 = Some(log2_max_pic_order_cnt_lsb_minus4_value as u8);
451 } else if pic_order_cnt_type == 1 {
452 pic_order_cnt_type1 = Some(PicOrderCountType1::parse(&mut bit_reader)?)
453 }
454
455 let max_num_ref_frames = bit_reader.read_exp_golomb()? as u8;
456 let gaps_in_frame_num_value_allowed_flag = bit_reader.read_bit()?;
457 let pic_width_in_mbs_minus1 = bit_reader.read_exp_golomb()?;
458 let pic_height_in_map_units_minus1 = bit_reader.read_exp_golomb()?;
459
460 let frame_mbs_only_flag = bit_reader.read_bit()?;
461 let mut mb_adaptive_frame_field_flag = None;
462 if !frame_mbs_only_flag {
463 mb_adaptive_frame_field_flag = Some(bit_reader.read_bit()?);
464 }
465
466 let direct_8x8_inference_flag = bit_reader.read_bit()?;
467
468 let mut frame_crop_info = None;
469
470 let frame_cropping_flag = bit_reader.read_bit()?;
471 if frame_cropping_flag {
472 frame_crop_info = Some(FrameCropInfo::parse(&mut bit_reader)?)
473 }
474
475 // setting default values for vui section
476 let mut sample_aspect_ratio = None;
477 let mut overscan_appropriate_flag = None;
478 let mut color_config = None;
479 let mut chroma_sample_loc = None;
480 let mut timing_info = None;
481
482 let vui_parameters_present_flag = bit_reader.read_bit()?;
483 if vui_parameters_present_flag {
484 // We read the VUI parameters to get the frame rate.
485
486 let aspect_ratio_info_present_flag = bit_reader.read_bit()?;
487 if aspect_ratio_info_present_flag {
488 sample_aspect_ratio = Some(SarDimensions::parse(&mut bit_reader)?)
489 }
490
491 let overscan_info_present_flag = bit_reader.read_bit()?;
492 if overscan_info_present_flag {
493 overscan_appropriate_flag = Some(bit_reader.read_bit()?);
494 }
495
496 let video_signal_type_present_flag = bit_reader.read_bit()?;
497 if video_signal_type_present_flag {
498 color_config = Some(ColorConfig::parse(&mut bit_reader)?)
499 }
500
501 let chroma_loc_info_present_flag = bit_reader.read_bit()?;
502 if sps_ext.as_ref().unwrap_or(&SpsExtended::default()).chroma_format_idc != 1 && chroma_loc_info_present_flag {
503 return Err(io::Error::new(
504 io::ErrorKind::InvalidData,
505 "chroma_loc_info_present_flag cannot be set to 1 when chroma_format_idc is not 1",
506 ));
507 }
508
509 if chroma_loc_info_present_flag {
510 chroma_sample_loc = Some(ChromaSampleLoc::parse(&mut bit_reader)?)
511 }
512
513 let timing_info_present_flag = bit_reader.read_bit()?;
514 if timing_info_present_flag {
515 timing_info = Some(TimingInfo::parse(&mut bit_reader)?)
516 }
517 }
518
519 Ok(Sps {
520 nal_ref_idc,
521 nal_unit_type: NALUnitType(nal_unit_type),
522 profile_idc,
523 constraint_set0_flag,
524 constraint_set1_flag,
525 constraint_set2_flag,
526 constraint_set3_flag,
527 constraint_set4_flag,
528 constraint_set5_flag,
529 level_idc,
530 seq_parameter_set_id,
531 ext: sps_ext,
532 log2_max_frame_num_minus4,
533 pic_order_cnt_type,
534 log2_max_pic_order_cnt_lsb_minus4,
535 pic_order_cnt_type1,
536 max_num_ref_frames,
537 gaps_in_frame_num_value_allowed_flag,
538 pic_width_in_mbs_minus1,
539 pic_height_in_map_units_minus1,
540 mb_adaptive_frame_field_flag,
541 direct_8x8_inference_flag,
542 frame_crop_info,
543 sample_aspect_ratio,
544 overscan_appropriate_flag,
545 color_config,
546 chroma_sample_loc,
547 timing_info,
548 })
549 }
550
551 /// Builds the Sps struct into a byte stream.
552 /// Returns a built byte stream.
553 pub fn build(&self, writer: impl io::Write) -> io::Result<()> {
554 let mut bit_writer = BitWriter::new(writer);
555
556 bit_writer.write_bit(false)?;
557 bit_writer.write_bits(self.nal_ref_idc as u64, 2)?;
558 bit_writer.write_bits(self.nal_unit_type.0 as u64, 5)?;
559 bit_writer.write_bits(self.profile_idc as u64, 8)?;
560
561 bit_writer.write_bit(self.constraint_set0_flag)?;
562 bit_writer.write_bit(self.constraint_set1_flag)?;
563 bit_writer.write_bit(self.constraint_set2_flag)?;
564 bit_writer.write_bit(self.constraint_set3_flag)?;
565 bit_writer.write_bit(self.constraint_set4_flag)?;
566 bit_writer.write_bit(self.constraint_set5_flag)?;
567 // reserved 2 bits
568 bit_writer.write_bits(0, 2)?;
569
570 bit_writer.write_bits(self.level_idc as u64, 8)?;
571 bit_writer.write_exp_golomb(self.seq_parameter_set_id as u64)?;
572
573 // sps ext
574 if let Some(ext) = &self.ext {
575 ext.build(&mut bit_writer)?;
576 }
577
578 bit_writer.write_exp_golomb(self.log2_max_frame_num_minus4 as u64)?;
579 bit_writer.write_exp_golomb(self.pic_order_cnt_type as u64)?;
580
581 if self.pic_order_cnt_type == 0 {
582 bit_writer.write_exp_golomb(self.log2_max_pic_order_cnt_lsb_minus4.unwrap() as u64)?;
583 } else if let Some(pic_order_cnt) = &self.pic_order_cnt_type1 {
584 pic_order_cnt.build(&mut bit_writer)?;
585 }
586
587 bit_writer.write_exp_golomb(self.max_num_ref_frames as u64)?;
588 bit_writer.write_bit(self.gaps_in_frame_num_value_allowed_flag)?;
589 bit_writer.write_exp_golomb(self.pic_width_in_mbs_minus1)?;
590 bit_writer.write_exp_golomb(self.pic_height_in_map_units_minus1)?;
591
592 bit_writer.write_bit(self.mb_adaptive_frame_field_flag.is_none())?;
593 if let Some(flag) = self.mb_adaptive_frame_field_flag {
594 bit_writer.write_bit(flag)?;
595 }
596
597 bit_writer.write_bit(self.direct_8x8_inference_flag)?;
598
599 bit_writer.write_bit(self.frame_crop_info.is_some())?;
600 if let Some(frame_crop_info) = &self.frame_crop_info {
601 frame_crop_info.build(&mut bit_writer)?;
602 }
603
604 match (
605 &self.sample_aspect_ratio,
606 &self.overscan_appropriate_flag,
607 &self.color_config,
608 &self.chroma_sample_loc,
609 &self.timing_info,
610 ) {
611 (None, None, None, None, None) => {
612 bit_writer.write_bit(false)?;
613 }
614 _ => {
615 // vui_parameters_present_flag
616 bit_writer.write_bit(true)?;
617
618 // aspect_ratio_info_present_flag
619 bit_writer.write_bit(self.sample_aspect_ratio.is_some())?;
620 if let Some(sar) = &self.sample_aspect_ratio {
621 sar.build(&mut bit_writer)?;
622 }
623
624 // overscan_info_present_flag
625 bit_writer.write_bit(self.overscan_appropriate_flag.is_some())?;
626 if let Some(overscan) = &self.overscan_appropriate_flag {
627 bit_writer.write_bit(*overscan)?;
628 }
629
630 // video_signal_type_prsent_flag
631 bit_writer.write_bit(self.color_config.is_some())?;
632 if let Some(color) = &self.color_config {
633 color.build(&mut bit_writer)?;
634 }
635
636 // chroma_log_info_present_flag
637 bit_writer.write_bit(self.chroma_sample_loc.is_some())?;
638 if let Some(chroma) = &self.chroma_sample_loc {
639 chroma.build(&mut bit_writer)?;
640 }
641
642 // timing_info_present_flag
643 bit_writer.write_bit(self.timing_info.is_some())?;
644 if let Some(timing) = &self.timing_info {
645 timing.build(&mut bit_writer)?;
646 }
647 }
648 }
649 bit_writer.finish()?;
650
651 Ok(())
652 }
653
654 /// Parses the Sps struct from a reader that may contain emulation prevention bytes.
655 /// Is the same as calling [`Self::parse`] with an [`EmulationPreventionIo`] wrapper.
656 pub fn parse_with_emulation_prevention(reader: impl io::Read) -> io::Result<Self> {
657 Self::parse(EmulationPreventionIo::new(reader))
658 }
659
660 /// Builds the Sps struct into a byte stream that may contain emulation prevention bytes.
661 /// Is the same as calling [`Self::build`] with an [`EmulationPreventionIo`] wrapper.
662 pub fn build_with_emulation_prevention(self, writer: impl io::Write) -> io::Result<()> {
663 self.build(EmulationPreventionIo::new(writer))
664 }
665
666 /// Returns the total byte size of the Sps struct.
667 pub fn size(&self) -> u64 {
668 (1 + // forbidden zero bit
669 2 + // nal_ref_idc
670 5 + // nal_unit_type
671 8 + // profile_idc
672 8 + // 6 constraint_setn_flags + 2 reserved bits
673 8 + // level_idc
674 size_of_exp_golomb(self.seq_parameter_set_id as u64) +
675 self.ext.as_ref().map_or(0, |ext| ext.bitsize()) +
676 size_of_exp_golomb(self.log2_max_frame_num_minus4 as u64) +
677 size_of_exp_golomb(self.pic_order_cnt_type as u64) +
678 match self.pic_order_cnt_type {
679 0 => size_of_exp_golomb(self.log2_max_pic_order_cnt_lsb_minus4.unwrap() as u64),
680 1 => self.pic_order_cnt_type1.as_ref().unwrap().bitsize(),
681 _ => 0
682 } +
683 size_of_exp_golomb(self.max_num_ref_frames as u64) +
684 1 + // gaps_in_frame_num_value_allowed_flag
685 size_of_exp_golomb(self.pic_width_in_mbs_minus1) +
686 size_of_exp_golomb(self.pic_height_in_map_units_minus1) +
687 1 + // frame_mbs_only_flag
688 self.mb_adaptive_frame_field_flag.is_some() as u64 +
689 1 + // direct_8x8_inference_flag
690 1 + // frame_cropping_flag
691 self.frame_crop_info.as_ref().map_or(0, |frame| frame.bitsize()) +
692 1 + // vui_parameters_present_flag
693 if matches!(
694 (&self.sample_aspect_ratio, &self.overscan_appropriate_flag, &self.color_config, &self.chroma_sample_loc, &self.timing_info),
695 (None, None, None, None, None)
696 ) {
697 0
698 } else {
699 self.sample_aspect_ratio.as_ref().map_or(1, |sar| 1 + sar.bitsize()) +
700 self.overscan_appropriate_flag.map_or(1, |_| 2) +
701 self.color_config.as_ref().map_or(1, |color| 1 + color.bitsize()) +
702 self.chroma_sample_loc.as_ref().map_or(1, |chroma| 1 + chroma.bitsize()) +
703 self.timing_info.as_ref().map_or(1, |timing| 1 + timing.bitsize())
704 }).div_ceil(8)
705 }
706
707 /// The height as a u64. This is computed from other fields, and isn't directly set.
708 ///
709 /// `height = ((2 - frame_mbs_only_flag as u64) * (pic_height_in_map_units_minus1 + 1) * 16) -
710 /// frame_crop_bottom_offset * 2 - frame_crop_top_offset * 2`
711 ///
712 /// We don't directly store `frame_mbs_only_flag` since we can tell if it's set:
713 /// If `mb_adaptive_frame_field_flag` is None, then `frame_mbs_only_flag` is set (1).
714 /// Otherwise `mb_adaptive_frame_field_flag` unset (0).
715 pub fn height(&self) -> u64 {
716 let base_height =
717 (2 - self.mb_adaptive_frame_field_flag.is_none() as u64) * (self.pic_height_in_map_units_minus1 + 1) * 16;
718
719 self.frame_crop_info.as_ref().map_or(base_height, |crop| {
720 base_height - (crop.frame_crop_top_offset + crop.frame_crop_bottom_offset) * 2
721 })
722 }
723
724 /// The width as a u64. This is computed from other fields, and isn't directly set.
725 ///
726 /// `width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_right_offset * 2 - frame_crop_left_offset * 2`
727 pub fn width(&self) -> u64 {
728 let base_width = (self.pic_width_in_mbs_minus1 + 1) * 16;
729
730 self.frame_crop_info.as_ref().map_or(base_width, |crop| {
731 base_width - (crop.frame_crop_left_offset + crop.frame_crop_right_offset) * 2
732 })
733 }
734
735 /// Returns the frame rate as a f64.
736 ///
737 /// If `timing_info_present_flag` is set, then the `frame_rate` will be computed, and
738 /// if `num_units_in_tick` is nonzero, then the framerate will be:
739 /// `frame_rate = time_scale as f64 / (2.0 * num_units_in_tick as f64)`
740 pub fn frame_rate(&self) -> Option<f64> {
741 self.timing_info.as_ref().map(|timing| timing.frame_rate())
742 }
743}
744
745#[cfg(test)]
746#[cfg_attr(all(test, coverage_nightly), coverage(off))]
747mod tests {
748 use std::io;
749
750 use scuffle_bytes_util::BitWriter;
751 use scuffle_expgolomb::{BitWriterExpGolombExt, size_of_exp_golomb, size_of_signed_exp_golomb};
752
753 use crate::sps::Sps;
754
755 #[test]
756 fn test_parse_sps_set_forbidden_bit() {
757 let mut sps = Vec::new();
758 let mut writer = BitWriter::new(&mut sps);
759
760 writer.write_bit(true).unwrap(); // sets the forbidden bit
761 writer.finish().unwrap();
762
763 let result = Sps::parse(std::io::Cursor::new(sps));
764
765 assert!(result.is_err());
766 let err = result.unwrap_err();
767
768 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
769 assert_eq!(err.to_string(), "Forbidden zero bit is set");
770 }
771
772 #[test]
773 fn test_parse_sps_invalid_nal() {
774 let mut sps = Vec::new();
775 let mut writer = BitWriter::new(&mut sps);
776
777 writer.write_bit(false).unwrap(); // forbidden zero bit must be unset
778 writer.write_bits(0b00, 2).unwrap(); // nal_ref_idc is 00
779 writer.write_bits(0b000, 3).unwrap(); // set nal_unit_type to something that isn't 7
780 writer.finish().unwrap();
781
782 let result = Sps::parse(std::io::Cursor::new(sps));
783
784 assert!(result.is_err());
785 let err = result.unwrap_err();
786
787 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
788 assert_eq!(err.to_string(), "NAL unit type is not SPS");
789 }
790
791 #[test]
792 fn test_parse_build_sps_4k_144fps() {
793 let mut sps = Vec::new();
794 let mut writer = BitWriter::new(&mut sps);
795
796 // forbidden zero bit must be unset
797 writer.write_bit(false).unwrap();
798 // nal_ref_idc is 0
799 writer.write_bits(0, 2).unwrap();
800 // nal_unit_type must be 7
801 writer.write_bits(7, 5).unwrap();
802
803 // profile_idc = 100
804 writer.write_bits(100, 8).unwrap();
805 // constraint_setn_flags all false
806 writer.write_bits(0, 8).unwrap();
807 // level_idc = 0
808 writer.write_bits(0, 8).unwrap();
809
810 // seq_parameter_set_id is expg
811 writer.write_exp_golomb(0).unwrap();
812
813 // sps ext
814 // chroma_format_idc is expg
815 writer.write_exp_golomb(0).unwrap();
816 // bit_depth_luma_minus8 is expg
817 writer.write_exp_golomb(0).unwrap();
818 // bit_depth_chroma_minus8 is expg
819 writer.write_exp_golomb(0).unwrap();
820 // qpprime
821 writer.write_bit(false).unwrap();
822 // seq_scaling_matrix_present_flag
823 writer.write_bit(false).unwrap();
824
825 // back to sps
826 // log2_max_frame_num_minus4 is expg
827 writer.write_exp_golomb(0).unwrap();
828 // pic_order_cnt_type is expg
829 writer.write_exp_golomb(0).unwrap();
830 // log2_max_pic_order_cnt_lsb_minus4 is expg
831 writer.write_exp_golomb(0).unwrap();
832
833 // max_num_ref_frames is expg
834 writer.write_exp_golomb(0).unwrap();
835 // gaps_in_frame_num_value_allowed_flag
836 writer.write_bit(false).unwrap();
837 // 3840 width:
838 // 3840 = (p + 1) * 16 - 2 * offset1 - 2 * offset2
839 // we set offset1 and offset2 to both be 0 later
840 // 3840 = (p + 1) * 16
841 // p = 239
842 writer.write_exp_golomb(239).unwrap();
843 // we want 2160 height:
844 // 2160 = ((2 - m) * (p + 1) * 16) - 2 * offset1 - 2 * offset2
845 // we set offset1 and offset2 to both be 0 later
846 // m is frame_mbs_only_flag which we set to 1 later
847 // 2160 = (2 - 1) * (p + 1) * 16
848 // 2160 = (p + 1) * 16
849 // p = 134
850 writer.write_exp_golomb(134).unwrap();
851
852 // frame_mbs_only_flag
853 writer.write_bit(true).unwrap();
854
855 // direct_8x8_inference_flag
856 writer.write_bit(false).unwrap();
857 // frame_cropping_flag
858 writer.write_bit(false).unwrap();
859
860 // vui_parameters_present_flag
861 writer.write_bit(true).unwrap();
862
863 // enter vui to set the framerate
864 // aspect_ratio_info_present_flag
865 writer.write_bit(true).unwrap();
866 // we want square (1:1) for 16:9 for 4k w/o overscan
867 // aspect_ratio_idc
868 writer.write_bits(1, 8).unwrap();
869
870 // overscan_info_present_flag
871 writer.write_bit(true).unwrap();
872 // we dont want overscan
873 // overscan_appropriate_flag
874 writer.write_bit(false).unwrap();
875
876 // video_signal_type_present_flag
877 writer.write_bit(false).unwrap();
878 // chroma_loc_info_present_flag
879 writer.write_bit(false).unwrap();
880
881 // timing_info_present_flag
882 writer.write_bit(true).unwrap();
883 // we can set this to 100 for example
884 // num_units_in_tick is a u32
885 writer.write_bits(100, 32).unwrap();
886 // fps = time_scale / (2 * num_units_in_tick)
887 // since we want 144 fps:
888 // 144 = time_scale / (2 * 100)
889 // 28800 = time_scale
890 // time_scale is a u32
891 writer.write_bits(28800, 32).unwrap();
892 writer.finish().unwrap();
893
894 let result = Sps::parse(std::io::Cursor::new(sps)).unwrap();
895
896 insta::assert_debug_snapshot!(result, @r"
897 Sps {
898 nal_ref_idc: 0,
899 nal_unit_type: NALUnitType::SPS,
900 profile_idc: 100,
901 constraint_set0_flag: false,
902 constraint_set1_flag: false,
903 constraint_set2_flag: false,
904 constraint_set3_flag: false,
905 constraint_set4_flag: false,
906 constraint_set5_flag: false,
907 level_idc: 0,
908 seq_parameter_set_id: 0,
909 ext: Some(
910 SpsExtended {
911 chroma_format_idc: 0,
912 separate_color_plane_flag: false,
913 bit_depth_luma_minus8: 0,
914 bit_depth_chroma_minus8: 0,
915 qpprime_y_zero_transform_bypass_flag: false,
916 scaling_matrix: [],
917 },
918 ),
919 log2_max_frame_num_minus4: 0,
920 pic_order_cnt_type: 0,
921 log2_max_pic_order_cnt_lsb_minus4: Some(
922 0,
923 ),
924 pic_order_cnt_type1: None,
925 max_num_ref_frames: 0,
926 gaps_in_frame_num_value_allowed_flag: false,
927 pic_width_in_mbs_minus1: 239,
928 pic_height_in_map_units_minus1: 134,
929 mb_adaptive_frame_field_flag: None,
930 direct_8x8_inference_flag: false,
931 frame_crop_info: None,
932 sample_aspect_ratio: Some(
933 SarDimensions {
934 aspect_ratio_idc: AspectRatioIdc::Square,
935 sar_width: 0,
936 sar_height: 0,
937 },
938 ),
939 overscan_appropriate_flag: Some(
940 false,
941 ),
942 color_config: None,
943 chroma_sample_loc: None,
944 timing_info: Some(
945 TimingInfo {
946 num_units_in_tick: 100,
947 time_scale: 28800,
948 },
949 ),
950 }
951 ");
952
953 assert_eq!(Some(144.0), result.frame_rate());
954 assert_eq!(3840, result.width());
955 assert_eq!(2160, result.height());
956
957 // create a writer for the builder
958 let mut buf = Vec::new();
959 let mut writer2 = BitWriter::new(&mut buf);
960
961 // build from the example sps
962 result.build(&mut writer2).unwrap();
963 writer2.finish().unwrap();
964
965 // sometimes bits can get lost because we save
966 // some space with how the SPS is rebuilt.
967 // so we can just confirm that they're the same
968 // by rebuilding it.
969 let reduced = Sps::parse(std::io::Cursor::new(&buf)).unwrap(); // <- this is where things break
970 assert_eq!(reduced, result);
971
972 // now we can check that the bitstream from
973 // the reduced version should be the same
974 let mut reduced_buf = Vec::new();
975 let mut writer3 = BitWriter::new(&mut reduced_buf);
976
977 reduced.build(&mut writer3).unwrap();
978 writer3.finish().unwrap();
979 assert_eq!(reduced_buf, buf);
980
981 // now we can check the size:
982 assert_eq!(reduced.size(), result.size());
983 }
984
985 #[test]
986 fn test_parse_build_sps_1080_480fps_scaling_matrix() {
987 let mut sps = Vec::new();
988 let mut writer = BitWriter::new(&mut sps);
989
990 // forbidden zero bit must be unset
991 writer.write_bit(false).unwrap();
992 // nal_ref_idc is 0
993 writer.write_bits(0, 2).unwrap();
994 // nal_unit_type must be 7
995 writer.write_bits(7, 5).unwrap();
996
997 // profile_idc = 44
998 writer.write_bits(44, 8).unwrap();
999 // constraint_setn_flags all false
1000 writer.write_bits(0, 8).unwrap();
1001 // level_idc = 0
1002 writer.write_bits(0, 8).unwrap();
1003 // seq_parameter_set_id is expg
1004 writer.write_exp_golomb(0).unwrap();
1005
1006 // sps ext
1007 // we want to try out chroma_format_idc = 3
1008 // chroma_format_idc is expg
1009 writer.write_exp_golomb(3).unwrap();
1010 // separate_color_plane_flag
1011 writer.write_bit(false).unwrap();
1012 // bit_depth_luma_minus8 is expg
1013 writer.write_exp_golomb(0).unwrap();
1014 // bit_depth_chroma_minus8 is expg
1015 writer.write_exp_golomb(0).unwrap();
1016 // qpprime
1017 writer.write_bit(false).unwrap();
1018 // we want to simulate a scaling matrix
1019 // seq_scaling_matrix_present_flag
1020 writer.write_bit(true).unwrap();
1021
1022 // enter scaling matrix, we loop 12 times since
1023 // chroma_format_idc = 3.
1024 // loop 1 of 12
1025 // true to enter if statement
1026 writer.write_bit(true).unwrap();
1027 // i < 6, so size is 16, so we loop 16 times
1028 // sub-loop 1 of 16
1029 // delta_scale is a SIGNED expg so we can try out
1030 // entering -4 so next_scale becomes 8 + 4 = 12
1031 writer.write_signed_exp_golomb(4).unwrap();
1032 // sub-loop 2 of 16
1033 // delta_scale is a SIGNED expg so we can try out
1034 // entering -12 so next scale becomes 12 - 12 = 0
1035 writer.write_signed_exp_golomb(-12).unwrap();
1036 // at this point next_scale is 0, which means we break
1037 // loop 2 through 12
1038 // we don't need to try anything else so we can just skip through them by writing `0` bit 11 times.
1039 writer.write_bits(0, 11).unwrap();
1040
1041 // back to sps
1042 // log2_max_frame_num_minus4 is expg
1043 writer.write_exp_golomb(0).unwrap();
1044 // we can try setting pic_order_cnt_type to 1
1045 // pic_order_cnt_type is expg
1046 writer.write_exp_golomb(1).unwrap();
1047
1048 // delta_pic_order_always_zero_flag
1049 writer.write_bit(false).unwrap();
1050 // offset_for_non_ref_pic
1051 writer.write_bit(true).unwrap();
1052 // offset_for_top_to_bottom_field
1053 writer.write_bit(true).unwrap();
1054 // num_ref_frames_in_pic_order_cnt_cycle is expg
1055 writer.write_exp_golomb(1).unwrap();
1056 // loop num_ref_frames_in_pic_order_cnt_cycle times (1)
1057 // offset_for_ref_frame is expg
1058 writer.write_exp_golomb(0).unwrap();
1059
1060 // max_num_ref_frames is expg
1061 writer.write_exp_golomb(0).unwrap();
1062 // gaps_in_frame_num_value_allowed_flag
1063 writer.write_bit(false).unwrap();
1064 // 1920 width:
1065 // 1920 = (p + 1) * 16 - 2 * offset1 - 2 * offset2
1066 // we set offset1 and offset2 to both be 4 later
1067 // 1920 = (p + 1) * 16 - 2 * 4 - 2 * 4
1068 // 1920 = (p + 1) * 16 - 16
1069 // p = 120
1070 // pic_width_in_mbs_minus1 is expg
1071 writer.write_exp_golomb(120).unwrap();
1072 // we want 1080 height:
1073 // 1080 = ((2 - m) * (p + 1) * 16) - 2 * offset1 - 2 * offset2
1074 // we set offset1 and offset2 to both be 2 later
1075 // m is frame_mbs_only_flag which we set to 0 later
1076 // 1080 = (2 - 0) * (p + 1) * 16 - 2 * 2 - 2 * 2
1077 // 1080 = 2 * (p + 1) * 16 - 8
1078 // p = 33
1079 // pic_height_in_map_units_minus1 is expg
1080 writer.write_exp_golomb(33).unwrap();
1081
1082 // frame_mbs_only_flag
1083 writer.write_bit(false).unwrap();
1084 // mb_adaptive_frame_field_flag
1085 writer.write_bit(false).unwrap();
1086
1087 // direct_8x8_inference_flag
1088 writer.write_bit(false).unwrap();
1089 // frame_cropping_flag
1090 writer.write_bit(true).unwrap();
1091
1092 // frame_crop_left_offset is expg
1093 writer.write_exp_golomb(4).unwrap();
1094 // frame_crop_left_offset is expg
1095 writer.write_exp_golomb(4).unwrap();
1096 // frame_crop_left_offset is expg
1097 writer.write_exp_golomb(2).unwrap();
1098 // frame_crop_left_offset is expg
1099 writer.write_exp_golomb(2).unwrap();
1100
1101 // vui_parameters_present_flag
1102 writer.write_bit(true).unwrap();
1103
1104 // enter vui to set the framerate
1105 // aspect_ratio_info_present_flag
1106 writer.write_bit(true).unwrap();
1107 // we can try 255 to set the sar_width and sar_height
1108 // aspect_ratio_idc
1109 writer.write_bits(255, 8).unwrap();
1110 // sar_width
1111 writer.write_bits(0, 16).unwrap();
1112 // sar_height
1113 writer.write_bits(0, 16).unwrap();
1114
1115 // overscan_info_present_flag
1116 writer.write_bit(false).unwrap();
1117
1118 // video_signal_type_present_flag
1119 writer.write_bit(true).unwrap();
1120 // video_format
1121 writer.write_bits(0, 3).unwrap();
1122 // video_full_range_flag
1123 writer.write_bit(false).unwrap();
1124 // color_description_present_flag
1125 writer.write_bit(true).unwrap();
1126 // color_primaries
1127 writer.write_bits(1, 8).unwrap();
1128 // transfer_characteristics
1129 writer.write_bits(1, 8).unwrap();
1130 // matrix_coefficients
1131 writer.write_bits(1, 8).unwrap();
1132
1133 // chroma_loc_info_present_flag
1134 writer.write_bit(false).unwrap();
1135
1136 // timing_info_present_flag
1137 writer.write_bit(true).unwrap();
1138 // we can set this to 1000 for example
1139 // num_units_in_tick is a u32
1140 writer.write_bits(1000, 32).unwrap();
1141 // fps = time_scale / (2 * num_units_in_tick)
1142 // since we want 480 fps:
1143 // 480 = time_scale / (2 * 1000)
1144 // 960 000 = time_scale
1145 // time_scale is a u32
1146 writer.write_bits(960000, 32).unwrap();
1147 writer.finish().unwrap();
1148
1149 let result = Sps::parse(std::io::Cursor::new(&sps)).unwrap();
1150
1151 insta::assert_debug_snapshot!(result, @r"
1152 Sps {
1153 nal_ref_idc: 0,
1154 nal_unit_type: NALUnitType::SPS,
1155 profile_idc: 44,
1156 constraint_set0_flag: false,
1157 constraint_set1_flag: false,
1158 constraint_set2_flag: false,
1159 constraint_set3_flag: false,
1160 constraint_set4_flag: false,
1161 constraint_set5_flag: false,
1162 level_idc: 0,
1163 seq_parameter_set_id: 0,
1164 ext: Some(
1165 SpsExtended {
1166 chroma_format_idc: 3,
1167 separate_color_plane_flag: false,
1168 bit_depth_luma_minus8: 0,
1169 bit_depth_chroma_minus8: 0,
1170 qpprime_y_zero_transform_bypass_flag: false,
1171 scaling_matrix: [
1172 [
1173 4,
1174 -12,
1175 ],
1176 [],
1177 [],
1178 [],
1179 [],
1180 [],
1181 [],
1182 [],
1183 [],
1184 [],
1185 [],
1186 [],
1187 ],
1188 },
1189 ),
1190 log2_max_frame_num_minus4: 0,
1191 pic_order_cnt_type: 1,
1192 log2_max_pic_order_cnt_lsb_minus4: None,
1193 pic_order_cnt_type1: Some(
1194 PicOrderCountType1 {
1195 delta_pic_order_always_zero_flag: false,
1196 offset_for_non_ref_pic: 0,
1197 offset_for_top_to_bottom_field: 0,
1198 num_ref_frames_in_pic_order_cnt_cycle: 1,
1199 offset_for_ref_frame: [
1200 0,
1201 ],
1202 },
1203 ),
1204 max_num_ref_frames: 0,
1205 gaps_in_frame_num_value_allowed_flag: false,
1206 pic_width_in_mbs_minus1: 120,
1207 pic_height_in_map_units_minus1: 33,
1208 mb_adaptive_frame_field_flag: Some(
1209 false,
1210 ),
1211 direct_8x8_inference_flag: false,
1212 frame_crop_info: Some(
1213 FrameCropInfo {
1214 frame_crop_left_offset: 4,
1215 frame_crop_right_offset: 4,
1216 frame_crop_top_offset: 2,
1217 frame_crop_bottom_offset: 2,
1218 },
1219 ),
1220 sample_aspect_ratio: Some(
1221 SarDimensions {
1222 aspect_ratio_idc: AspectRatioIdc::ExtendedSar,
1223 sar_width: 0,
1224 sar_height: 0,
1225 },
1226 ),
1227 overscan_appropriate_flag: None,
1228 color_config: Some(
1229 ColorConfig {
1230 video_format: VideoFormat::Component,
1231 video_full_range_flag: false,
1232 color_primaries: 1,
1233 transfer_characteristics: 1,
1234 matrix_coefficients: 1,
1235 },
1236 ),
1237 chroma_sample_loc: None,
1238 timing_info: Some(
1239 TimingInfo {
1240 num_units_in_tick: 1000,
1241 time_scale: 960000,
1242 },
1243 ),
1244 }
1245 ");
1246
1247 assert_eq!(Some(480.0), result.frame_rate());
1248 assert_eq!(1920, result.width());
1249 assert_eq!(1080, result.height());
1250
1251 // create a writer for the builder
1252 let mut buf = Vec::new();
1253 result.build(&mut buf).unwrap();
1254
1255 assert_eq!(buf, sps);
1256 }
1257
1258 #[test]
1259 fn test_parse_build_sps_1280x800_0fps() {
1260 let mut sps = Vec::new();
1261 let mut writer = BitWriter::new(&mut sps);
1262
1263 // forbidden zero bit must be unset
1264 writer.write_bit(false).unwrap();
1265 // nal_ref_idc is 0
1266 writer.write_bits(0, 2).unwrap();
1267 // nal_unit_type must be 7
1268 writer.write_bits(7, 5).unwrap();
1269
1270 // profile_idc = 77
1271 writer.write_bits(77, 8).unwrap();
1272 // constraint_setn_flags all false
1273 writer.write_bits(0, 8).unwrap();
1274 // level_idc = 0
1275 writer.write_bits(0, 8).unwrap();
1276
1277 // seq_parameter_set_id is expg
1278 writer.write_exp_golomb(0).unwrap();
1279
1280 // profile_idc = 77 means we skip the sps_ext
1281 // log2_max_frame_num_minus4 is expg
1282 writer.write_exp_golomb(0).unwrap();
1283 // pic_order_cnt_type is expg
1284 writer.write_exp_golomb(0).unwrap();
1285 // log2_max_pic_order_cnt_lsb_minus4
1286 writer.write_exp_golomb(0).unwrap();
1287
1288 // max_num_ref_frames is expg
1289 writer.write_exp_golomb(0).unwrap();
1290 // gaps_in_frame_num_value_allowed_flag
1291 writer.write_bit(false).unwrap();
1292 // 1280 width:
1293 // 1280 = (p + 1) * 16 - 2 * offset1 - 2 * offset2
1294 // we set offset1 and offset2 to both be 0 later
1295 // 1280 = (p + 1) * 16
1296 // p = 79
1297 writer.write_exp_golomb(79).unwrap();
1298 // we want 800 height:
1299 // 800 = ((2 - m) * (p + 1) * 16) - 2 * offset1 - 2 * offset2
1300 // we set offset1 and offset2 to both be 0 later
1301 // m is frame_mbs_only_flag which we set to 1 later
1302 // 800 = (2 - 1) * (p + 1) * 16 - 2 * 0 - 2 * 0
1303 // 800 = (p + 1) * 16
1304 // p = 49
1305 writer.write_exp_golomb(49).unwrap();
1306
1307 // frame_mbs_only_flag
1308 writer.write_bit(true).unwrap();
1309
1310 // direct_8x8_inference_flag
1311 writer.write_bit(false).unwrap();
1312 // frame_cropping_flag
1313 writer.write_bit(false).unwrap();
1314
1315 // vui_parameters_present_flag
1316 writer.write_bit(true).unwrap();
1317
1318 // enter vui to set the framerate
1319 // aspect_ratio_info_present_flag
1320 writer.write_bit(false).unwrap();
1321
1322 // overscan_info_present_flag
1323 writer.write_bit(false).unwrap();
1324
1325 // video_signal_type_present_flag
1326 writer.write_bit(true).unwrap();
1327 // video_format
1328 writer.write_bits(0, 3).unwrap();
1329 // video_full_range_flag
1330 writer.write_bit(false).unwrap();
1331 // color_description_present_flag
1332 writer.write_bit(false).unwrap();
1333
1334 // chroma_loc_info_present_flag
1335 writer.write_bit(true).unwrap();
1336 // chroma_sample_loc_type_top_field is expg
1337 writer.write_exp_golomb(2).unwrap();
1338 // chroma_sample_loc_type_bottom_field is expg
1339 writer.write_exp_golomb(2).unwrap();
1340
1341 // timing_info_present_flag
1342 writer.write_bit(false).unwrap();
1343 writer.finish().unwrap();
1344
1345 let result = Sps::parse(std::io::Cursor::new(&sps)).unwrap();
1346
1347 insta::assert_debug_snapshot!(result, @r"
1348 Sps {
1349 nal_ref_idc: 0,
1350 nal_unit_type: NALUnitType::SPS,
1351 profile_idc: 77,
1352 constraint_set0_flag: false,
1353 constraint_set1_flag: false,
1354 constraint_set2_flag: false,
1355 constraint_set3_flag: false,
1356 constraint_set4_flag: false,
1357 constraint_set5_flag: false,
1358 level_idc: 0,
1359 seq_parameter_set_id: 0,
1360 ext: None,
1361 log2_max_frame_num_minus4: 0,
1362 pic_order_cnt_type: 0,
1363 log2_max_pic_order_cnt_lsb_minus4: Some(
1364 0,
1365 ),
1366 pic_order_cnt_type1: None,
1367 max_num_ref_frames: 0,
1368 gaps_in_frame_num_value_allowed_flag: false,
1369 pic_width_in_mbs_minus1: 79,
1370 pic_height_in_map_units_minus1: 49,
1371 mb_adaptive_frame_field_flag: None,
1372 direct_8x8_inference_flag: false,
1373 frame_crop_info: None,
1374 sample_aspect_ratio: None,
1375 overscan_appropriate_flag: None,
1376 color_config: Some(
1377 ColorConfig {
1378 video_format: VideoFormat::Component,
1379 video_full_range_flag: false,
1380 color_primaries: 2,
1381 transfer_characteristics: 2,
1382 matrix_coefficients: 2,
1383 },
1384 ),
1385 chroma_sample_loc: Some(
1386 ChromaSampleLoc {
1387 chroma_sample_loc_type_top_field: 2,
1388 chroma_sample_loc_type_bottom_field: 2,
1389 },
1390 ),
1391 timing_info: None,
1392 }
1393 ");
1394
1395 assert_eq!(None, result.frame_rate());
1396 assert_eq!(1280, result.width());
1397 assert_eq!(800, result.height());
1398
1399 // create a writer for the builder
1400 let mut buf = Vec::new();
1401 result.build(&mut buf).unwrap();
1402
1403 assert_eq!(buf, sps);
1404 }
1405
1406 #[test]
1407 fn test_parse_build_sps_pic_order_cnt_type_2() {
1408 let mut sps = Vec::new();
1409 let mut writer = BitWriter::new(&mut sps);
1410
1411 // forbidden zero bit must be unset
1412 writer.write_bit(false).unwrap();
1413 // nal_ref_idc is 0
1414 writer.write_bits(0, 2).unwrap();
1415 // nal_unit_type must be 7
1416 writer.write_bits(7, 5).unwrap();
1417
1418 // profile_idc = 77
1419 writer.write_bits(77, 8).unwrap();
1420 // constraint_setn_flags all false
1421 writer.write_bits(0, 8).unwrap();
1422 // level_idc = 0
1423 writer.write_bits(0, 8).unwrap();
1424
1425 // seq_parameter_set_id is expg
1426 writer.write_exp_golomb(0).unwrap();
1427
1428 // profile_idc = 77 means we skip the sps_ext
1429 // log2_max_frame_num_minus4 is expg
1430 writer.write_exp_golomb(0).unwrap();
1431 // pic_order_cnt_type is expg
1432 writer.write_exp_golomb(2).unwrap();
1433 // log2_max_pic_order_cnt_lsb_minus4
1434 writer.write_exp_golomb(0).unwrap();
1435
1436 // max_num_ref_frames is expg
1437 writer.write_exp_golomb(0).unwrap();
1438 // gaps_in_frame_num_value_allowed_flag
1439 writer.write_bit(false).unwrap();
1440 writer.write_exp_golomb(1).unwrap();
1441 writer.write_exp_golomb(2).unwrap();
1442
1443 // frame_mbs_only_flag
1444 writer.write_bit(true).unwrap();
1445
1446 // direct_8x8_inference_flag
1447 writer.write_bit(false).unwrap();
1448 // frame_cropping_flag
1449 writer.write_bit(false).unwrap();
1450
1451 // enter vui to set redundant parameters so they get reduced
1452 // vui_parameters_present_flag
1453 writer.write_bit(false).unwrap();
1454 writer.finish().unwrap();
1455
1456 let result = Sps::parse(std::io::Cursor::new(&sps)).unwrap();
1457
1458 insta::assert_debug_snapshot!(result, @r"
1459 Sps {
1460 nal_ref_idc: 0,
1461 nal_unit_type: NALUnitType::SPS,
1462 profile_idc: 77,
1463 constraint_set0_flag: false,
1464 constraint_set1_flag: false,
1465 constraint_set2_flag: false,
1466 constraint_set3_flag: false,
1467 constraint_set4_flag: false,
1468 constraint_set5_flag: false,
1469 level_idc: 0,
1470 seq_parameter_set_id: 0,
1471 ext: None,
1472 log2_max_frame_num_minus4: 0,
1473 pic_order_cnt_type: 2,
1474 log2_max_pic_order_cnt_lsb_minus4: None,
1475 pic_order_cnt_type1: None,
1476 max_num_ref_frames: 0,
1477 gaps_in_frame_num_value_allowed_flag: true,
1478 pic_width_in_mbs_minus1: 3,
1479 pic_height_in_map_units_minus1: 0,
1480 mb_adaptive_frame_field_flag: None,
1481 direct_8x8_inference_flag: true,
1482 frame_crop_info: None,
1483 sample_aspect_ratio: None,
1484 overscan_appropriate_flag: None,
1485 color_config: None,
1486 chroma_sample_loc: None,
1487 timing_info: None,
1488 }
1489 ");
1490
1491 assert_eq!(None, result.frame_rate());
1492 assert_eq!(result.size(), 7);
1493
1494 // create a writer for the builder
1495 let mut buf = Vec::new();
1496 result.build_with_emulation_prevention(&mut buf).unwrap();
1497
1498 assert_eq!(buf, sps);
1499 }
1500
1501 #[test]
1502 fn test_parse_sps_chroma_loc_info_error() {
1503 let mut sps = Vec::new();
1504 let mut writer = BitWriter::new(&mut sps);
1505
1506 // forbidden zero bit must be unset
1507 writer.write_bit(false).unwrap();
1508 // nal_ref_idc is 0
1509 writer.write_bits(0, 2).unwrap();
1510 // nal_unit_type must be 7
1511 writer.write_bits(7, 5).unwrap();
1512
1513 // profile_idc = 100
1514 writer.write_bits(100, 8).unwrap();
1515 // constraint_setn_flags all false
1516 writer.write_bits(0, 8).unwrap();
1517 // level_idc = 0
1518 writer.write_bits(0, 8).unwrap();
1519
1520 // seq_parameter_set_id is expg
1521 writer.write_exp_golomb(0).unwrap();
1522
1523 // ext
1524 // chroma_format_idc is expg
1525 writer.write_exp_golomb(0).unwrap();
1526 // bit_depth_luma_minus8 is expg
1527 writer.write_exp_golomb(0).unwrap();
1528 // bit_depth_chroma_minus8 is expg
1529 writer.write_exp_golomb(0).unwrap();
1530 // qpprime
1531 writer.write_bit(false).unwrap();
1532 // seq_scaling_matrix_present_flag
1533 writer.write_bit(false).unwrap();
1534
1535 // return to sps
1536 // log2_max_frame_num_minus4 is expg
1537 writer.write_exp_golomb(0).unwrap();
1538 // pic_order_cnt_type is expg
1539 writer.write_exp_golomb(0).unwrap();
1540 // log2_max_pic_order_cnt_lsb_minus4
1541 writer.write_exp_golomb(0).unwrap();
1542
1543 // max_num_ref_frames is expg
1544 writer.write_exp_golomb(0).unwrap();
1545 // gaps_in_frame_num_value_allowed_flag
1546 writer.write_bit(false).unwrap();
1547 // 1280 width:
1548 // 1280 = (p + 1) * 16 - 2 * offset1 - 2 * offset2
1549 // we set offset1 and offset2 to both be 0 later
1550 // 1280 = (p + 1) * 16
1551 // p = 79
1552 writer.write_exp_golomb(79).unwrap();
1553 // we want 800 height:
1554 // 800 = ((2 - m) * (p + 1) * 16) - 2 * offset1 - 2 * offset2
1555 // we set offset1 and offset2 to both be 0 later
1556 // m is frame_mbs_only_flag which we set to 1 later
1557 // 800 = (2 - 1) * (p + 1) * 16 - 2 * 0 - 2 * 0
1558 // 800 = 2 * (p + 1) * 16 - 8
1559 // p = 33
1560 writer.write_exp_golomb(33).unwrap();
1561
1562 // frame_mbs_only_flag
1563 writer.write_bit(false).unwrap();
1564 // mb_adaptive_frame_field_flag
1565 writer.write_bit(false).unwrap();
1566
1567 // direct_8x8_inference_flag
1568 writer.write_bit(false).unwrap();
1569 // frame_cropping_flag
1570 writer.write_bit(false).unwrap();
1571
1572 // vui_parameters_present_flag
1573 writer.write_bit(true).unwrap();
1574
1575 // enter vui to set the framerate
1576 // aspect_ratio_info_present_flag
1577 writer.write_bit(false).unwrap();
1578
1579 // overscan_info_present_flag
1580 writer.write_bit(false).unwrap();
1581
1582 // video_signal_type_present_flag
1583 writer.write_bit(true).unwrap();
1584 // video_format
1585 writer.write_bits(0, 3).unwrap();
1586 // video_full_range_flag
1587 writer.write_bit(false).unwrap();
1588 // color_description_present_flag
1589 writer.write_bit(false).unwrap();
1590
1591 // chroma_loc_info_present_flag
1592 writer.write_bit(true).unwrap();
1593 writer.finish().unwrap();
1594
1595 let result = Sps::parse(std::io::Cursor::new(&sps));
1596
1597 assert!(result.is_err());
1598 let err = result.unwrap_err();
1599 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
1600 assert_eq!(
1601 err.to_string(),
1602 "chroma_loc_info_present_flag cannot be set to 1 when chroma_format_idc is not 1"
1603 );
1604 }
1605
1606 #[test]
1607 fn test_invalid_num_units_in_tick() {
1608 let mut sps = Vec::new();
1609 let mut writer = BitWriter::new(&mut sps);
1610
1611 // forbidden zero bit must be unset
1612 writer.write_bit(false).unwrap();
1613 // nal_ref_idc is 0
1614 writer.write_bits(0, 2).unwrap();
1615 // nal_unit_type must be 7
1616 writer.write_bits(7, 5).unwrap();
1617
1618 // profile_idc = 100
1619 writer.write_bits(100, 8).unwrap();
1620 // constraint_setn_flags all false
1621 writer.write_bits(0, 8).unwrap();
1622 // level_idc = 0
1623 writer.write_bits(0, 8).unwrap();
1624
1625 // seq_parameter_set_id is expg
1626 writer.write_exp_golomb(0).unwrap();
1627
1628 // ext
1629 // chroma_format_idc is expg
1630 writer.write_exp_golomb(0).unwrap();
1631 // bit_depth_luma_minus8 is expg
1632 writer.write_exp_golomb(0).unwrap();
1633 // bit_depth_chroma_minus8 is expg
1634 writer.write_exp_golomb(0).unwrap();
1635 // qpprime
1636 writer.write_bit(false).unwrap();
1637 // seq_scaling_matrix_present_flag
1638 writer.write_bit(false).unwrap();
1639
1640 // return to sps
1641 // log2_max_frame_num_minus4 is expg
1642 writer.write_exp_golomb(0).unwrap();
1643 // pic_order_cnt_type is expg
1644 writer.write_exp_golomb(0).unwrap();
1645 // log2_max_pic_order_cnt_lsb_minus4
1646 writer.write_exp_golomb(0).unwrap();
1647
1648 // max_num_ref_frames is expg
1649 writer.write_exp_golomb(0).unwrap();
1650 // gaps_in_frame_num_value_allowed_flag
1651 writer.write_bit(false).unwrap();
1652 // 1280 width:
1653 // 1280 = (p + 1) * 16 - 2 * offset1 - 2 * offset2
1654 // we set offset1 and offset2 to both be 0 later
1655 // 1280 = (p + 1) * 16
1656 // p = 79
1657 writer.write_exp_golomb(79).unwrap();
1658 // we want 800 height:
1659 // 800 = ((2 - m) * (p + 1) * 16) - 2 * offset1 - 2 * offset2
1660 // we set offset1 and offset2 to both be 0 later
1661 // m is frame_mbs_only_flag which we set to 1 later
1662 // 800 = (2 - 1) * (p + 1) * 16 - 2 * 0 - 2 * 0
1663 // 800 = 2 * (p + 1) * 16 - 8
1664 // p = 33
1665 writer.write_exp_golomb(33).unwrap();
1666
1667 // frame_mbs_only_flag
1668 writer.write_bit(false).unwrap();
1669 // mb_adaptive_frame_field_flag
1670 writer.write_bit(false).unwrap();
1671
1672 // direct_8x8_inference_flag
1673 writer.write_bit(false).unwrap();
1674 // frame_cropping_flag
1675 writer.write_bit(false).unwrap();
1676
1677 // vui_parameters_present_flag
1678 writer.write_bit(true).unwrap();
1679
1680 // enter vui to set the framerate
1681 // aspect_ratio_info_present_flag
1682 writer.write_bit(false).unwrap();
1683
1684 // overscan_info_present_flag
1685 writer.write_bit(false).unwrap();
1686
1687 // video_signal_type_present_flag
1688 writer.write_bit(true).unwrap();
1689 // video_format
1690 writer.write_bits(0, 3).unwrap();
1691 // video_full_range_flag
1692 writer.write_bit(false).unwrap();
1693 // color_description_present_flag
1694 writer.write_bit(false).unwrap();
1695
1696 // chroma_loc_info_present_flag
1697 writer.write_bit(false).unwrap();
1698
1699 // timing_info_present_flag
1700 writer.write_bit(true).unwrap();
1701 // num_units_in_tick to 0 (invalid)
1702 writer.write_bits(0, 32).unwrap();
1703 writer.finish().unwrap();
1704
1705 let result = Sps::parse(std::io::Cursor::new(&sps));
1706
1707 assert!(result.is_err());
1708 let err = result.unwrap_err();
1709 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
1710 assert_eq!(err.to_string(), "num_units_in_tick cannot be 0");
1711 }
1712
1713 #[test]
1714 fn test_invalid_time_scale() {
1715 let mut sps = Vec::new();
1716 let mut writer = BitWriter::new(&mut sps);
1717
1718 // forbidden zero bit must be unset
1719 writer.write_bit(false).unwrap();
1720 // nal_ref_idc is 0
1721 writer.write_bits(0, 2).unwrap();
1722 // nal_unit_type must be 7
1723 writer.write_bits(7, 5).unwrap();
1724
1725 // profile_idc = 100
1726 writer.write_bits(100, 8).unwrap();
1727 // constraint_setn_flags all false
1728 writer.write_bits(0, 8).unwrap();
1729 // level_idc = 0
1730 writer.write_bits(0, 8).unwrap();
1731
1732 // seq_parameter_set_id is expg
1733 writer.write_exp_golomb(0).unwrap();
1734
1735 // ext
1736 // chroma_format_idc is expg
1737 writer.write_exp_golomb(0).unwrap();
1738 // bit_depth_luma_minus8 is expg
1739 writer.write_exp_golomb(0).unwrap();
1740 // bit_depth_chroma_minus8 is expg
1741 writer.write_exp_golomb(0).unwrap();
1742 // qpprime
1743 writer.write_bit(false).unwrap();
1744 // seq_scaling_matrix_present_flag
1745 writer.write_bit(false).unwrap();
1746
1747 // return to sps
1748 // log2_max_frame_num_minus4 is expg
1749 writer.write_exp_golomb(0).unwrap();
1750 // pic_order_cnt_type is expg
1751 writer.write_exp_golomb(0).unwrap();
1752 // log2_max_pic_order_cnt_lsb_minus4
1753 writer.write_exp_golomb(0).unwrap();
1754
1755 // max_num_ref_frames is expg
1756 writer.write_exp_golomb(0).unwrap();
1757 // gaps_in_frame_num_value_allowed_flag
1758 writer.write_bit(false).unwrap();
1759 // 1280 width:
1760 // 1280 = (p + 1) * 16 - 2 * offset1 - 2 * offset2
1761 // we set offset1 and offset2 to both be 0 later
1762 // 1280 = (p + 1) * 16
1763 // p = 79
1764 writer.write_exp_golomb(79).unwrap();
1765 // we want 800 height:
1766 // 800 = ((2 - m) * (p + 1) * 16) - 2 * offset1 - 2 * offset2
1767 // we set offset1 and offset2 to both be 0 later
1768 // m is frame_mbs_only_flag which we set to 1 later
1769 // 800 = (2 - 1) * (p + 1) * 16 - 2 * 0 - 2 * 0
1770 // 800 = 2 * (p + 1) * 16 - 8
1771 // p = 33
1772 writer.write_exp_golomb(33).unwrap();
1773
1774 // frame_mbs_only_flag
1775 writer.write_bit(false).unwrap();
1776 // mb_adaptive_frame_field_flag
1777 writer.write_bit(false).unwrap();
1778
1779 // direct_8x8_inference_flag
1780 writer.write_bit(false).unwrap();
1781 // frame_cropping_flag
1782 writer.write_bit(false).unwrap();
1783
1784 // vui_parameters_present_flag
1785 writer.write_bit(true).unwrap();
1786
1787 // enter vui to set the framerate
1788 // aspect_ratio_info_present_flag
1789 writer.write_bit(false).unwrap();
1790
1791 // overscan_info_present_flag
1792 writer.write_bit(false).unwrap();
1793
1794 // video_signal_type_present_flag
1795 writer.write_bit(true).unwrap();
1796 // video_format
1797 writer.write_bits(0, 3).unwrap();
1798 // video_full_range_flag
1799 writer.write_bit(false).unwrap();
1800 // color_description_present_flag
1801 writer.write_bit(false).unwrap();
1802
1803 // chroma_loc_info_present_flag
1804 writer.write_bit(false).unwrap();
1805
1806 // timing_info_present_flag
1807 writer.write_bit(true).unwrap();
1808 // num_units_in_tick to 0 (invalid)
1809 writer.write_bits(0, 32).unwrap();
1810 writer.finish().unwrap();
1811
1812 let result = Sps::parse(std::io::Cursor::new(&sps));
1813
1814 assert!(result.is_err());
1815 let err = result.unwrap_err();
1816 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
1817 assert_eq!(err.to_string(), "num_units_in_tick cannot be 0");
1818 }
1819
1820 #[test]
1821 fn test_parse_build_sps_no_vui() {
1822 let mut sps = Vec::new();
1823 let mut writer = BitWriter::new(&mut sps);
1824
1825 // forbidden zero bit must be unset
1826 writer.write_bit(false).unwrap();
1827 // nal_ref_idc is 0
1828 writer.write_bits(0, 2).unwrap();
1829 // nal_unit_type must be 7
1830 writer.write_bits(7, 5).unwrap();
1831
1832 // profile_idc = 77
1833 writer.write_bits(77, 8).unwrap();
1834 // constraint_setn_flags all false
1835 writer.write_bits(0, 8).unwrap();
1836 // level_idc = 0
1837 writer.write_bits(0, 8).unwrap();
1838
1839 // seq_parameter_set_id is expg so 0b1 (true) = false
1840 writer.write_exp_golomb(0).unwrap();
1841
1842 // skip sps ext since profile_idc = 77
1843 // log2_max_frame_num_minus4 is expg so 0b1 (true) = false
1844 writer.write_exp_golomb(0).unwrap();
1845 // we can try setting pic_order_cnt_type to 1
1846 writer.write_exp_golomb(1).unwrap();
1847
1848 // delta_pic_order_always_zero_flag
1849 writer.write_bit(false).unwrap();
1850 // offset_for_non_ref_pic
1851 writer.write_bit(true).unwrap();
1852 // offset_for_top_to_bottom_field
1853 writer.write_bit(true).unwrap();
1854 // num_ref_frames_in_pic_order_cnt_cycle is expg so 0b010 = 1
1855 writer.write_bits(0b010, 3).unwrap();
1856 // loop num_ref_frames_in_pic_order_cnt_cycle times (1)
1857 // offset_for_ref_frame is expg so 0b1 (true) = false
1858 writer.write_bit(true).unwrap();
1859
1860 // max_num_ref_frames is expg so 0b1 (true) = false
1861 writer.write_bit(true).unwrap();
1862 // gaps_in_frame_num_value_allowed_flag
1863 writer.write_bit(false).unwrap();
1864 // 1920 width:
1865 // 1920 = (p + 1) * 16 - 2 * offset1 - 2 * offset2
1866 // we set offset1 and offset2 to both be 4 later
1867 // 1920 = (p + 1) * 16 - 2 * 4 - 2 * 4
1868 // 1920 = (p + 1) * 16 - 16
1869 // p = 120
1870 // pic_width_in_mbs_minus1 is expg so:
1871 // 0 0000 0111 1001
1872 writer.write_exp_golomb(999).unwrap();
1873 // we want 1080 height:
1874 // 1080 = ((2 - m) * (p + 1) * 16) - 2 * offset1 - 2 * offset2
1875 // we set offset1 and offset2 to both be 2 later
1876 // m is frame_mbs_only_flag which we set to 0 later
1877 // 1080 = (2 - 0) * (p + 1) * 16 - 2 * 2 - 2 * 2
1878 // 1080 = 2 * (p + 1) * 16 - 8
1879 // p = 33
1880 // pic_height_in_map_units_minus1 is expg so:
1881 // 000 0010 0010
1882 writer.write_exp_golomb(899).unwrap();
1883
1884 // frame_mbs_only_flag
1885 writer.write_bit(false).unwrap();
1886 // mb_adaptive_frame_field_flag
1887 writer.write_bit(false).unwrap();
1888
1889 // direct_8x8_inference_flag
1890 writer.write_bit(false).unwrap();
1891 // frame_cropping_flag
1892 writer.write_bit(true).unwrap();
1893
1894 // frame_crop_left_offset is expg
1895 writer.write_exp_golomb(100).unwrap();
1896 // frame_crop_right_offset is expg
1897 writer.write_exp_golomb(200).unwrap();
1898 // frame_crop_top_offset is expg
1899 writer.write_exp_golomb(300).unwrap();
1900 // frame_crop_bottom_offset is expg
1901 writer.write_exp_golomb(400).unwrap();
1902
1903 // vui_parameters_present_flag
1904 writer.write_bit(false).unwrap();
1905 writer.finish().unwrap();
1906
1907 let result = Sps::parse(std::io::Cursor::new(&sps)).unwrap();
1908
1909 insta::assert_debug_snapshot!(result, @r"
1910 Sps {
1911 nal_ref_idc: 0,
1912 nal_unit_type: NALUnitType::SPS,
1913 profile_idc: 77,
1914 constraint_set0_flag: false,
1915 constraint_set1_flag: false,
1916 constraint_set2_flag: false,
1917 constraint_set3_flag: false,
1918 constraint_set4_flag: false,
1919 constraint_set5_flag: false,
1920 level_idc: 0,
1921 seq_parameter_set_id: 0,
1922 ext: None,
1923 log2_max_frame_num_minus4: 0,
1924 pic_order_cnt_type: 1,
1925 log2_max_pic_order_cnt_lsb_minus4: None,
1926 pic_order_cnt_type1: Some(
1927 PicOrderCountType1 {
1928 delta_pic_order_always_zero_flag: false,
1929 offset_for_non_ref_pic: 0,
1930 offset_for_top_to_bottom_field: 0,
1931 num_ref_frames_in_pic_order_cnt_cycle: 1,
1932 offset_for_ref_frame: [
1933 0,
1934 ],
1935 },
1936 ),
1937 max_num_ref_frames: 0,
1938 gaps_in_frame_num_value_allowed_flag: false,
1939 pic_width_in_mbs_minus1: 999,
1940 pic_height_in_map_units_minus1: 899,
1941 mb_adaptive_frame_field_flag: Some(
1942 false,
1943 ),
1944 direct_8x8_inference_flag: false,
1945 frame_crop_info: Some(
1946 FrameCropInfo {
1947 frame_crop_left_offset: 100,
1948 frame_crop_right_offset: 200,
1949 frame_crop_top_offset: 300,
1950 frame_crop_bottom_offset: 400,
1951 },
1952 ),
1953 sample_aspect_ratio: None,
1954 overscan_appropriate_flag: None,
1955 color_config: None,
1956 chroma_sample_loc: None,
1957 timing_info: None,
1958 }
1959 ");
1960
1961 // create a writer for the builder
1962 let mut buf = Vec::new();
1963 // build from the example sps
1964 result.build(&mut buf).unwrap();
1965
1966 assert_eq!(buf, sps);
1967 }
1968
1969 #[test]
1970 fn test_size_sps() {
1971 let mut bit_count = 0;
1972 let mut sps = Vec::new();
1973 let mut writer = BitWriter::new(&mut sps);
1974
1975 // forbidden zero bit must be unset
1976 writer.write_bit(false).unwrap();
1977 bit_count += 1;
1978 // nal_ref_idc is 0
1979 writer.write_bits(0, 2).unwrap();
1980 bit_count += 2;
1981 // nal_unit_type must be 7
1982 writer.write_bits(7, 5).unwrap();
1983 bit_count += 5;
1984
1985 // profile_idc = 44
1986 writer.write_bits(44, 8).unwrap();
1987 bit_count += 8;
1988 // constraint_setn_flags all false
1989 writer.write_bits(0, 8).unwrap();
1990 bit_count += 8;
1991 // level_idc = 0
1992 writer.write_bits(0, 8).unwrap();
1993 bit_count += 8;
1994 // seq_parameter_set_id is expg
1995 writer.write_exp_golomb(0).unwrap();
1996 bit_count += size_of_exp_golomb(0);
1997
1998 // sps ext
1999 // we want to try out chroma_format_idc = 3
2000 // chroma_format_idc is expg
2001 writer.write_exp_golomb(3).unwrap();
2002 bit_count += size_of_exp_golomb(3);
2003 // separate_color_plane_flag
2004 writer.write_bit(false).unwrap();
2005 bit_count += 1;
2006 // bit_depth_luma_minus8 is expg
2007 writer.write_exp_golomb(0).unwrap();
2008 bit_count += size_of_exp_golomb(0);
2009 // bit_depth_chroma_minus8 is expg
2010 writer.write_exp_golomb(0).unwrap();
2011 bit_count += size_of_exp_golomb(0);
2012 // qpprime
2013 writer.write_bit(false).unwrap();
2014 bit_count += 1;
2015 // we want to simulate a scaling matrix
2016 // seq_scaling_matrix_present_flag
2017 writer.write_bit(true).unwrap();
2018 bit_count += 1;
2019
2020 // enter scaling matrix, we loop 12 times since
2021 // chroma_format_idc = 3.
2022 // loop 1 of 12
2023 // true to enter if statement
2024 writer.write_bit(true).unwrap();
2025 bit_count += 1;
2026 // i < 6, so size is 16, so we loop 16 times
2027 // sub-loop 1 of 16
2028 // delta_scale is a SIGNED expg so we can try out
2029 // entering -4 so next_scale becomes 8 + 4 = 12
2030 writer.write_signed_exp_golomb(4).unwrap();
2031 bit_count += size_of_signed_exp_golomb(4);
2032 // sub-loop 2 of 16
2033 // delta_scale is a SIGNED expg so we can try out
2034 // entering -12 so next scale becomes 12 - 12 = 0
2035 writer.write_signed_exp_golomb(-12).unwrap();
2036 bit_count += size_of_signed_exp_golomb(-12);
2037 // at this point next_scale is 0, which means we break
2038 // loop 2 through 12
2039 // we don't need to try anything else so we can just skip through them by writing `0` bit 11 times.
2040 writer.write_bits(0, 11).unwrap();
2041 bit_count += 11;
2042
2043 // back to sps
2044 // log2_max_frame_num_minus4 is expg
2045 writer.write_exp_golomb(0).unwrap();
2046 bit_count += size_of_exp_golomb(0);
2047 // we can try setting pic_order_cnt_type to 1
2048 // pic_order_cnt_type is expg
2049 writer.write_exp_golomb(1).unwrap();
2050 bit_count += size_of_exp_golomb(1);
2051
2052 // delta_pic_order_always_zero_flag
2053 writer.write_bit(false).unwrap();
2054 bit_count += 1;
2055 // offset_for_non_ref_pic
2056 writer.write_bit(true).unwrap();
2057 bit_count += 1;
2058 // offset_for_top_to_bottom_field
2059 writer.write_bit(true).unwrap();
2060 bit_count += 1;
2061 // num_ref_frames_in_pic_order_cnt_cycle is expg
2062 writer.write_exp_golomb(1).unwrap();
2063 bit_count += size_of_exp_golomb(1);
2064 // loop num_ref_frames_in_pic_order_cnt_cycle times (1)
2065 // offset_for_ref_frame is expg
2066 writer.write_exp_golomb(0).unwrap();
2067 bit_count += size_of_exp_golomb(0);
2068
2069 // max_num_ref_frames is expg
2070 writer.write_exp_golomb(0).unwrap();
2071 bit_count += size_of_exp_golomb(0);
2072 // gaps_in_frame_num_value_allowed_flag
2073 writer.write_bit(false).unwrap();
2074 bit_count += 1;
2075 // 1920 width:
2076 // 1920 = (p + 1) * 16 - 2 * offset1 - 2 * offset2
2077 // we set offset1 and offset2 to both be 4 later
2078 // 1920 = (p + 1) * 16 - 2 * 4 - 2 * 4
2079 // 1920 = (p + 1) * 16 - 16
2080 // p = 120
2081 // pic_width_in_mbs_minus1 is expg
2082 writer.write_exp_golomb(120).unwrap();
2083 bit_count += size_of_exp_golomb(120);
2084 // we want 1080 height:
2085 // 1080 = ((2 - m) * (p + 1) * 16) - 2 * offset1 - 2 * offset2
2086 // we set offset1 and offset2 to both be 2 later
2087 // m is frame_mbs_only_flag which we set to 0 later
2088 // 1080 = (2 - 0) * (p + 1) * 16 - 2 * 2 - 2 * 2
2089 // 1080 = 2 * (p + 1) * 16 - 8
2090 // p = 33
2091 // pic_height_in_map_units_minus1 is expg
2092 writer.write_exp_golomb(33).unwrap();
2093 bit_count += size_of_exp_golomb(33);
2094
2095 // frame_mbs_only_flag
2096 writer.write_bit(false).unwrap();
2097 bit_count += 1;
2098 // mb_adaptive_frame_field_flag
2099 writer.write_bit(false).unwrap();
2100 bit_count += 1;
2101
2102 // direct_8x8_inference_flag
2103 writer.write_bit(false).unwrap();
2104 bit_count += 1;
2105 // frame_cropping_flag
2106 writer.write_bit(true).unwrap();
2107 bit_count += 1;
2108
2109 // frame_crop_left_offset is expg
2110 writer.write_exp_golomb(4).unwrap();
2111 bit_count += size_of_exp_golomb(4);
2112 // frame_crop_left_offset is expg
2113 writer.write_exp_golomb(4).unwrap();
2114 bit_count += size_of_exp_golomb(4);
2115 // frame_crop_left_offset is expg
2116 writer.write_exp_golomb(2).unwrap();
2117 bit_count += size_of_exp_golomb(2);
2118 // frame_crop_left_offset is expg
2119 writer.write_exp_golomb(2).unwrap();
2120 bit_count += size_of_exp_golomb(2);
2121
2122 // vui_parameters_present_flag
2123 writer.write_bit(true).unwrap();
2124 bit_count += 1;
2125
2126 // enter vui to set the framerate
2127 // aspect_ratio_info_present_flag
2128 writer.write_bit(true).unwrap();
2129 bit_count += 1;
2130 // we can try 255 to set the sar_width and sar_height
2131 // aspect_ratio_idc
2132 writer.write_bits(255, 8).unwrap();
2133 bit_count += 8;
2134 // sar_width
2135 writer.write_bits(0, 16).unwrap();
2136 bit_count += 16;
2137 // sar_height
2138 writer.write_bits(0, 16).unwrap();
2139 bit_count += 16;
2140
2141 // overscan_info_present_flag
2142 writer.write_bit(false).unwrap();
2143 bit_count += 1;
2144
2145 // video_signal_type_present_flag
2146 writer.write_bit(true).unwrap();
2147 bit_count += 1;
2148 // video_format
2149 writer.write_bits(0, 3).unwrap();
2150 bit_count += 3;
2151 // video_full_range_flag
2152 writer.write_bit(false).unwrap();
2153 bit_count += 1;
2154 // color_description_present_flag
2155 writer.write_bit(true).unwrap();
2156 bit_count += 1;
2157 // color_primaries
2158 writer.write_bits(1, 8).unwrap();
2159 bit_count += 8;
2160 // transfer_characteristics
2161 writer.write_bits(1, 8).unwrap();
2162 bit_count += 8;
2163 // matrix_coefficients
2164 writer.write_bits(1, 8).unwrap();
2165 bit_count += 8;
2166
2167 // chroma_loc_info_present_flag
2168 writer.write_bit(false).unwrap();
2169 bit_count += 1;
2170
2171 // timing_info_present_flag
2172 writer.write_bit(true).unwrap();
2173 bit_count += 1;
2174 // we can set this to 1000 for example
2175 // num_units_in_tick is a u32
2176 writer.write_bits(1000, 32).unwrap();
2177 bit_count += 32;
2178 // fps = time_scale / (2 * num_units_in_tick)
2179 // since we want 480 fps:
2180 // 480 = time_scale / (2 * 1000)
2181 // 960 000 = time_scale
2182 // time_scale is a u32
2183 writer.write_bits(960000, 32).unwrap();
2184 bit_count += 32;
2185 writer.finish().unwrap();
2186
2187 let result = Sps::parse(std::io::Cursor::new(&sps)).unwrap();
2188
2189 // now we can check the size:
2190 assert_eq!(result.size(), bit_count.div_ceil(8));
2191 }
2192
2193 #[test]
2194 fn test_reduce_color_config() {
2195 let mut sps = Vec::new();
2196 let mut writer = BitWriter::new(&mut sps);
2197
2198 // forbidden zero bit must be unset
2199 writer.write_bit(false).unwrap();
2200 // nal_ref_idc is 0
2201 writer.write_bits(0, 2).unwrap();
2202 // nal_unit_type must be 7
2203 writer.write_bits(7, 5).unwrap();
2204
2205 // profile_idc = 100
2206 writer.write_bits(100, 8).unwrap();
2207 // constraint_setn_flags all false
2208 writer.write_bits(0, 8).unwrap();
2209 // level_idc = 0
2210 writer.write_bits(0, 8).unwrap();
2211
2212 // seq_parameter_set_id is expg
2213 writer.write_exp_golomb(0).unwrap();
2214
2215 // sps ext
2216 // chroma_format_idc is expg
2217 writer.write_exp_golomb(0).unwrap();
2218 // bit_depth_luma_minus8 is expg
2219 writer.write_exp_golomb(0).unwrap();
2220 // bit_depth_chroma_minus8 is expg
2221 writer.write_exp_golomb(0).unwrap();
2222 // qpprime
2223 writer.write_bit(false).unwrap();
2224 // seq_scaling_matrix_present_flag
2225 writer.write_bit(false).unwrap();
2226
2227 // back to sps
2228 // log2_max_frame_num_minus4 is expg
2229 writer.write_exp_golomb(0).unwrap();
2230 // pic_order_cnt_type is expg
2231 writer.write_exp_golomb(0).unwrap();
2232 // log2_max_pic_order_cnt_lsb_minus4 is expg
2233 writer.write_exp_golomb(0).unwrap();
2234
2235 // max_num_ref_frames is expg
2236 writer.write_exp_golomb(0).unwrap();
2237 // gaps_in_frame_num_value_allowed_flag
2238 writer.write_bit(false).unwrap();
2239 // width
2240 writer.write_exp_golomb(0).unwrap();
2241 // height
2242 writer.write_exp_golomb(0).unwrap();
2243
2244 // frame_mbs_only_flag
2245 writer.write_bit(true).unwrap();
2246
2247 // direct_8x8_inference_flag
2248 writer.write_bit(false).unwrap();
2249 // frame_cropping_flag
2250 writer.write_bit(false).unwrap();
2251
2252 // vui_parameters_present_flag
2253 writer.write_bit(true).unwrap();
2254
2255 // aspect_ratio_info_present_flag
2256 writer.write_bit(false).unwrap();
2257
2258 // overscan_info_present_flag
2259 writer.write_bit(false).unwrap();
2260
2261 // we want to change the color_config
2262 // video_signal_type_present_flag
2263 writer.write_bit(true).unwrap();
2264
2265 // video_format
2266 writer.write_bits(1, 3).unwrap();
2267 // video_full_range_flag
2268 writer.write_bit(false).unwrap();
2269 // color_description_present_flag: we want this to be true
2270 writer.write_bit(true).unwrap();
2271
2272 // now we set these to redundant values (each should be 2)
2273 writer.write_bits(2, 8).unwrap();
2274 writer.write_bits(2, 8).unwrap();
2275 writer.write_bits(2, 8).unwrap();
2276
2277 // chroma_loc_info_present_flag
2278 writer.write_bit(false).unwrap();
2279
2280 // timing_info_present_flag
2281 writer.write_bit(false).unwrap();
2282 writer.finish().unwrap();
2283
2284 let reduced_sps = Sps::parse(std::io::Cursor::new(&sps)).unwrap();
2285
2286 let mut reduced_buf = Vec::new();
2287 reduced_sps.build(&mut reduced_buf).unwrap();
2288
2289 assert_ne!(sps, reduced_buf);
2290 }
2291
2292 #[test]
2293 fn test_reduce_vui() {
2294 let mut sps = Vec::new();
2295 let mut writer = BitWriter::new(&mut sps);
2296
2297 // forbidden zero bit must be unset
2298 writer.write_bit(false).unwrap();
2299 // nal_ref_idc is 0
2300 writer.write_bits(0, 2).unwrap();
2301 // nal_unit_type must be 7
2302 writer.write_bits(7, 5).unwrap();
2303
2304 // profile_idc = 100
2305 writer.write_bits(100, 8).unwrap();
2306 // constraint_setn_flags all false
2307 writer.write_bits(0, 8).unwrap();
2308 // level_idc = 0
2309 writer.write_bits(0, 8).unwrap();
2310
2311 // seq_parameter_set_id is expg
2312 writer.write_exp_golomb(0).unwrap();
2313
2314 // sps ext
2315 // chroma_format_idc is expg
2316 writer.write_exp_golomb(0).unwrap();
2317 // bit_depth_luma_minus8 is expg
2318 writer.write_exp_golomb(0).unwrap();
2319 // bit_depth_chroma_minus8 is expg
2320 writer.write_exp_golomb(0).unwrap();
2321 // qpprime
2322 writer.write_bit(false).unwrap();
2323 // seq_scaling_matrix_present_flag
2324 writer.write_bit(false).unwrap();
2325
2326 // back to sps
2327 // log2_max_frame_num_minus4 is expg
2328 writer.write_exp_golomb(0).unwrap();
2329 // pic_order_cnt_type is expg
2330 writer.write_exp_golomb(0).unwrap();
2331 // log2_max_pic_order_cnt_lsb_minus4 is expg
2332 writer.write_exp_golomb(0).unwrap();
2333
2334 // max_num_ref_frames is expg
2335 writer.write_exp_golomb(0).unwrap();
2336 // gaps_in_frame_num_value_allowed_flag
2337 writer.write_bit(false).unwrap();
2338 // width
2339 writer.write_exp_golomb(0).unwrap();
2340 // height
2341 writer.write_exp_golomb(0).unwrap();
2342
2343 // frame_mbs_only_flag
2344 writer.write_bit(true).unwrap();
2345
2346 // direct_8x8_inference_flag
2347 writer.write_bit(false).unwrap();
2348 // frame_cropping_flag
2349 writer.write_bit(false).unwrap();
2350
2351 // we want to set this flag to be true and all subsequent flags to be false.
2352 // vui_parameters_present_flag
2353 writer.write_bit(true).unwrap();
2354
2355 // aspect_ratio_info_present_flag
2356 writer.write_bit(false).unwrap();
2357
2358 // overscan_info_present_flag
2359 writer.write_bit(false).unwrap();
2360
2361 // video_signal_type_present_flag
2362 writer.write_bit(false).unwrap();
2363
2364 // chroma_loc_info_present_flag
2365 writer.write_bit(false).unwrap();
2366
2367 // timing_info_present_flag
2368 writer.write_bit(false).unwrap();
2369 writer.finish().unwrap();
2370
2371 let result = Sps::parse(std::io::Cursor::new(&sps)).unwrap();
2372
2373 let mut reduced_buf = Vec::new();
2374 result.build(&mut reduced_buf).unwrap();
2375
2376 let reduced_result = Sps::parse(std::io::Cursor::new(&reduced_buf)).unwrap();
2377 assert_eq!(result.size(), reduced_result.size());
2378
2379 insta::assert_debug_snapshot!(reduced_result, @r"
2380 Sps {
2381 nal_ref_idc: 0,
2382 nal_unit_type: NALUnitType::SPS,
2383 profile_idc: 100,
2384 constraint_set0_flag: false,
2385 constraint_set1_flag: false,
2386 constraint_set2_flag: false,
2387 constraint_set3_flag: false,
2388 constraint_set4_flag: false,
2389 constraint_set5_flag: false,
2390 level_idc: 0,
2391 seq_parameter_set_id: 0,
2392 ext: Some(
2393 SpsExtended {
2394 chroma_format_idc: 0,
2395 separate_color_plane_flag: false,
2396 bit_depth_luma_minus8: 0,
2397 bit_depth_chroma_minus8: 0,
2398 qpprime_y_zero_transform_bypass_flag: false,
2399 scaling_matrix: [],
2400 },
2401 ),
2402 log2_max_frame_num_minus4: 0,
2403 pic_order_cnt_type: 0,
2404 log2_max_pic_order_cnt_lsb_minus4: Some(
2405 0,
2406 ),
2407 pic_order_cnt_type1: None,
2408 max_num_ref_frames: 0,
2409 gaps_in_frame_num_value_allowed_flag: false,
2410 pic_width_in_mbs_minus1: 0,
2411 pic_height_in_map_units_minus1: 0,
2412 mb_adaptive_frame_field_flag: None,
2413 direct_8x8_inference_flag: false,
2414 frame_crop_info: None,
2415 sample_aspect_ratio: None,
2416 overscan_appropriate_flag: None,
2417 color_config: None,
2418 chroma_sample_loc: None,
2419 timing_info: None,
2420 }
2421 ");
2422 }
2423}