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