scuffle_h265/sps/vui_parameters/
mod.rs

1use std::io;
2use std::num::NonZero;
3
4use byteorder::{BigEndian, ReadBytesExt};
5use scuffle_bytes_util::BitReader;
6use scuffle_expgolomb::BitReaderExpGolombExt;
7
8use super::{ConformanceWindow, Profile};
9use crate::range_check::range_check;
10use crate::{AspectRatioIdc, VideoFormat};
11
12mod hrd_parameters;
13
14pub use hrd_parameters::*;
15
16/// VUI parameters.
17///
18/// `vui_parameters()`
19///
20/// - ISO/IEC 23008-2 - E.2.1
21/// - ISO/IEC 23008-2 - E.3.1
22#[derive(Debug, Clone, PartialEq)]
23pub struct VuiParameters {
24    /// [`AspectRatioInfo`] if `aspect_ratio_info_present_flag` is `true`.
25    pub aspect_ratio_info: AspectRatioInfo,
26    /// Equal to `true` indicates that the cropped decoded pictures output are suitable
27    /// for display using overscan.
28    ///
29    /// Equal to `false` indicates that the cropped decoded pictures output contain visually important information
30    /// in the entire region out to the edges of the conformance cropping window of the picture, such that the
31    /// cropped decoded pictures output should not be displayed using overscan. Instead, they should be displayed
32    /// using either an exact match between the display area and the conformance cropping window, or using underscan.
33    /// As used in this paragraph, the term "overscan" refers to display processes in which some parts near the
34    /// borders of the cropped decoded pictures are not visible in the display area. The term "underscan" describes
35    /// display processes in which the entire cropped decoded pictures are visible in the display area, but they do
36    /// not cover the entire display area. For display processes that neither use overscan nor underscan, the display
37    /// area exactly matches the area of the cropped decoded pictures.
38    ///
39    /// Only present if `overscan_info_present_flag` is `true`.
40    pub overscan_appropriate_flag: Option<bool>,
41    /// `video_format`, `video_full_range_flag` and `colour_primaries`, if `video_signal_type_present_flag` is `true`.
42    ///
43    /// See [`VideoSignalType`] for details.
44    pub video_signal_type: VideoSignalType,
45    /// `chroma_sample_loc_type_top_field` and `chroma_sample_loc_type_bottom_field`, if `chroma_loc_info_present_flag` is `true`.
46    ///
47    /// See [`ChromaLocInfo`] for details.
48    pub chroma_loc_info: Option<ChromaLocInfo>,
49    /// Equal to `true` indicates that the value of all decoded chroma samples is
50    /// equal to [`1 << (BitDepthC − 1)`](crate::SpsRbsp::bit_depth_c).
51    ///
52    /// Equal to `false` provides no indication of decoded chroma sample values.
53    pub neutral_chroma_indication_flag: bool,
54    /// Equal to `true` indicates that the CVS conveys pictures that represent fields, and specifies that
55    /// a picture timing SEI message shall be present in every access unit of the current CVS.
56    ///
57    /// Equal to `false` indicates that the CVS conveys pictures that represent frames and that a picture timing SEI message
58    /// may or may not be present in any access unit of the current CVS.
59    pub field_seq_flag: bool,
60    /// Equal to `true` specifies that picture timing SEI messages are present for every
61    /// picture and include the `pic_struct`, `source_scan_type`, and `duplicate_flag` syntax elements.
62    ///
63    /// Equal to `false` specifies that the `pic_struct` syntax element is not present in
64    /// picture timing SEI messages.
65    pub frame_field_info_present_flag: bool,
66    /// `def_disp_win_left_offset`, `def_disp_win_right_offset`, `def_disp_win_top_offset` and `def_disp_win_bottom_offset`,
67    /// if `default_display_window_flag` is `true`.
68    ///
69    /// See [`DefaultDisplayWindow`] for details.
70    pub default_display_window: DefaultDisplayWindow,
71    /// `vui_num_units_in_tick`, `vui_time_scale`, `vui_poc_proportional_to_timing_flag` and `vui_num_ticks_poc_diff_one_minus1`,
72    /// if `vui_timing_info_present_flag` is `true`.
73    ///
74    /// See [`VuiTimingInfo`] for details.
75    pub vui_timing_info: Option<VuiTimingInfo>,
76    /// `tiles_fixed_structure_flag`, `motion_vectors_over_pic_boundaries_flag`, `restricted_ref_pic_lists_flag`,
77    /// `min_spatial_segmentation_idc`, `max_bytes_per_pic_denom`, `max_bits_per_min_cu_denom`,
78    /// `log2_max_mv_length_horizontal` and `log2_max_mv_length_vertical`, if `bitstream_restriction_flag` is `true`.
79    ///
80    /// See [`BitStreamRestriction`] for details.
81    pub bitstream_restriction: BitStreamRestriction,
82}
83
84impl VuiParameters {
85    // TODO: Find a solution for this
86    #[allow(clippy::too_many_arguments)]
87    pub(crate) fn parse<R: io::Read>(
88        bit_reader: &mut BitReader<R>,
89        sps_max_sub_layers_minus1: u8,
90        bit_depth_y: u8,
91        bit_depth_c: u8,
92        chroma_format_idc: u8,
93        general_profile: &Profile,
94        conformance_window: &ConformanceWindow,
95        sub_width_c: u8,
96        pic_width_in_luma_samples: NonZero<u64>,
97        sub_height_c: u8,
98        pic_height_in_luma_samples: NonZero<u64>,
99    ) -> io::Result<Self> {
100        let mut aspect_ratio_info = AspectRatioInfo::Predefined(AspectRatioIdc::Unspecified);
101        let mut overscan_appropriate_flag = None;
102        let mut video_signal_type = None;
103        let mut chroma_loc_info = None;
104        let mut default_display_window = None;
105        let mut vui_timing_info = None;
106
107        let aspect_ratio_info_present_flag = bit_reader.read_bit()?;
108        if aspect_ratio_info_present_flag {
109            let aspect_ratio_idc = bit_reader.read_u8()?;
110            if aspect_ratio_idc == AspectRatioIdc::ExtendedSar.0 {
111                let sar_width = bit_reader.read_u16::<BigEndian>()?;
112                let sar_height = bit_reader.read_u16::<BigEndian>()?;
113                aspect_ratio_info = AspectRatioInfo::ExtendedSar { sar_width, sar_height };
114            } else {
115                aspect_ratio_info = AspectRatioInfo::Predefined(aspect_ratio_idc.into());
116            }
117        }
118
119        let overscan_info_present_flag = bit_reader.read_bit()?;
120        if overscan_info_present_flag {
121            overscan_appropriate_flag = Some(bit_reader.read_bit()?);
122        }
123
124        let video_signal_type_present_flag = bit_reader.read_bit()?;
125        if video_signal_type_present_flag {
126            let video_format = VideoFormat::from(bit_reader.read_bits(3)? as u8);
127            let video_full_range_flag = bit_reader.read_bit()?;
128            let colour_description_present_flag = bit_reader.read_bit()?;
129
130            if colour_description_present_flag {
131                let colour_primaries = bit_reader.read_u8()?;
132                let transfer_characteristics = bit_reader.read_u8()?;
133                let matrix_coeffs = bit_reader.read_u8()?;
134
135                if matrix_coeffs == 0 && !(bit_depth_c == bit_depth_y && chroma_format_idc == 3) {
136                    return Err(io::Error::new(
137                        io::ErrorKind::InvalidData,
138                        "matrix_coeffs must not be 0 unless bit_depth_c == bit_depth_y and chroma_format_idc == 3",
139                    ));
140                }
141
142                if matrix_coeffs == 8
143                    && !(bit_depth_c == bit_depth_y || (bit_depth_c == bit_depth_y + 1 && chroma_format_idc == 3))
144                {
145                    return Err(io::Error::new(
146                        io::ErrorKind::InvalidData,
147                        "matrix_coeffs must not be 8 unless bit_depth_c == bit_depth_y or (bit_depth_c == bit_depth_y + 1 and chroma_format_idc == 3)",
148                    ));
149                }
150
151                video_signal_type = Some(VideoSignalType {
152                    video_format,
153                    video_full_range_flag,
154                    colour_primaries,
155                    transfer_characteristics,
156                    matrix_coeffs,
157                });
158            } else {
159                video_signal_type = Some(VideoSignalType {
160                    video_format,
161                    video_full_range_flag,
162                    ..Default::default()
163                });
164            }
165        }
166
167        let chroma_loc_info_present_flag = bit_reader.read_bit()?;
168
169        if chroma_format_idc != 1 && chroma_loc_info_present_flag {
170            return Err(io::Error::new(
171                io::ErrorKind::InvalidData,
172                "chroma_loc_info_present_flag must be 0 if chroma_format_idc != 1",
173            ));
174        }
175
176        if chroma_loc_info_present_flag {
177            let chroma_sample_loc_type_top_field = bit_reader.read_exp_golomb()?;
178            let chroma_sample_loc_type_bottom_field = bit_reader.read_exp_golomb()?;
179
180            chroma_loc_info = Some(ChromaLocInfo {
181                top_field: chroma_sample_loc_type_top_field,
182                bottom_field: chroma_sample_loc_type_bottom_field,
183            });
184        }
185
186        let neutral_chroma_indication_flag = bit_reader.read_bit()?;
187        let field_seq_flag = bit_reader.read_bit()?;
188
189        if general_profile.frame_only_constraint_flag && field_seq_flag {
190            return Err(io::Error::new(
191                io::ErrorKind::InvalidData,
192                "field_seq_flag must be 0 if general_frame_only_constraint_flag is 1",
193            ));
194        }
195
196        let frame_field_info_present_flag = bit_reader.read_bit()?;
197
198        if !frame_field_info_present_flag
199            && (field_seq_flag || (general_profile.progressive_source_flag && general_profile.interlaced_source_flag))
200        {
201            return Err(io::Error::new(
202                io::ErrorKind::InvalidData,
203                "frame_field_info_present_flag must be 1 if field_seq_flag is 1 or general_progressive_source_flag and general_interlaced_source_flag are both 1",
204            ));
205        }
206
207        let default_display_window_flag = bit_reader.read_bit()?;
208        if default_display_window_flag {
209            let def_disp_win_left_offset = bit_reader.read_exp_golomb()?;
210            let def_disp_win_right_offset = bit_reader.read_exp_golomb()?;
211            let def_disp_win_top_offset = bit_reader.read_exp_golomb()?;
212            let def_disp_win_bottom_offset = bit_reader.read_exp_golomb()?;
213            let left_offset = conformance_window.conf_win_left_offset + def_disp_win_left_offset;
214            let right_offset = conformance_window.conf_win_right_offset + def_disp_win_right_offset;
215            let top_offset = conformance_window.conf_win_top_offset + def_disp_win_top_offset;
216            let bottom_offset = conformance_window.conf_win_bottom_offset + def_disp_win_bottom_offset;
217
218            if sub_width_c as u64 * (left_offset + right_offset) >= pic_width_in_luma_samples.get() {
219                return Err(io::Error::new(
220                    io::ErrorKind::InvalidData,
221                    "sub_width_c * (left_offset + right_offset) must be less than pic_width_in_luma_samples",
222                ));
223            }
224
225            if sub_height_c as u64 * (top_offset + bottom_offset) >= pic_height_in_luma_samples.get() {
226                return Err(io::Error::new(
227                    io::ErrorKind::InvalidData,
228                    "sub_height_c * (top_offset + bottom_offset) must be less than pic_height_in_luma_samples",
229                ));
230            }
231
232            default_display_window = Some(DefaultDisplayWindow {
233                def_disp_win_left_offset,
234                def_disp_win_right_offset,
235                def_disp_win_top_offset,
236                def_disp_win_bottom_offset,
237            });
238        }
239
240        let vui_timing_info_present_flag = bit_reader.read_bit()?;
241        if vui_timing_info_present_flag {
242            let vui_num_units_in_tick = NonZero::new(bit_reader.read_u32::<BigEndian>()?).ok_or(io::Error::new(
243                io::ErrorKind::InvalidData,
244                "vui_num_units_in_tick must greater than zero",
245            ))?;
246            let vui_time_scale = NonZero::new(bit_reader.read_u32::<BigEndian>()?)
247                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "vui_time_scale must not be zero"))?;
248
249            let mut num_ticks_poc_diff_one_minus1 = None;
250            let vui_poc_proportional_to_timing_flag = bit_reader.read_bit()?;
251            if vui_poc_proportional_to_timing_flag {
252                let vui_num_ticks_poc_diff_one_minus1 = bit_reader.read_exp_golomb()?;
253                range_check!(vui_num_ticks_poc_diff_one_minus1, 0, 2u64.pow(32) - 2)?;
254                num_ticks_poc_diff_one_minus1 = Some(vui_num_ticks_poc_diff_one_minus1 as u32);
255            }
256
257            let mut vui_hrd_parameters = None;
258            let vui_hrd_parameters_present_flag = bit_reader.read_bit()?;
259            if vui_hrd_parameters_present_flag {
260                vui_hrd_parameters = Some(HrdParameters::parse(bit_reader, true, sps_max_sub_layers_minus1)?);
261            }
262
263            vui_timing_info = Some(VuiTimingInfo {
264                num_units_in_tick: vui_num_units_in_tick,
265                time_scale: vui_time_scale,
266                poc_proportional_to_timing_flag: vui_poc_proportional_to_timing_flag,
267                num_ticks_poc_diff_one_minus1,
268                hrd_parameters: vui_hrd_parameters,
269            });
270        }
271
272        let mut bitstream_restriction = BitStreamRestriction::default();
273        let bitstream_restriction_flag = bit_reader.read_bit()?;
274        if bitstream_restriction_flag {
275            bitstream_restriction.tiles_fixed_structure_flag = bit_reader.read_bit()?;
276            bitstream_restriction.motion_vectors_over_pic_boundaries_flag = bit_reader.read_bit()?;
277            bitstream_restriction.restricted_ref_pic_lists_flag = Some(bit_reader.read_bit()?);
278
279            let min_spatial_segmentation_idc = bit_reader.read_exp_golomb()?;
280            range_check!(min_spatial_segmentation_idc, 0, 4095)?;
281            bitstream_restriction.min_spatial_segmentation_idc = min_spatial_segmentation_idc as u16;
282
283            let max_bytes_per_pic_denom = bit_reader.read_exp_golomb()?;
284            range_check!(max_bytes_per_pic_denom, 0, 16)?;
285            bitstream_restriction.max_bytes_per_pic_denom = max_bytes_per_pic_denom as u8;
286
287            let max_bits_per_min_cu_denom = bit_reader.read_exp_golomb()?;
288            range_check!(max_bits_per_min_cu_denom, 0, 16)?;
289            bitstream_restriction.max_bits_per_min_cu_denom = max_bits_per_min_cu_denom as u8;
290
291            let log2_max_mv_length_horizontal = bit_reader.read_exp_golomb()?;
292            range_check!(log2_max_mv_length_horizontal, 0, 15)?;
293            bitstream_restriction.log2_max_mv_length_horizontal = log2_max_mv_length_horizontal as u8;
294
295            let log2_max_mv_length_vertical = bit_reader.read_exp_golomb()?;
296            range_check!(log2_max_mv_length_vertical, 0, 15)?;
297            bitstream_restriction.log2_max_mv_length_vertical = log2_max_mv_length_vertical as u8;
298        }
299
300        Ok(Self {
301            aspect_ratio_info,
302            overscan_appropriate_flag,
303            video_signal_type: video_signal_type.unwrap_or_default(),
304            chroma_loc_info,
305            neutral_chroma_indication_flag,
306            field_seq_flag,
307            frame_field_info_present_flag,
308            default_display_window: default_display_window.unwrap_or_default(),
309            vui_timing_info,
310            bitstream_restriction,
311        })
312    }
313}
314
315/// Specifies the value of the sample aspect ratio of the luma samples.
316#[derive(Debug, Clone, PartialEq)]
317pub enum AspectRatioInfo {
318    /// Any value other than [`AspectRatioIdc::ExtendedSar`].
319    Predefined(AspectRatioIdc),
320    /// [`AspectRatioIdc::ExtendedSar`].
321    ExtendedSar {
322        /// Indicates the horizontal size of the sample aspect ratio (in arbitrary units).
323        sar_width: u16,
324        /// Indicates the vertical size of the sample aspect ratio (in the same arbitrary units as `sar_width`).
325        sar_height: u16,
326    },
327}
328
329/// Directly part of [`VuiParameters`].
330#[derive(Debug, Clone, PartialEq)]
331pub struct VideoSignalType {
332    /// Indicates the representation of the pictures as specified in ISO/IEC 23008-2 - Table E.2, before being coded
333    /// in accordance with this document.
334    ///
335    /// The values 6 and 7 for video_format are reserved for future use by ITU-T | ISO/IEC and
336    /// shall not be present in bitstreams conforming to this version of this document.
337    /// Decoders shall interpret the values 6 and 7 for video_format as equivalent to the value [`VideoFormat::Unspecified`].
338    pub video_format: VideoFormat,
339    /// Indicates the black level and range of the luma and chroma signals as derived from
340    /// `E'Y`, `E'PB`, and `E'PR` or `E'R`, `E'G`, and `E'B` real-valued component signals.
341    pub video_full_range_flag: bool,
342    /// Indicates the chromaticity coordinates of the source primaries as specified in
343    /// ISO/IEC 23008-2 - Table E.3 in terms of the CIE 1931 definition of x and y as specified in ISO 11664-1.
344    pub colour_primaries: u8,
345    /// As specified in ISO/IEC 23008-2 - Table E.4, either indicates the reference opto-electronic transfer
346    /// characteristic function of the source picture as a function of a source input linear optical intensity `Lc` with
347    /// a nominal real-valued range of 0 to 1 or indicates the inverse of the reference electro-optical transfer
348    /// characteristic function as a function of an output linear optical intensity `Lo` with a nominal real-valued
349    /// range of 0 to 1.
350    ///
351    /// For more details, see ISO/IEC 23008-2 - E.3.1.
352    pub transfer_characteristics: u8,
353    /// Describes the matrix coefficients used in deriving luma and chroma signals from the green,
354    /// blue, and red, or Y, Z, and X primaries, as specified in ISO/IEC 23008-2 - Table E.5.
355    pub matrix_coeffs: u8,
356}
357
358impl Default for VideoSignalType {
359    fn default() -> Self {
360        Self {
361            video_format: VideoFormat::Unspecified,
362            video_full_range_flag: false,
363            colour_primaries: 2,
364            transfer_characteristics: 2,
365            matrix_coeffs: 2,
366        }
367    }
368}
369
370/// Directly part of [`VuiParameters`].
371///
372/// Specifies the location of chroma samples as follows:
373/// - If [`chroma_format_idc`](crate::SpsRbsp::chroma_format_idc) is equal to 1 (4:2:0 chroma format),
374///   [`chroma_sample_loc_type_top_field`](ChromaLocInfo::top_field) and
375///   [`chroma_sample_loc_type_bottom_field`](ChromaLocInfo::bottom_field) specify the location of chroma samples
376///   for the top field and the bottom field, respectively, as shown in ISO/IEC 23008-2 - Figure E.1.
377/// - Otherwise ([`chroma_format_idc`](crate::SpsRbsp::chroma_format_idc) is not equal to 1), the values of the syntax elements
378///   [`chroma_sample_loc_type_top_field`](ChromaLocInfo::top_field) and
379///   [`chroma_sample_loc_type_bottom_field`](ChromaLocInfo::bottom_field) shall be ignored.
380///   When [`chroma_format_idc`](crate::SpsRbsp::chroma_format_idc) is equal to 2 (4:2:2 chroma format) or 3 (4:4:4 chroma format),
381///   the location of chroma samples is specified in ISO/IEC 23008-2 - 6.2.
382///   When [`chroma_format_idc`](crate::SpsRbsp::chroma_format_idc) is equal to 0, there is no chroma sample array.
383#[derive(Debug, Clone, PartialEq)]
384pub struct ChromaLocInfo {
385    /// `chroma_sample_loc_type_top_field`
386    pub top_field: u64,
387    /// `chroma_sample_loc_type_bottom_field`
388    pub bottom_field: u64,
389}
390
391/// Directly part of [`VuiParameters`].
392///
393/// Specifies the samples of the pictures in the CVS that are within the default display window,
394/// in terms of a rectangular region specified in picture coordinates for display.
395#[derive(Debug, Clone, PartialEq, Default)]
396pub struct DefaultDisplayWindow {
397    /// `def_disp_win_left_offset`
398    pub def_disp_win_left_offset: u64,
399    /// `def_disp_win_right_offset`
400    pub def_disp_win_right_offset: u64,
401    /// `def_disp_win_top_offset`
402    pub def_disp_win_top_offset: u64,
403    /// `def_disp_win_bottom_offset`
404    pub def_disp_win_bottom_offset: u64,
405}
406
407impl DefaultDisplayWindow {
408    /// `leftOffset = conf_win_left_offset + def_disp_win_left_offset` (E-68)
409    ///
410    /// ISO/IEC 23008-2 - E.3.1
411    pub fn left_offset(&self, conformance_window: &ConformanceWindow) -> u64 {
412        conformance_window.conf_win_left_offset + self.def_disp_win_left_offset
413    }
414
415    /// `rightOffset = conf_win_right_offset + def_disp_win_right_offset` (E-69)
416    ///
417    /// ISO/IEC 23008-2 - E.3.1
418    pub fn right_offset(&self, conformance_window: &ConformanceWindow) -> u64 {
419        conformance_window.conf_win_right_offset + self.def_disp_win_right_offset
420    }
421
422    /// `topOffset = conf_win_top_offset + def_disp_win_top_offset` (E-70)
423    ///
424    /// ISO/IEC 23008-2 - E.3.1
425    pub fn top_offset(&self, conformance_window: &ConformanceWindow) -> u64 {
426        conformance_window.conf_win_top_offset + self.def_disp_win_top_offset
427    }
428
429    /// `bottomOffset = conf_win_bottom_offset + def_disp_win_bottom_offset` (E-71)
430    ///
431    /// ISO/IEC 23008-2 - E.3.1
432    pub fn bottom_offset(&self, conformance_window: &ConformanceWindow) -> u64 {
433        conformance_window.conf_win_bottom_offset + self.def_disp_win_bottom_offset
434    }
435}
436
437/// Directly part of [`VuiParameters`].
438#[derive(Debug, Clone, PartialEq)]
439pub struct VuiTimingInfo {
440    /// This value is the number of time units of a clock operating at the frequency `vui_time_scale`
441    /// Hz that corresponds to one increment (called a clock tick) of a clock tick counter.
442    ///
443    /// This value is greater than 0.
444    ///
445    /// A clock tick, in units of seconds, is equal to the quotient of `vui_num_units_in_tick` divided by `vui_time_scale`.
446    /// For example, when the picture rate of a video signal is 25 Hz, `vui_time_scale`
447    /// may be equal to `27 000 000` and `vui_num_units_in_tick` may be equal to 1 080 000, and consequently a
448    /// clock tick may be equal to `0.04` seconds.
449    pub num_units_in_tick: NonZero<u32>,
450    /// This value is the number of time units that pass in one second.
451    ///
452    /// For example, a time coordinate system that measures time using a `27 MHz` clock has a `vui_time_scale` of `27 000 000`.
453    ///
454    /// The value of `vui_time_scale` is greater than 0.
455    pub time_scale: NonZero<u32>,
456    /// equal to 1 indicates that the picture order count value for each
457    /// picture in the CVS that is not the first picture in the CVS, in decoding order, is proportional to the output
458    /// time of the picture relative to the output time of the first picture in the CVS.
459    /// vui_poc_proportional_to_timing_flag equal to 0 indicates that the picture order count value for each
460    /// picture in the CVS that is not the first picture in the CVS, in decoding order, may or may not be
461    /// proportional to the output time of the picture relative to the output time of the first picture in the CVS.
462    pub poc_proportional_to_timing_flag: bool,
463    /// This value plus 1 specifies the number of clock ticks corresponding to a
464    /// difference of picture order count values equal to 1.
465    ///
466    /// The value is in range \[0, 2^32 − 2\].
467    pub num_ticks_poc_diff_one_minus1: Option<u32>,
468    /// If `vui_hrd_parameters_present_flag` is equal to 1, this value specifies the HRD parameters.
469    pub hrd_parameters: Option<HrdParameters>,
470}
471
472/// Directly part of [`VuiParameters`].
473#[derive(Debug, Clone, PartialEq)]
474pub struct BitStreamRestriction {
475    /// Equal to `true` indicates that each PPS that is active in the CVS has the same value
476    /// of the syntax elements `num_tile_columns_minus1`, `num_tile_rows_minus1`, `uniform_spacing_flag`,
477    /// `column_width_minus1[i]`, `row_height_minus1[i]` and `loop_filter_across_tiles_enabled_flag`, when
478    /// present.
479    ///
480    /// Equal to `false` indicates that tiles syntax elements in different PPSs may or
481    /// may not have the same value.
482    pub tiles_fixed_structure_flag: bool,
483    /// Equal to `false` indicates that no sample outside the picture
484    /// boundaries and no sample at a fractional sample position for which the sample value is derived using one
485    /// or more samples outside the picture boundaries is used for inter prediction of any sample.
486    ///
487    /// Equal to `true` indicates that one or more samples outside the
488    /// picture boundaries may be used in inter prediction.
489    pub motion_vectors_over_pic_boundaries_flag: bool,
490    /// Equal to `Some(true)` indicates that all P and B slices (when present) that belong to the
491    /// same picture have an identical reference picture list 0, and that all B slices (when present) that belong to
492    /// the same picture have an identical reference picture list 1.
493    pub restricted_ref_pic_lists_flag: Option<bool>,
494    /// When not equal to 0, establishes a bound on the maximum possible size
495    /// of distinct coded spatial segmentation regions in the pictures of the CVS.
496    ///
497    /// The value is in range \[0, 4095\].
498    ///
499    /// Defines [`minSpatialSegmentationTimes4`](BitStreamRestriction::min_spatial_segmentation_times4).
500    pub min_spatial_segmentation_idc: u16,
501    /// Indicates a number of bytes not exceeded by the sum of the sizes of the VCL
502    /// NAL units associated with any coded picture in the CVS.
503    ///
504    /// The number of bytes that represent a picture in the NAL unit stream is specified for this purpose as the
505    /// total number of bytes of VCL NAL unit data (i.e. the total of the `NumBytesInNalUnit` variables for the VCL
506    /// NAL units) for the picture.
507    ///
508    /// The value is in range \[0, 16\].
509    pub max_bytes_per_pic_denom: u8,
510    /// Indicates an upper bound for the number of coded bits of `coding_unit()`
511    /// data for any coding block in any picture of the CVS.
512    ///
513    /// The value is in range \[0, 16\].
514    pub max_bits_per_min_cu_denom: u8,
515    /// Indicates the maximum absolute
516    /// value of a decoded horizontal and vertical motion vector component, respectively, in quarter luma sample
517    /// units, for all pictures in the CVS. A value of n asserts that no value of a motion vector component is outside
518    /// the range of \[`−2n`, `2n − 1`\], in units of quarter luma sample displacement, where `n` refers to the
519    /// value of [`log2_max_mv_length_horizontal`](BitStreamRestriction::log2_max_mv_length_horizontal) and
520    /// [`log2_max_mv_length_vertical`](BitStreamRestriction::log2_max_mv_length_vertical) for the horizontal and
521    /// vertical component of the MV, respectively.
522    ///
523    /// The value is in range \[0, 15\].
524    pub log2_max_mv_length_horizontal: u8,
525    /// Same as [`log2_max_mv_length_horizontal`](BitStreamRestriction::log2_max_mv_length_horizontal).
526    pub log2_max_mv_length_vertical: u8,
527}
528
529impl Default for BitStreamRestriction {
530    fn default() -> Self {
531        Self {
532            tiles_fixed_structure_flag: false,
533            motion_vectors_over_pic_boundaries_flag: true,
534            restricted_ref_pic_lists_flag: None,
535            min_spatial_segmentation_idc: 0,
536            max_bytes_per_pic_denom: 2,
537            max_bits_per_min_cu_denom: 1,
538            log2_max_mv_length_horizontal: 15,
539            log2_max_mv_length_vertical: 15,
540        }
541    }
542}
543
544impl BitStreamRestriction {
545    /// `minSpatialSegmentationTimes4 = min_spatial_segmentation_idc + 4` (E-72)
546    ///
547    /// ISO/IEC 23008-2 - E.3.1
548    pub fn min_spatial_segmentation_times4(&self) -> u16 {
549        self.min_spatial_segmentation_idc + 4
550    }
551}
552
553#[cfg(test)]
554#[cfg_attr(all(test, coverage_nightly), coverage(off))]
555mod tests {
556    use std::io::Write;
557    use std::num::NonZero;
558
559    use byteorder::{BigEndian, WriteBytesExt};
560    use scuffle_bytes_util::{BitReader, BitWriter};
561    use scuffle_expgolomb::BitWriterExpGolombExt;
562
563    use crate::sps::vui_parameters::{BitStreamRestriction, DefaultDisplayWindow};
564    use crate::{AspectRatioIdc, ConformanceWindow, Profile, ProfileCompatibilityFlags, VideoFormat, VuiParameters};
565
566    #[test]
567    fn vui_parameters() {
568        let mut data = Vec::new();
569        let mut writer = BitWriter::new(&mut data);
570
571        writer.write_bit(true).unwrap(); // aspect_ratio_info_present_flag
572        writer.write_u8(AspectRatioIdc::ExtendedSar.0).unwrap(); // aspect_ratio_idc
573        writer.write_u16::<BigEndian>(1).unwrap(); // sar_width
574        writer.write_u16::<BigEndian>(1).unwrap(); // sar_height
575
576        writer.write_bit(true).unwrap(); // overscan_info_present_flag
577        writer.write_bit(true).unwrap(); // overscan_appropriate_flag
578
579        writer.write_bit(false).unwrap(); // video_signal_type_present_flag
580        writer.write_bit(false).unwrap(); // chroma_loc_info_present_flag
581        writer.write_bit(false).unwrap(); // neutral_chroma_indication_flag
582        writer.write_bit(false).unwrap(); // field_seq_flag
583        writer.write_bit(false).unwrap(); // frame_field_info_present_flag
584
585        writer.write_bit(true).unwrap(); // default_display_window_flag
586        writer.write_exp_golomb(0).unwrap(); // def_disp_win_left_offset
587        writer.write_exp_golomb(10).unwrap(); // def_disp_win_right_offset
588        writer.write_exp_golomb(0).unwrap(); // def_disp_win_top_offset
589        writer.write_exp_golomb(10).unwrap(); // def_disp_win_bottom_offset
590
591        writer.write_bit(false).unwrap(); // vui_timing_info_present_flag
592        writer.write_bit(false).unwrap(); // bitstream_restriction_flag
593
594        writer.write_bits(0, 5).unwrap(); // fill the byte
595        writer.flush().unwrap();
596
597        let conf_window = ConformanceWindow {
598            conf_win_left_offset: 2,
599            conf_win_right_offset: 2,
600            conf_win_top_offset: 2,
601            conf_win_bottom_offset: 2,
602        };
603
604        let vui_parameters = VuiParameters::parse(
605            &mut BitReader::new(data.as_slice()),
606            0,
607            8,
608            8,
609            1,
610            &Profile {
611                profile_space: 0,
612                tier_flag: false,
613                profile_idc: 0,
614                profile_compatibility_flag: ProfileCompatibilityFlags::empty(),
615                progressive_source_flag: false,
616                interlaced_source_flag: false,
617                non_packed_constraint_flag: false,
618                frame_only_constraint_flag: false,
619                additional_flags: crate::ProfileAdditionalFlags::None,
620                inbld_flag: None,
621                level_idc: Some(0),
622            },
623            &conf_window,
624            1,
625            NonZero::new(1920).unwrap(),
626            1,
627            NonZero::new(1080).unwrap(),
628        )
629        .unwrap();
630
631        assert_eq!(
632            vui_parameters.aspect_ratio_info,
633            super::AspectRatioInfo::ExtendedSar {
634                sar_width: 1,
635                sar_height: 1
636            }
637        );
638        assert_eq!(vui_parameters.overscan_appropriate_flag, Some(true));
639        assert_eq!(vui_parameters.video_signal_type.video_format, VideoFormat::Unspecified);
640        assert!(!vui_parameters.video_signal_type.video_full_range_flag);
641        assert_eq!(vui_parameters.video_signal_type.colour_primaries, 2);
642        assert_eq!(vui_parameters.video_signal_type.transfer_characteristics, 2);
643        assert_eq!(vui_parameters.video_signal_type.matrix_coeffs, 2);
644        assert_eq!(vui_parameters.chroma_loc_info, None);
645        assert!(!vui_parameters.neutral_chroma_indication_flag);
646        assert!(!vui_parameters.field_seq_flag);
647        assert!(!vui_parameters.frame_field_info_present_flag);
648        assert_eq!(
649            vui_parameters.default_display_window,
650            DefaultDisplayWindow {
651                def_disp_win_left_offset: 0,
652                def_disp_win_right_offset: 10,
653                def_disp_win_top_offset: 0,
654                def_disp_win_bottom_offset: 10,
655            }
656        );
657        assert_eq!(vui_parameters.default_display_window.left_offset(&conf_window), 2);
658        assert_eq!(vui_parameters.default_display_window.right_offset(&conf_window), 12);
659        assert_eq!(vui_parameters.default_display_window.top_offset(&conf_window), 2);
660        assert_eq!(vui_parameters.default_display_window.bottom_offset(&conf_window), 12);
661        assert_eq!(vui_parameters.vui_timing_info, None);
662        assert_eq!(vui_parameters.bitstream_restriction, BitStreamRestriction::default());
663        assert_eq!(vui_parameters.bitstream_restriction.min_spatial_segmentation_times4(), 4);
664    }
665}