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