1use std::io;
2use std::num::NonZero;
3
4use scuffle_bytes_util::{BitReader, EmulationPreventionIo, range_check};
5use scuffle_expgolomb::BitReaderExpGolombExt;
6
7use crate::NALUnitType;
8use crate::nal_unit_header::NALUnitHeader;
9use crate::rbsp_trailing_bits::rbsp_trailing_bits;
10
11mod conformance_window;
12mod long_term_ref_pics;
13mod pcm;
14mod profile_tier_level;
15mod scaling_list;
16mod sps_3d_extension;
17mod sps_multilayer_extension;
18mod sps_range_extension;
19mod sps_scc_extension;
20mod st_ref_pic_set;
21mod sub_layer_ordering_info;
22mod vui_parameters;
23
24pub use conformance_window::*;
25pub use long_term_ref_pics::*;
26pub use pcm::*;
27pub use profile_tier_level::*;
28pub use scaling_list::*;
29pub use sps_3d_extension::*;
30pub use sps_multilayer_extension::*;
31pub use sps_range_extension::*;
32pub use sps_scc_extension::*;
33pub use st_ref_pic_set::*;
34pub use sub_layer_ordering_info::*;
35pub use vui_parameters::*;
36
37#[derive(Debug, Clone, PartialEq)]
55pub struct SpsNALUnit {
56 pub nal_unit_header: NALUnitHeader,
58 pub rbsp: SpsRbsp,
60}
61
62impl SpsNALUnit {
63 pub fn parse(mut reader: impl io::Read) -> io::Result<Self> {
65 let nal_unit_header = NALUnitHeader::parse(&mut reader)?;
66 if nal_unit_header.nal_unit_type != NALUnitType::SpsNut {
67 return Err(io::Error::new(io::ErrorKind::InvalidData, "nal_unit_type is not SPS_NUT"));
68 }
69
70 let rbsp = SpsRbsp::parse(reader, nal_unit_header.nuh_layer_id)?;
71
72 Ok(SpsNALUnit { nal_unit_header, rbsp })
73 }
74}
75
76#[derive(Debug, Clone, PartialEq)]
85pub struct SpsRbsp {
86 pub sps_video_parameter_set_id: u8,
88 pub sps_max_sub_layers_minus1: u8,
93 pub sps_temporal_id_nesting_flag: bool,
97 pub profile_tier_level: ProfileTierLevel,
99 pub sps_seq_parameter_set_id: u64,
103 pub chroma_format_idc: u8,
107 pub separate_colour_plane_flag: bool,
113 pub pic_width_in_luma_samples: NonZero<u64>,
117 pub pic_height_in_luma_samples: NonZero<u64>,
121 pub conformance_window: ConformanceWindow,
125 pub bit_depth_luma_minus8: u8,
130 pub bit_depth_chroma_minus8: u8,
135 pub log2_max_pic_order_cnt_lsb_minus4: u8,
140 pub sub_layer_ordering_info: SubLayerOrderingInfo,
144 pub log2_min_luma_coding_block_size_minus3: u64,
148 pub log2_diff_max_min_luma_coding_block_size: u64,
150 pub log2_min_luma_transform_block_size_minus2: u64,
154 pub log2_diff_max_min_luma_transform_block_size: u64,
158 pub max_transform_hierarchy_depth_inter: u64,
162 pub max_transform_hierarchy_depth_intra: u64,
166 pub scaling_list_data: Option<ScalingListData>,
168 pub amp_enabled_flag: bool,
173 pub sample_adaptive_offset_enabled_flag: bool,
179 pub pcm: Option<Pcm>,
184 pub short_term_ref_pic_sets: ShortTermRefPicSets,
186 pub long_term_ref_pics: Option<LongTermRefPics>,
190 pub sps_temporal_mvp_enabled_flag: bool,
196 pub strong_intra_smoothing_enabled_flag: bool,
201 pub vui_parameters: Option<VuiParameters>,
203 pub range_extension: Option<SpsRangeExtension>,
205 pub multilayer_extension: Option<SpsMultilayerExtension>,
207 pub sps_3d_extension: Option<Sps3dExtension>,
209 pub scc_extension: Option<SpsSccExtension>,
211}
212
213impl SpsRbsp {
214 pub fn parse(reader: impl io::Read, nuh_layer_id: u8) -> io::Result<Self> {
220 let mut bit_reader = BitReader::new(EmulationPreventionIo::new(reader));
221
222 let sps_video_parameter_set_id = bit_reader.read_bits(4)? as u8;
223
224 let sps_max_sub_layers_minus1 = bit_reader.read_bits(3)? as u8;
225 range_check!(sps_max_sub_layers_minus1, 0, 6)?;
226
227 let sps_temporal_id_nesting_flag = bit_reader.read_bit()?;
228
229 if sps_max_sub_layers_minus1 == 0 && !sps_temporal_id_nesting_flag {
230 return Err(io::Error::new(
231 io::ErrorKind::InvalidData,
232 "sps_temporal_id_nesting_flag must be 1 when sps_max_sub_layers_minus1 is 0",
233 ));
234 }
235
236 let profile_tier_level = ProfileTierLevel::parse(&mut bit_reader, sps_max_sub_layers_minus1)?;
237
238 let sps_seq_parameter_set_id = bit_reader.read_exp_golomb()?;
239 range_check!(sps_seq_parameter_set_id, 0, 15)?;
240
241 let chroma_format_idc = bit_reader.read_exp_golomb()?;
242 range_check!(chroma_format_idc, 0, 3)?;
243 let chroma_format_idc = chroma_format_idc as u8;
244
245 let mut separate_colour_plane_flag = false;
246 if chroma_format_idc == 3 {
247 separate_colour_plane_flag = bit_reader.read_bit()?;
248 }
249
250 let sub_width_c = if chroma_format_idc == 1 || chroma_format_idc == 2 {
252 2
253 } else {
254 1
255 };
256 let sub_height_c = if chroma_format_idc == 1 { 2 } else { 1 };
257
258 let pic_width_in_luma_samples = NonZero::new(bit_reader.read_exp_golomb()?)
259 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "pic_width_in_luma_samples must not be 0"))?;
260
261 let pic_height_in_luma_samples = NonZero::new(bit_reader.read_exp_golomb()?)
262 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "pic_height_in_luma_samples must not be 0"))?;
263
264 let conformance_window_flag = bit_reader.read_bit()?;
265
266 let conformance_window = conformance_window_flag
267 .then(|| ConformanceWindow::parse(&mut bit_reader))
268 .transpose()?
269 .unwrap_or_default();
270
271 let bit_depth_luma_minus8 = bit_reader.read_exp_golomb()?;
272 range_check!(bit_depth_luma_minus8, 0, 8)?;
273 let bit_depth_luma_minus8 = bit_depth_luma_minus8 as u8;
274 let bit_depth_y = 8 + bit_depth_luma_minus8; let bit_depth_chroma_minus8 = bit_reader.read_exp_golomb()?;
276 range_check!(bit_depth_chroma_minus8, 0, 8)?;
277 let bit_depth_chroma_minus8 = bit_depth_chroma_minus8 as u8;
278 let bit_depth_c = 8 + bit_depth_chroma_minus8; let log2_max_pic_order_cnt_lsb_minus4 = bit_reader.read_exp_golomb()?;
281 range_check!(log2_max_pic_order_cnt_lsb_minus4, 0, 12)?;
282 let log2_max_pic_order_cnt_lsb_minus4 = log2_max_pic_order_cnt_lsb_minus4 as u8;
283
284 let sps_sub_layer_ordering_info_present_flag = bit_reader.read_bit()?;
285 let sub_layer_ordering_info = SubLayerOrderingInfo::parse(
286 &mut bit_reader,
287 sps_sub_layer_ordering_info_present_flag,
288 sps_max_sub_layers_minus1,
289 )?;
290
291 let log2_min_luma_coding_block_size_minus3 = bit_reader.read_exp_golomb()?;
292 let log2_diff_max_min_luma_coding_block_size = bit_reader.read_exp_golomb()?;
293
294 let min_cb_log2_size_y = log2_min_luma_coding_block_size_minus3 + 3;
295 let ctb_log2_size_y = min_cb_log2_size_y + log2_diff_max_min_luma_coding_block_size;
296
297 let log2_min_luma_transform_block_size_minus2 = bit_reader.read_exp_golomb()?;
298
299 let min_tb_log2_size_y = log2_min_luma_transform_block_size_minus2 + 2;
300
301 let log2_diff_max_min_luma_transform_block_size = bit_reader.read_exp_golomb()?;
302 let max_transform_hierarchy_depth_inter = bit_reader.read_exp_golomb()?;
303 range_check!(max_transform_hierarchy_depth_inter, 0, ctb_log2_size_y - min_tb_log2_size_y)?;
304 let max_transform_hierarchy_depth_intra = bit_reader.read_exp_golomb()?;
305 range_check!(max_transform_hierarchy_depth_intra, 0, ctb_log2_size_y - min_tb_log2_size_y)?;
306
307 let scaling_list_enabled_flag = bit_reader.read_bit()?;
308
309 let mut scaling_list_data = None;
310 if scaling_list_enabled_flag {
311 let sps_scaling_list_data_present_flag = bit_reader.read_bit()?;
312
313 if sps_scaling_list_data_present_flag {
314 scaling_list_data = Some(ScalingListData::parse(&mut bit_reader)?);
315 }
316 }
317
318 let amp_enabled_flag = bit_reader.read_bit()?;
319 let sample_adaptive_offset_enabled_flag = bit_reader.read_bit()?;
320
321 let mut pcm = None;
322 let pcm_enabled_flag = bit_reader.read_bit()?;
323 if pcm_enabled_flag {
324 pcm = Some(Pcm::parse(
325 &mut bit_reader,
326 bit_depth_y,
327 bit_depth_c,
328 min_cb_log2_size_y,
329 ctb_log2_size_y,
330 )?);
331 }
332
333 let num_short_term_ref_pic_sets = bit_reader.read_exp_golomb()?;
334 range_check!(num_short_term_ref_pic_sets, 0, 64)?;
335 let num_short_term_ref_pic_sets = num_short_term_ref_pic_sets as u8;
336 let short_term_ref_pic_sets = ShortTermRefPicSets::parse(
337 &mut bit_reader,
338 num_short_term_ref_pic_sets as usize,
339 nuh_layer_id,
340 *sub_layer_ordering_info
341 .sps_max_dec_pic_buffering_minus1
342 .last()
343 .expect("unreachable: cannot be empty"),
344 )?;
345
346 let mut long_term_ref_pics = None;
347 let long_term_ref_pics_present_flag = bit_reader.read_bit()?;
348 if long_term_ref_pics_present_flag {
349 long_term_ref_pics = Some(LongTermRefPics::parse(&mut bit_reader, log2_max_pic_order_cnt_lsb_minus4)?);
350 }
351
352 let sps_temporal_mvp_enabled_flag = bit_reader.read_bit()?;
353 let strong_intra_smoothing_enabled_flag = bit_reader.read_bit()?;
354
355 let mut vui_parameters = None;
356 let vui_parameters_present_flag = bit_reader.read_bit()?;
357 if vui_parameters_present_flag {
358 vui_parameters = Some(VuiParameters::parse(
359 &mut bit_reader,
360 sps_max_sub_layers_minus1,
361 bit_depth_y,
362 bit_depth_c,
363 chroma_format_idc,
364 &profile_tier_level.general_profile,
365 &conformance_window,
366 sub_width_c,
367 pic_width_in_luma_samples,
368 sub_height_c,
369 pic_height_in_luma_samples,
370 )?);
371 }
372
373 let mut range_extension = None;
375 let mut multilayer_extension = None;
376 let mut sps_3d_extension = None;
377 let mut scc_extension = None;
378
379 let sps_extension_flag = bit_reader.read_bit()?;
380 if sps_extension_flag {
381 let sps_range_extension_flag = bit_reader.read_bit()?;
382 let sps_multilayer_extension_flag = bit_reader.read_bit()?;
383 let sps_3d_extension_flag = bit_reader.read_bit()?;
384 let sps_scc_extension_flag = bit_reader.read_bit()?;
385 let sps_extension_4bits = bit_reader.read_bits(4)? as u8;
386
387 if sps_extension_4bits != 0 {
388 return Err(io::Error::new(io::ErrorKind::InvalidData, "sps_extension_4bits must be 0"));
389 }
390
391 if sps_range_extension_flag {
392 range_extension = Some(SpsRangeExtension::parse(&mut bit_reader)?);
393 }
394
395 if sps_multilayer_extension_flag {
396 multilayer_extension = Some(SpsMultilayerExtension::parse(&mut bit_reader)?);
397 }
398
399 if sps_3d_extension_flag {
400 sps_3d_extension = Some(Sps3dExtension::parse(&mut bit_reader, min_cb_log2_size_y, ctb_log2_size_y)?);
401 }
402
403 if sps_scc_extension_flag {
404 scc_extension = Some(SpsSccExtension::parse(
405 &mut bit_reader,
406 chroma_format_idc,
407 bit_depth_y,
408 bit_depth_c,
409 )?);
410 }
411
412 }
414
415 rbsp_trailing_bits(&mut bit_reader)?;
416
417 Ok(SpsRbsp {
418 sps_video_parameter_set_id,
419 sps_max_sub_layers_minus1,
420 sps_temporal_id_nesting_flag,
421 profile_tier_level,
422 sps_seq_parameter_set_id,
423 chroma_format_idc,
424 separate_colour_plane_flag,
425 pic_width_in_luma_samples,
426 pic_height_in_luma_samples,
427 conformance_window,
428 bit_depth_luma_minus8,
429 bit_depth_chroma_minus8,
430 log2_max_pic_order_cnt_lsb_minus4,
431 sub_layer_ordering_info,
432 log2_min_luma_coding_block_size_minus3,
433 log2_diff_max_min_luma_coding_block_size,
434 log2_min_luma_transform_block_size_minus2,
435 log2_diff_max_min_luma_transform_block_size,
436 max_transform_hierarchy_depth_inter,
437 max_transform_hierarchy_depth_intra,
438 scaling_list_data,
439 amp_enabled_flag,
440 sample_adaptive_offset_enabled_flag,
441 pcm,
442 short_term_ref_pic_sets,
443 long_term_ref_pics,
444 sps_temporal_mvp_enabled_flag,
445 strong_intra_smoothing_enabled_flag,
446 vui_parameters,
447 range_extension,
448 multilayer_extension,
449 sps_3d_extension,
450 scc_extension,
451 })
452 }
453
454 pub fn cropped_width(&self) -> u64 {
462 self.pic_width_in_luma_samples.get()
463 - self.sub_width_c() as u64
464 * (self.conformance_window.conf_win_left_offset + self.conformance_window.conf_win_right_offset)
465 }
466
467 pub fn cropped_height(&self) -> u64 {
475 self.pic_height_in_luma_samples.get()
476 - self.sub_height_c() as u64
477 * (self.conformance_window.conf_win_top_offset + self.conformance_window.conf_win_bottom_offset)
478 }
479
480 pub fn chroma_array_type(&self) -> u8 {
485 if self.separate_colour_plane_flag {
486 0
487 } else {
488 self.chroma_format_idc
489 }
490 }
491
492 pub fn sub_width_c(&self) -> u8 {
494 if self.chroma_format_idc == 1 || self.chroma_format_idc == 2 {
495 2
496 } else {
497 1
498 }
499 }
500
501 pub fn sub_height_c(&self) -> u8 {
503 if self.chroma_format_idc == 1 { 2 } else { 1 }
504 }
505
506 pub fn bit_depth_y(&self) -> u8 {
512 8 + self.bit_depth_luma_minus8
513 }
514
515 pub fn qp_bd_offset_y(&self) -> u8 {
521 6 * self.bit_depth_y()
522 }
523
524 #[inline]
530 pub fn bit_depth_c(&self) -> u8 {
531 8 + self.bit_depth_chroma_minus8
532 }
533
534 pub fn qp_bd_offset_c(&self) -> u8 {
540 6 * self.bit_depth_c()
541 }
542
543 pub fn max_pic_order_cnt_lsb(&self) -> u32 {
549 2u32.pow(self.log2_max_pic_order_cnt_lsb_minus4 as u32 + 4)
550 }
551
552 pub fn min_cb_log2_size_y(&self) -> u64 {
556 self.log2_min_luma_coding_block_size_minus3 + 3
557 }
558
559 pub fn ctb_log2_size_y(&self) -> u64 {
563 self.min_cb_log2_size_y() + self.log2_diff_max_min_luma_coding_block_size
564 }
565
566 pub fn min_cb_size_y(&self) -> u64 {
570 1 << self.min_cb_log2_size_y()
571 }
572
573 pub fn ctb_size_y(&self) -> NonZero<u64> {
577 NonZero::new(1 << self.ctb_log2_size_y()).unwrap()
578 }
579
580 pub fn pic_width_in_min_cbs_y(&self) -> u64 {
584 self.pic_width_in_luma_samples.get() / self.min_cb_size_y()
585 }
586
587 pub fn pic_width_in_ctbs_y(&self) -> u64 {
591 (self.pic_width_in_luma_samples.get() / self.ctb_size_y()) + 1
592 }
593
594 pub fn pic_height_in_min_cbs_y(&self) -> u64 {
598 self.pic_height_in_luma_samples.get() / self.min_cb_size_y()
599 }
600
601 pub fn pic_height_in_ctbs_y(&self) -> u64 {
605 (self.pic_height_in_luma_samples.get() / self.ctb_size_y()) + 1
606 }
607
608 pub fn pic_size_in_min_cbs_y(&self) -> u64 {
612 self.pic_width_in_min_cbs_y() * self.pic_height_in_min_cbs_y()
613 }
614
615 pub fn pic_size_in_ctbs_y(&self) -> u64 {
619 self.pic_width_in_ctbs_y() * self.pic_height_in_ctbs_y()
620 }
621
622 pub fn pic_size_in_samples_y(&self) -> u64 {
626 self.pic_width_in_luma_samples.get() * self.pic_height_in_luma_samples.get()
627 }
628
629 pub fn pic_width_in_samples_c(&self) -> u64 {
633 self.pic_width_in_luma_samples.get() / self.sub_width_c() as u64
634 }
635
636 pub fn pic_height_in_samples_c(&self) -> u64 {
640 self.pic_height_in_luma_samples.get() / self.sub_height_c() as u64
641 }
642
643 pub fn ctb_width_c(&self) -> u64 {
649 if self.chroma_format_idc == 0 || self.separate_colour_plane_flag {
650 0
651 } else {
652 self.ctb_size_y().get() / self.sub_width_c() as u64
653 }
654 }
655
656 pub fn ctb_height_c(&self) -> u64 {
662 if self.chroma_format_idc == 0 || self.separate_colour_plane_flag {
663 0
664 } else {
665 self.ctb_size_y().get() / self.sub_height_c() as u64
666 }
667 }
668
669 pub fn min_tb_log2_size_y(&self) -> u64 {
676 self.log2_min_luma_transform_block_size_minus2 + 2
677 }
678
679 pub fn max_tb_log2_size_y(&self) -> u64 {
685 self.log2_min_luma_transform_block_size_minus2 + 2 + self.log2_diff_max_min_luma_transform_block_size
686 }
687
688 pub fn raw_ctu_bits(&self) -> u64 {
692 let ctb_size_y = self.ctb_size_y().get();
693 ctb_size_y * ctb_size_y * self.bit_depth_y() as u64
694 + 2 * (self.ctb_width_c() * self.ctb_height_c()) * self.bit_depth_c() as u64
695 }
696}
697
698#[cfg(test)]
699#[cfg_attr(all(test, coverage_nightly), coverage(off))]
700mod tests {
701 use std::io;
702
703 use crate::SpsNALUnit;
704
705 #[test]
708 fn test_sps_parse() {
709 let data = b"B\x01\x01\x01@\0\0\x03\0\x90\0\0\x03\0\0\x03\0\x99\xa0\x01@ \x05\xa1e\x95R\x90\x84d_\xf8\xc0Z\x80\x80\x80\x82\0\0\x03\0\x02\0\0\x03\x01 \xc0\x0b\xbc\xa2\0\x02bX\0\x011-\x08";
710
711 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
712 let sps = &nalu.rbsp;
713
714 assert_eq!(sps.cropped_width(), 2560);
715 assert_eq!(sps.cropped_height(), 1440);
716 assert_eq!(sps.chroma_array_type(), 1);
717 assert_eq!(sps.sub_width_c(), 2);
718 assert_eq!(sps.sub_height_c(), 2);
719 assert_eq!(sps.bit_depth_y(), 8);
720 assert_eq!(sps.qp_bd_offset_y(), 48);
721 assert_eq!(sps.bit_depth_c(), 8);
722 assert_eq!(sps.qp_bd_offset_c(), 48);
723 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
724 assert_eq!(sps.min_cb_log2_size_y(), 4);
725 assert_eq!(sps.ctb_log2_size_y(), 5);
726 assert_eq!(sps.min_cb_size_y(), 16);
727 assert_eq!(sps.ctb_size_y().get(), 32);
728 assert_eq!(sps.pic_width_in_min_cbs_y(), 160);
729 assert_eq!(sps.pic_width_in_ctbs_y(), 81);
730 assert_eq!(sps.pic_height_in_min_cbs_y(), 90);
731 assert_eq!(sps.pic_height_in_ctbs_y(), 46);
732 assert_eq!(sps.pic_size_in_min_cbs_y(), 14400);
733 assert_eq!(sps.pic_size_in_ctbs_y(), 3726);
734 assert_eq!(sps.pic_size_in_samples_y(), 3686400);
735 assert_eq!(sps.pic_width_in_samples_c(), 1280);
736 assert_eq!(sps.pic_height_in_samples_c(), 720);
737 assert_eq!(sps.ctb_width_c(), 16);
738 assert_eq!(sps.ctb_height_c(), 16);
739 assert_eq!(sps.min_tb_log2_size_y(), 2);
740 assert_eq!(sps.max_tb_log2_size_y(), 5);
741 assert_eq!(sps.raw_ctu_bits(), 12288);
742 insta::assert_debug_snapshot!(nalu);
743 }
744
745 #[test]
746 fn test_sps_parse2() {
747 let data = b"\x42\x01\x01\x01\x40\x00\x00\x03\x00\x90\x00\x00\x03\x00\x00\x03\x00\x78\xa0\x03\xc0\x80\x11\x07\xcb\x96\xb4\xa4\x25\x92\xe3\x01\x6a\x02\x02\x02\x08\x00\x00\x03\x00\x08\x00\x00\x03\x00\xf3\x00\x2e\xf2\x88\x00\x02\x62\x5a\x00\x00\x13\x12\xd0\x20";
749
750 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
751 let sps = &nalu.rbsp;
752
753 assert_eq!(sps.cropped_width(), 1920);
754 assert_eq!(sps.cropped_height(), 1080);
755 assert_eq!(sps.chroma_array_type(), 1);
756 assert_eq!(sps.sub_width_c(), 2);
757 assert_eq!(sps.sub_height_c(), 2);
758 assert_eq!(sps.bit_depth_y(), 8);
759 assert_eq!(sps.qp_bd_offset_y(), 48);
760 assert_eq!(sps.bit_depth_c(), 8);
761 assert_eq!(sps.qp_bd_offset_c(), 48);
762 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
763 assert_eq!(sps.min_cb_log2_size_y(), 4);
764 assert_eq!(sps.ctb_log2_size_y(), 5);
765 assert_eq!(sps.min_cb_size_y(), 16);
766 assert_eq!(sps.ctb_size_y().get(), 32);
767 assert_eq!(sps.pic_width_in_min_cbs_y(), 120);
768 assert_eq!(sps.pic_width_in_ctbs_y(), 61);
769 assert_eq!(sps.pic_height_in_min_cbs_y(), 68);
770 assert_eq!(sps.pic_height_in_ctbs_y(), 35);
771 assert_eq!(sps.pic_size_in_min_cbs_y(), 8160);
772 assert_eq!(sps.pic_size_in_ctbs_y(), 2135);
773 assert_eq!(sps.pic_size_in_samples_y(), 2088960);
774 assert_eq!(sps.pic_width_in_samples_c(), 960);
775 assert_eq!(sps.pic_height_in_samples_c(), 544);
776 assert_eq!(sps.ctb_width_c(), 16);
777 assert_eq!(sps.ctb_height_c(), 16);
778 assert_eq!(sps.min_tb_log2_size_y(), 2);
779 assert_eq!(sps.max_tb_log2_size_y(), 5);
780 assert_eq!(sps.raw_ctu_bits(), 12288);
781 insta::assert_debug_snapshot!(nalu);
782 }
783
784 #[test]
785 fn test_sps_parse3() {
786 let data = b"\x42\x01\x01\x22\x20\x00\x00\x03\x00\x90\x00\x00\x03\x00\x00\x03\x00\x99\xA0\x01\xE0\x20\x02\x1C\x4D\x8D\x35\x92\x4F\x84\x14\x70\xF1\xC0\x90\x3B\x0E\x18\x36\x1A\x08\x42\xF0\x81\x21\x00\x88\x40\x10\x06\xE1\xA3\x06\xC3\x41\x08\x5C\xA0\xA0\x21\x04\x41\x70\xB0\x2A\x0A\xC2\x80\x35\x40\x70\x80\xE0\x07\xD0\x2B\x41\x80\xA8\x20\x0B\x85\x81\x50\x56\x14\x01\xAA\x03\x84\x07\x00\x3E\x81\x58\xA1\x0D\x35\xE9\xE8\x60\xD7\x43\x03\x41\xB1\xB8\xC0\xD0\x70\x3A\x1B\x1B\x18\x1A\x0E\x43\x21\x30\xC8\x60\x24\x18\x10\x1F\x1F\x1C\x1E\x30\x74\x26\x12\x0E\x0C\x04\x30\x40\x38\x10\x82\x00\x94\x0F\xF0\x86\x9A\xF2\x17\x20\x48\x26\x59\x02\x41\x20\x98\x4F\x09\x04\x83\x81\xD0\x98\x4E\x12\x09\x07\x21\x90\x98\x5C\x2C\x12\x0C\x08\x0F\x8F\x8E\x0F\x18\x3A\x13\x09\x07\x06\x02\x18\x20\x1C\x08\x41\x00\x4A\x07\xF2\x86\x89\x4D\x08\x2C\x83\x8E\x52\x18\x17\x02\xF2\xC8\x0B\x80\xDC\x06\xB0\x5F\x82\xE0\x35\x03\xA0\x66\x06\xB0\x63\x06\x00\x6A\x06\x40\xE0\x0B\x20\x73\x06\x60\xC8\x0E\x40\x58\x03\x90\x0A\xB0\x77\x07\x40\x2A\x81\xC7\xFF\xC1\x24\x34\x49\x8E\x61\x82\x62\x0C\x72\x90\xC0\xB8\x17\x96\x40\x5C\x06\xE0\x35\x82\xFC\x17\x01\xA8\x1D\x03\x30\x35\x83\x18\x30\x03\x50\x32\x07\x00\x59\x03\x98\x33\x06\x40\x72\x02\xC0\x1C\x80\x55\x83\xB8\x3A\x01\x54\x0E\x3F\xFE\x09\x0A\x10\xE9\xAF\x4F\x43\x06\xBA\x18\x1A\x0D\x8D\xC6\x06\x83\x81\xD0\xD8\xD8\xC0\xD0\x72\x19\x09\x86\x43\x01\x20\xC0\x80\xF8\xF8\xE0\xF1\x83\xA1\x30\x90\x70\x60\x21\x82\x01\xC0\x84\x10\x04\xA0\x7F\x84\x3A\x6B\xC8\x5C\x81\x20\x99\x64\x09\x04\x82\x61\x3C\x24\x12\x0E\x07\x42\x61\x38\x48\x24\x1C\x86\x42\x61\x70\xB0\x48\x30\x20\x3E\x3E\x38\x3C\x60\xE8\x4C\x24\x1C\x18\x08\x60\x80\x70\x21\x04\x01\x28\x1F\xCA\x1A\x92\x9A\x10\x59\x07\x1C\xA4\x30\x2E\x05\xE5\x90\x17\x01\xB8\x0D\x60\xBF\x05\xC0\x6A\x07\x40\xCC\x0D\x60\xC6\x0C\x00\xD4\x0C\x81\xC0\x16\x40\xE6\x0C\xC1\x90\x1C\x80\xB0\x07\x20\x15\x60\xEE\x0E\x80\x55\x03\x8F\xFF\x82\x48\x6A\x49\x8E\x61\x82\x62\x0C\x72\x90\xC0\xB8\x17\x96\x40\x5C\x06\xE0\x35\x82\xFC\x17\x01\xA8\x1D\x03\x30\x35\x83\x18\x30\x03\x50\x32\x07\x00\x59\x03\x98\x33\x06\x40\x72\x02\xC0\x1C\x80\x55\x83\xB8\x3A\x01\x54\x0E\x3F\xFE\x09\x0A\x10\xE9\xAF\x4F\x43\x06\xBA\x18\x1A\x0D\x8D\xC6\x06\x83\x81\xD0\xD8\xD8\xC0\xD0\x72\x19\x09\x86\x43\x01\x20\xC0\x80\xF8\xF8\xE0\xF1\x83\xA1\x30\x90\x70\x60\x21\x82\x01\xC0\x84\x10\x04\xA0\x7F\x86\xA4\x98\xE6\x18\x26\x20\xC7\x29\x0C\x0B\x81\x79\x64\x05\xC0\x6E\x03\x58\x2F\xC1\x70\x1A\x81\xD0\x33\x03\x58\x31\x83\x00\x35\x03\x20\x70\x05\x90\x39\x83\x30\x64\x07\x20\x2C\x01\xC8\x05\x58\x3B\x83\xA0\x15\x40\xE3\xFF\xE0\x91\x11\x5C\x96\xA5\xDE\x02\xD4\x24\x40\x26\xD9\x40\x00\x07\xD2\x00\x01\xD4\xC0\x3E\x46\x81\x8D\xC0\x00\x26\x25\xA0\x00\x13\x12\xD0\x00\x04\xC4\xB4\x00\x02\x62\x5A\x8B\x84\x02\x08\xA2\x00\x01\x00\x08\x44\x01\xC1\x72\x43\x8D\x62\x24\x00\x00\x00\x14";
788
789 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
790 let sps = &nalu.rbsp;
791
792 assert_eq!(sps.cropped_width(), 3840);
793 assert_eq!(sps.cropped_height(), 2160);
794 assert_eq!(sps.chroma_array_type(), 1);
795 assert_eq!(sps.sub_width_c(), 2);
796 assert_eq!(sps.sub_height_c(), 2);
797 assert_eq!(sps.bit_depth_y(), 10);
798 assert_eq!(sps.qp_bd_offset_y(), 60);
799 assert_eq!(sps.bit_depth_c(), 10);
800 assert_eq!(sps.qp_bd_offset_c(), 60);
801 assert_eq!(sps.max_pic_order_cnt_lsb(), 65536);
802 assert_eq!(sps.min_cb_log2_size_y(), 3);
803 assert_eq!(sps.ctb_log2_size_y(), 6);
804 assert_eq!(sps.min_cb_size_y(), 8);
805 assert_eq!(sps.ctb_size_y().get(), 64);
806 assert_eq!(sps.pic_width_in_min_cbs_y(), 480);
807 assert_eq!(sps.pic_width_in_ctbs_y(), 61);
808 assert_eq!(sps.pic_height_in_min_cbs_y(), 270);
809 assert_eq!(sps.pic_height_in_ctbs_y(), 34);
810 assert_eq!(sps.pic_size_in_min_cbs_y(), 129600);
811 assert_eq!(sps.pic_size_in_ctbs_y(), 2074);
812 assert_eq!(sps.pic_size_in_samples_y(), 8294400);
813 assert_eq!(sps.pic_width_in_samples_c(), 1920);
814 assert_eq!(sps.pic_height_in_samples_c(), 1080);
815 assert_eq!(sps.ctb_width_c(), 32);
816 assert_eq!(sps.ctb_height_c(), 32);
817 assert_eq!(sps.min_tb_log2_size_y(), 2);
818 assert_eq!(sps.max_tb_log2_size_y(), 5);
819 assert_eq!(sps.raw_ctu_bits(), 61440);
820 insta::assert_debug_snapshot!(nalu);
821 }
822
823 #[test]
824 fn test_sps_parse4() {
825 let data = b"\x42\x01\x01\x01\x60\x00\x00\x03\x00\x90\x00\x00\x03\x00\x00\x03\x00\xB4\xA0\x00\xF0\x08\x00\x43\x85\x96\x56\x69\x24\xC2\xB0\x16\x80\x80\x00\x00\x03\x00\x80\x00\x00\x05\x04\x22\x00\x01";
827
828 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
829 let sps = &nalu.rbsp;
830
831 assert_eq!(sps.cropped_width(), 7680);
832 assert_eq!(sps.cropped_height(), 4320);
833 assert_eq!(sps.chroma_array_type(), 1);
834 assert_eq!(sps.sub_width_c(), 2);
835 assert_eq!(sps.sub_height_c(), 2);
836 assert_eq!(sps.bit_depth_y(), 8);
837 assert_eq!(sps.qp_bd_offset_y(), 48);
838 assert_eq!(sps.bit_depth_c(), 8);
839 assert_eq!(sps.qp_bd_offset_c(), 48);
840 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
841 assert_eq!(sps.min_cb_log2_size_y(), 3);
842 assert_eq!(sps.ctb_log2_size_y(), 6);
843 assert_eq!(sps.min_cb_size_y(), 8);
844 assert_eq!(sps.ctb_size_y().get(), 64);
845 assert_eq!(sps.pic_width_in_min_cbs_y(), 960);
846 assert_eq!(sps.pic_width_in_ctbs_y(), 121);
847 assert_eq!(sps.pic_height_in_min_cbs_y(), 540);
848 assert_eq!(sps.pic_height_in_ctbs_y(), 68);
849 assert_eq!(sps.pic_size_in_min_cbs_y(), 518400);
850 assert_eq!(sps.pic_size_in_ctbs_y(), 8228);
851 assert_eq!(sps.pic_size_in_samples_y(), 33177600);
852 assert_eq!(sps.pic_width_in_samples_c(), 3840);
853 assert_eq!(sps.pic_height_in_samples_c(), 2160);
854 assert_eq!(sps.ctb_width_c(), 32);
855 assert_eq!(sps.ctb_height_c(), 32);
856 assert_eq!(sps.min_tb_log2_size_y(), 2);
857 assert_eq!(sps.max_tb_log2_size_y(), 5);
858 assert_eq!(sps.raw_ctu_bits(), 49152);
859 insta::assert_debug_snapshot!(nalu);
860 }
861
862 #[test]
863 fn test_sps_parse5() {
864 let data = b"\x42\x01\x01\x03\x70\x00\x00\x03\x00\x00\x03\x00\x00\x03\x00\x00\x03\x00\x78\xA0\x03\xC0\x80\x10\xE7\xF9\x7E\x49\x1B\x65\xB2\x22\x00\x01\x00\x07\x44\x01\xC1\x90\x95\x81\x12\x00\x00\x00\x14";
866
867 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
868 let sps = &nalu.rbsp;
869
870 assert_eq!(sps.cropped_width(), 1920);
871 assert_eq!(sps.cropped_height(), 1080);
872 assert_eq!(sps.chroma_array_type(), 1);
873 assert_eq!(sps.sub_width_c(), 2);
874 assert_eq!(sps.sub_height_c(), 2);
875 assert_eq!(sps.bit_depth_y(), 8);
876 assert_eq!(sps.qp_bd_offset_y(), 48);
877 assert_eq!(sps.bit_depth_c(), 8);
878 assert_eq!(sps.qp_bd_offset_c(), 48);
879 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
880 assert_eq!(sps.min_cb_log2_size_y(), 3);
881 assert_eq!(sps.ctb_log2_size_y(), 6);
882 assert_eq!(sps.min_cb_size_y(), 8);
883 assert_eq!(sps.ctb_size_y().get(), 64);
884 assert_eq!(sps.pic_width_in_min_cbs_y(), 240);
885 assert_eq!(sps.pic_width_in_ctbs_y(), 31);
886 assert_eq!(sps.pic_height_in_min_cbs_y(), 135);
887 assert_eq!(sps.pic_height_in_ctbs_y(), 17);
888 assert_eq!(sps.pic_size_in_min_cbs_y(), 32400);
889 assert_eq!(sps.pic_size_in_ctbs_y(), 527);
890 assert_eq!(sps.pic_size_in_samples_y(), 2073600);
891 assert_eq!(sps.pic_width_in_samples_c(), 960);
892 assert_eq!(sps.pic_height_in_samples_c(), 540);
893 assert_eq!(sps.ctb_width_c(), 32);
894 assert_eq!(sps.ctb_height_c(), 32);
895 assert_eq!(sps.min_tb_log2_size_y(), 2);
896 assert_eq!(sps.max_tb_log2_size_y(), 5);
897 assert_eq!(sps.raw_ctu_bits(), 49152);
898 insta::assert_debug_snapshot!(nalu);
899 }
900
901 #[test]
902 fn test_sps_parse6() {
903 let data = b"\x42\x01\x01\x24\x08\x00\x00\x03\x00\x9D\x08\x00\x00\x03\x00\x00\x99\xB0\x01\xE0\x20\x02\x1C\x4D\x94\xD6\xED\xBE\x41\x12\x64\xEB\x25\x11\x44\x1A\x6C\x9D\x64\xA2\x29\x09\x26\xBA\xF5\xFF\xEB\xFA\xFD\x7F\xEB\xF5\x44\x51\x04\x93\x5D\x7A\xFF\xF5\xFD\x7E\xBF\xF5\xFA\xC8\xA4\x92\x4D\x75\xEB\xFF\xD7\xF5\xFA\xFF\xD7\xEA\x88\xA2\x24\x93\x5D\x7A\xFF\xF5\xFD\x7E\xBF\xF5\xFA\xC8\x94\x08\x53\x49\x29\x24\x89\x55\x12\xA5\x2A\x94\xC1\x35\x01\x01\x01\x03\xB8\x40\x20\x80\xA2\x00\x01\x00\x07\x44\x01\xC0\x72\xB0\x3C\x90\x00\x00\x00\x13\x63\x6F\x6C\x72\x6E\x63\x6C\x78\x00\x01\x00\x01\x00\x01\x00\x00\x00\x00\x18";
905
906 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
907 let sps = &nalu.rbsp;
908
909 assert_eq!(sps.cropped_width(), 3840);
910 assert_eq!(sps.cropped_height(), 2160);
911 assert_eq!(sps.chroma_array_type(), 2);
912 assert_eq!(sps.sub_width_c(), 2);
913 assert_eq!(sps.sub_height_c(), 1);
914 assert_eq!(sps.bit_depth_y(), 10);
915 assert_eq!(sps.qp_bd_offset_y(), 60);
916 assert_eq!(sps.bit_depth_c(), 10);
917 assert_eq!(sps.qp_bd_offset_c(), 60);
918 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
919 assert_eq!(sps.min_cb_log2_size_y(), 3);
920 assert_eq!(sps.ctb_log2_size_y(), 5);
921 assert_eq!(sps.min_cb_size_y(), 8);
922 assert_eq!(sps.ctb_size_y().get(), 32);
923 assert_eq!(sps.pic_width_in_min_cbs_y(), 480);
924 assert_eq!(sps.pic_width_in_ctbs_y(), 121);
925 assert_eq!(sps.pic_height_in_min_cbs_y(), 270);
926 assert_eq!(sps.pic_height_in_ctbs_y(), 68);
927 assert_eq!(sps.pic_size_in_min_cbs_y(), 129600);
928 assert_eq!(sps.pic_size_in_ctbs_y(), 8228);
929 assert_eq!(sps.pic_size_in_samples_y(), 8294400);
930 assert_eq!(sps.pic_width_in_samples_c(), 1920);
931 assert_eq!(sps.pic_height_in_samples_c(), 2160);
932 assert_eq!(sps.ctb_width_c(), 16);
933 assert_eq!(sps.ctb_height_c(), 32);
934 assert_eq!(sps.min_tb_log2_size_y(), 2);
935 assert_eq!(sps.max_tb_log2_size_y(), 4);
936 assert_eq!(sps.raw_ctu_bits(), 20480);
937 insta::assert_debug_snapshot!(nalu);
938 }
939
940 #[test]
941 fn test_sps_parse_inter_ref_prediction() {
942 let data = b"\x42\x01\x01\x01\x60\x00\x00\x03\x00\x00\x03\x00\x00\x03\x00\x00\x03\x00\x00\xA0\x0B\x08\x04\x85\x96\x5E\x49\x1B\x60\xD9\x78\x88\x88\x8F\xE7\x9F\xCF\xE7\xF3\xF9\xFC\xF2\xFF\xFF\xFF\xCF\xE7\xF3\xF9\xFC\xFE\x7F\x3F\x3F\x9F\xCF\xE7\xF3\xF9\xDB\x20";
944
945 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
946 insta::assert_debug_snapshot!(nalu);
947 }
948
949 #[test]
950 fn test_forbidden_zero_bit() {
951 let data = [0x80];
953 let err = SpsNALUnit::parse(io::Cursor::new(data)).unwrap_err();
954 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
955 assert_eq!(err.to_string(), "forbidden_zero_bit is not zero");
956 }
957
958 #[test]
959 fn test_invalid_nalu_type() {
960 #[allow(clippy::unusual_byte_groupings)]
965 let data = [0b0_100000_0, 0b00000_001];
966 let err = SpsNALUnit::parse(io::Cursor::new(data)).unwrap_err();
967 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
968 assert_eq!(err.to_string(), "nal_unit_type is not SPS_NUT");
969 }
970}