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}