1use std::io;
2use std::num::NonZero;
3
4use scuffle_bytes_util::{BitReader, EmulationPreventionIo};
5use scuffle_expgolomb::BitReaderExpGolombExt;
6
7use crate::NALUnitType;
8use crate::nal_unit_header::NALUnitHeader;
9use crate::range_check::range_check;
10use crate::rbsp_trailing_bits::rbsp_trailing_bits;
11
12mod conformance_window;
13mod long_term_ref_pics;
14mod pcm;
15mod profile_tier_level;
16mod scaling_list;
17mod sps_3d_extension;
18mod sps_multilayer_extension;
19mod sps_range_extension;
20mod sps_scc_extension;
21mod st_ref_pic_set;
22mod sub_layer_ordering_info;
23mod vui_parameters;
24
25pub use conformance_window::*;
26pub use long_term_ref_pics::*;
27pub use pcm::*;
28pub use profile_tier_level::*;
29pub use scaling_list::*;
30pub use sps_3d_extension::*;
31pub use sps_multilayer_extension::*;
32pub use sps_range_extension::*;
33pub use sps_scc_extension::*;
34pub use st_ref_pic_set::*;
35pub use sub_layer_ordering_info::*;
36pub use vui_parameters::*;
37
38#[derive(Debug, Clone, PartialEq)]
56pub struct SpsNALUnit {
57 pub nal_unit_header: NALUnitHeader,
59 pub rbsp: SpsRbsp,
61}
62
63impl SpsNALUnit {
64 pub fn parse(mut reader: impl io::Read) -> io::Result<Self> {
66 let nal_unit_header = NALUnitHeader::parse(&mut reader)?;
67 if nal_unit_header.nal_unit_type != NALUnitType::SpsNut {
68 return Err(io::Error::new(io::ErrorKind::InvalidData, "nal_unit_type is not SPS_NUT"));
69 }
70
71 let rbsp = SpsRbsp::parse(reader, nal_unit_header.nuh_layer_id)?;
72
73 Ok(SpsNALUnit { nal_unit_header, rbsp })
74 }
75}
76
77#[derive(Debug, Clone, PartialEq)]
86pub struct SpsRbsp {
87 pub sps_video_parameter_set_id: u8,
89 pub sps_max_sub_layers_minus1: u8,
94 pub sps_temporal_id_nesting_flag: bool,
98 pub profile_tier_level: ProfileTierLevel,
100 pub sps_seq_parameter_set_id: u64,
104 pub chroma_format_idc: u8,
108 pub separate_colour_plane_flag: bool,
114 pub pic_width_in_luma_samples: NonZero<u64>,
118 pub pic_height_in_luma_samples: NonZero<u64>,
122 pub conformance_window: ConformanceWindow,
126 pub bit_depth_luma_minus8: u8,
131 pub bit_depth_chroma_minus8: u8,
136 pub log2_max_pic_order_cnt_lsb_minus4: u8,
141 pub sub_layer_ordering_info: SubLayerOrderingInfo,
145 pub log2_min_luma_coding_block_size_minus3: u64,
149 pub log2_diff_max_min_luma_coding_block_size: u64,
151 pub log2_min_luma_transform_block_size_minus2: u64,
155 pub log2_diff_max_min_luma_transform_block_size: u64,
159 pub max_transform_hierarchy_depth_inter: u64,
163 pub max_transform_hierarchy_depth_intra: u64,
167 pub scaling_list_data: Option<ScalingListData>,
169 pub amp_enabled_flag: bool,
174 pub sample_adaptive_offset_enabled_flag: bool,
180 pub pcm: Option<Pcm>,
185 pub short_term_ref_pic_sets: ShortTermRefPicSets,
187 pub long_term_ref_pics: Option<LongTermRefPics>,
191 pub sps_temporal_mvp_enabled_flag: bool,
197 pub strong_intra_smoothing_enabled_flag: bool,
202 pub vui_parameters: Option<VuiParameters>,
204 pub range_extension: Option<SpsRangeExtension>,
206 pub multilayer_extension: Option<SpsMultilayerExtension>,
208 pub sps_3d_extension: Option<Sps3dExtension>,
210 pub scc_extension: Option<SpsSccExtension>,
212}
213
214impl SpsRbsp {
215 pub fn parse(reader: impl io::Read, nuh_layer_id: u8) -> io::Result<Self> {
221 let mut bit_reader = BitReader::new(EmulationPreventionIo::new(reader));
222
223 let sps_video_parameter_set_id = bit_reader.read_bits(4)? as u8;
224
225 let sps_max_sub_layers_minus1 = bit_reader.read_bits(3)? as u8;
226 range_check!(sps_max_sub_layers_minus1, 0, 6)?;
227
228 let sps_temporal_id_nesting_flag = bit_reader.read_bit()?;
229
230 if sps_max_sub_layers_minus1 == 0 && !sps_temporal_id_nesting_flag {
231 return Err(io::Error::new(
232 io::ErrorKind::InvalidData,
233 "sps_temporal_id_nesting_flag must be 1 when sps_max_sub_layers_minus1 is 0",
234 ));
235 }
236
237 let profile_tier_level = ProfileTierLevel::parse(&mut bit_reader, sps_max_sub_layers_minus1)?;
238
239 let sps_seq_parameter_set_id = bit_reader.read_exp_golomb()?;
240 range_check!(sps_seq_parameter_set_id, 0, 15)?;
241
242 let chroma_format_idc = bit_reader.read_exp_golomb()?;
243 range_check!(chroma_format_idc, 0, 3)?;
244 let chroma_format_idc = chroma_format_idc as u8;
245
246 let mut separate_colour_plane_flag = false;
247 if chroma_format_idc == 3 {
248 separate_colour_plane_flag = bit_reader.read_bit()?;
249 }
250
251 let sub_width_c = if chroma_format_idc == 1 || chroma_format_idc == 2 {
253 2
254 } else {
255 1
256 };
257 let sub_height_c = if chroma_format_idc == 1 { 2 } else { 1 };
258
259 let pic_width_in_luma_samples = NonZero::new(bit_reader.read_exp_golomb()?)
260 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "pic_width_in_luma_samples must not be 0"))?;
261
262 let pic_height_in_luma_samples = NonZero::new(bit_reader.read_exp_golomb()?)
263 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "pic_height_in_luma_samples must not be 0"))?;
264
265 let conformance_window_flag = bit_reader.read_bit()?;
266
267 let conformance_window = conformance_window_flag
268 .then(|| ConformanceWindow::parse(&mut bit_reader))
269 .transpose()?
270 .unwrap_or_default();
271
272 let bit_depth_luma_minus8 = bit_reader.read_exp_golomb()?;
273 range_check!(bit_depth_luma_minus8, 0, 8)?;
274 let bit_depth_luma_minus8 = bit_depth_luma_minus8 as u8;
275 let bit_depth_y = 8 + bit_depth_luma_minus8; let bit_depth_chroma_minus8 = bit_reader.read_exp_golomb()?;
277 range_check!(bit_depth_chroma_minus8, 0, 8)?;
278 let bit_depth_chroma_minus8 = bit_depth_chroma_minus8 as u8;
279 let bit_depth_c = 8 + bit_depth_chroma_minus8; let log2_max_pic_order_cnt_lsb_minus4 = bit_reader.read_exp_golomb()?;
282 range_check!(log2_max_pic_order_cnt_lsb_minus4, 0, 12)?;
283 let log2_max_pic_order_cnt_lsb_minus4 = log2_max_pic_order_cnt_lsb_minus4 as u8;
284
285 let sps_sub_layer_ordering_info_present_flag = bit_reader.read_bit()?;
286 let sub_layer_ordering_info = SubLayerOrderingInfo::parse(
287 &mut bit_reader,
288 sps_sub_layer_ordering_info_present_flag,
289 sps_max_sub_layers_minus1,
290 )?;
291
292 let log2_min_luma_coding_block_size_minus3 = bit_reader.read_exp_golomb()?;
293 let log2_diff_max_min_luma_coding_block_size = bit_reader.read_exp_golomb()?;
294
295 let min_cb_log2_size_y = log2_min_luma_coding_block_size_minus3 + 3;
296 let ctb_log2_size_y = min_cb_log2_size_y + log2_diff_max_min_luma_coding_block_size;
297
298 let log2_min_luma_transform_block_size_minus2 = bit_reader.read_exp_golomb()?;
299
300 let min_tb_log2_size_y = log2_min_luma_transform_block_size_minus2 + 2;
301
302 let log2_diff_max_min_luma_transform_block_size = bit_reader.read_exp_golomb()?;
303 let max_transform_hierarchy_depth_inter = bit_reader.read_exp_golomb()?;
304 range_check!(max_transform_hierarchy_depth_inter, 0, ctb_log2_size_y - min_tb_log2_size_y)?;
305 let max_transform_hierarchy_depth_intra = bit_reader.read_exp_golomb()?;
306 range_check!(max_transform_hierarchy_depth_intra, 0, ctb_log2_size_y - min_tb_log2_size_y)?;
307
308 let scaling_list_enabled_flag = bit_reader.read_bit()?;
309
310 let mut scaling_list_data = None;
311 if scaling_list_enabled_flag {
312 let sps_scaling_list_data_present_flag = bit_reader.read_bit()?;
313
314 if sps_scaling_list_data_present_flag {
315 scaling_list_data = Some(ScalingListData::parse(&mut bit_reader)?);
316 }
317 }
318
319 let amp_enabled_flag = bit_reader.read_bit()?;
320 let sample_adaptive_offset_enabled_flag = bit_reader.read_bit()?;
321
322 let mut pcm = None;
323 let pcm_enabled_flag = bit_reader.read_bit()?;
324 if pcm_enabled_flag {
325 pcm = Some(Pcm::parse(
326 &mut bit_reader,
327 bit_depth_y,
328 bit_depth_c,
329 min_cb_log2_size_y,
330 ctb_log2_size_y,
331 )?);
332 }
333
334 let num_short_term_ref_pic_sets = bit_reader.read_exp_golomb()?;
335 range_check!(num_short_term_ref_pic_sets, 0, 64)?;
336 let num_short_term_ref_pic_sets = num_short_term_ref_pic_sets as u8;
337 let short_term_ref_pic_sets = ShortTermRefPicSets::parse(
338 &mut bit_reader,
339 num_short_term_ref_pic_sets as usize,
340 nuh_layer_id,
341 *sub_layer_ordering_info
342 .sps_max_dec_pic_buffering_minus1
343 .last()
344 .expect("unreachable: cannot be empty"),
345 )?;
346
347 let mut long_term_ref_pics = None;
348 let long_term_ref_pics_present_flag = bit_reader.read_bit()?;
349 if long_term_ref_pics_present_flag {
350 long_term_ref_pics = Some(LongTermRefPics::parse(&mut bit_reader, log2_max_pic_order_cnt_lsb_minus4)?);
351 }
352
353 let sps_temporal_mvp_enabled_flag = bit_reader.read_bit()?;
354 let strong_intra_smoothing_enabled_flag = bit_reader.read_bit()?;
355
356 let mut vui_parameters = None;
357 let vui_parameters_present_flag = bit_reader.read_bit()?;
358 if vui_parameters_present_flag {
359 vui_parameters = Some(VuiParameters::parse(
360 &mut bit_reader,
361 sps_max_sub_layers_minus1,
362 bit_depth_y,
363 bit_depth_c,
364 chroma_format_idc,
365 &profile_tier_level.general_profile,
366 &conformance_window,
367 sub_width_c,
368 pic_width_in_luma_samples,
369 sub_height_c,
370 pic_height_in_luma_samples,
371 )?);
372 }
373
374 let mut range_extension = None;
376 let mut multilayer_extension = None;
377 let mut sps_3d_extension = None;
378 let mut scc_extension = None;
379
380 let sps_extension_flag = bit_reader.read_bit()?;
381 if sps_extension_flag {
382 let sps_range_extension_flag = bit_reader.read_bit()?;
383 let sps_multilayer_extension_flag = bit_reader.read_bit()?;
384 let sps_3d_extension_flag = bit_reader.read_bit()?;
385 let sps_scc_extension_flag = bit_reader.read_bit()?;
386 let sps_extension_4bits = bit_reader.read_bits(4)? as u8;
387
388 if sps_extension_4bits != 0 {
389 return Err(io::Error::new(io::ErrorKind::InvalidData, "sps_extension_4bits must be 0"));
390 }
391
392 if sps_range_extension_flag {
393 range_extension = Some(SpsRangeExtension::parse(&mut bit_reader)?);
394 }
395
396 if sps_multilayer_extension_flag {
397 multilayer_extension = Some(SpsMultilayerExtension::parse(&mut bit_reader)?);
398 }
399
400 if sps_3d_extension_flag {
401 sps_3d_extension = Some(Sps3dExtension::parse(&mut bit_reader, min_cb_log2_size_y, ctb_log2_size_y)?);
402 }
403
404 if sps_scc_extension_flag {
405 scc_extension = Some(SpsSccExtension::parse(
406 &mut bit_reader,
407 chroma_format_idc,
408 bit_depth_y,
409 bit_depth_c,
410 )?);
411 }
412
413 }
415
416 rbsp_trailing_bits(&mut bit_reader)?;
417
418 Ok(SpsRbsp {
419 sps_video_parameter_set_id,
420 sps_max_sub_layers_minus1,
421 sps_temporal_id_nesting_flag,
422 profile_tier_level,
423 sps_seq_parameter_set_id,
424 chroma_format_idc,
425 separate_colour_plane_flag,
426 pic_width_in_luma_samples,
427 pic_height_in_luma_samples,
428 conformance_window,
429 bit_depth_luma_minus8,
430 bit_depth_chroma_minus8,
431 log2_max_pic_order_cnt_lsb_minus4,
432 sub_layer_ordering_info,
433 log2_min_luma_coding_block_size_minus3,
434 log2_diff_max_min_luma_coding_block_size,
435 log2_min_luma_transform_block_size_minus2,
436 log2_diff_max_min_luma_transform_block_size,
437 max_transform_hierarchy_depth_inter,
438 max_transform_hierarchy_depth_intra,
439 scaling_list_data,
440 amp_enabled_flag,
441 sample_adaptive_offset_enabled_flag,
442 pcm,
443 short_term_ref_pic_sets,
444 long_term_ref_pics,
445 sps_temporal_mvp_enabled_flag,
446 strong_intra_smoothing_enabled_flag,
447 vui_parameters,
448 range_extension,
449 multilayer_extension,
450 sps_3d_extension,
451 scc_extension,
452 })
453 }
454
455 pub fn cropped_width(&self) -> u64 {
463 self.pic_width_in_luma_samples.get()
464 - self.sub_width_c() as u64
465 * (self.conformance_window.conf_win_left_offset + self.conformance_window.conf_win_right_offset)
466 }
467
468 pub fn cropped_height(&self) -> u64 {
476 self.pic_height_in_luma_samples.get()
477 - self.sub_height_c() as u64
478 * (self.conformance_window.conf_win_top_offset + self.conformance_window.conf_win_bottom_offset)
479 }
480
481 pub fn chroma_array_type(&self) -> u8 {
486 if self.separate_colour_plane_flag {
487 0
488 } else {
489 self.chroma_format_idc
490 }
491 }
492
493 pub fn sub_width_c(&self) -> u8 {
495 if self.chroma_format_idc == 1 || self.chroma_format_idc == 2 {
496 2
497 } else {
498 1
499 }
500 }
501
502 pub fn sub_height_c(&self) -> u8 {
504 if self.chroma_format_idc == 1 { 2 } else { 1 }
505 }
506
507 pub fn bit_depth_y(&self) -> u8 {
513 8 + self.bit_depth_luma_minus8
514 }
515
516 pub fn qp_bd_offset_y(&self) -> u8 {
522 6 * self.bit_depth_y()
523 }
524
525 #[inline]
531 pub fn bit_depth_c(&self) -> u8 {
532 8 + self.bit_depth_chroma_minus8
533 }
534
535 pub fn qp_bd_offset_c(&self) -> u8 {
541 6 * self.bit_depth_c()
542 }
543
544 pub fn max_pic_order_cnt_lsb(&self) -> u32 {
550 2u32.pow(self.log2_max_pic_order_cnt_lsb_minus4 as u32 + 4)
551 }
552
553 pub fn min_cb_log2_size_y(&self) -> u64 {
557 self.log2_min_luma_coding_block_size_minus3 + 3
558 }
559
560 pub fn ctb_log2_size_y(&self) -> u64 {
564 self.min_cb_log2_size_y() + self.log2_diff_max_min_luma_coding_block_size
565 }
566
567 pub fn min_cb_size_y(&self) -> u64 {
571 1 << self.min_cb_log2_size_y()
572 }
573
574 pub fn ctb_size_y(&self) -> NonZero<u64> {
578 NonZero::new(1 << self.ctb_log2_size_y()).unwrap()
579 }
580
581 pub fn pic_width_in_min_cbs_y(&self) -> u64 {
585 self.pic_width_in_luma_samples.get() / self.min_cb_size_y()
586 }
587
588 pub fn pic_width_in_ctbs_y(&self) -> u64 {
592 (self.pic_width_in_luma_samples.get() / self.ctb_size_y()) + 1
593 }
594
595 pub fn pic_height_in_min_cbs_y(&self) -> u64 {
599 self.pic_height_in_luma_samples.get() / self.min_cb_size_y()
600 }
601
602 pub fn pic_height_in_ctbs_y(&self) -> u64 {
606 (self.pic_height_in_luma_samples.get() / self.ctb_size_y()) + 1
607 }
608
609 pub fn pic_size_in_min_cbs_y(&self) -> u64 {
613 self.pic_width_in_min_cbs_y() * self.pic_height_in_min_cbs_y()
614 }
615
616 pub fn pic_size_in_ctbs_y(&self) -> u64 {
620 self.pic_width_in_ctbs_y() * self.pic_height_in_ctbs_y()
621 }
622
623 pub fn pic_size_in_samples_y(&self) -> u64 {
627 self.pic_width_in_luma_samples.get() * self.pic_height_in_luma_samples.get()
628 }
629
630 pub fn pic_width_in_samples_c(&self) -> u64 {
634 self.pic_width_in_luma_samples.get() / self.sub_width_c() as u64
635 }
636
637 pub fn pic_height_in_samples_c(&self) -> u64 {
641 self.pic_height_in_luma_samples.get() / self.sub_height_c() as u64
642 }
643
644 pub fn ctb_width_c(&self) -> u64 {
650 if self.chroma_format_idc == 0 || self.separate_colour_plane_flag {
651 0
652 } else {
653 self.ctb_size_y().get() / self.sub_width_c() as u64
654 }
655 }
656
657 pub fn ctb_height_c(&self) -> u64 {
663 if self.chroma_format_idc == 0 || self.separate_colour_plane_flag {
664 0
665 } else {
666 self.ctb_size_y().get() / self.sub_height_c() as u64
667 }
668 }
669
670 pub fn min_tb_log2_size_y(&self) -> u64 {
677 self.log2_min_luma_transform_block_size_minus2 + 2
678 }
679
680 pub fn max_tb_log2_size_y(&self) -> u64 {
686 self.log2_min_luma_transform_block_size_minus2 + 2 + self.log2_diff_max_min_luma_transform_block_size
687 }
688
689 pub fn raw_ctu_bits(&self) -> u64 {
693 let ctb_size_y = self.ctb_size_y().get();
694 ctb_size_y * ctb_size_y * self.bit_depth_y() as u64
695 + 2 * (self.ctb_width_c() * self.ctb_height_c()) * self.bit_depth_c() as u64
696 }
697}
698
699#[cfg(test)]
700#[cfg_attr(all(test, coverage_nightly), coverage(off))]
701mod tests {
702 use std::io;
703
704 use crate::SpsNALUnit;
705
706 #[test]
709 fn test_sps_parse() {
710 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";
711
712 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
713 let sps = &nalu.rbsp;
714
715 assert_eq!(sps.cropped_width(), 2560);
716 assert_eq!(sps.cropped_height(), 1440);
717 assert_eq!(sps.chroma_array_type(), 1);
718 assert_eq!(sps.sub_width_c(), 2);
719 assert_eq!(sps.sub_height_c(), 2);
720 assert_eq!(sps.bit_depth_y(), 8);
721 assert_eq!(sps.qp_bd_offset_y(), 48);
722 assert_eq!(sps.bit_depth_c(), 8);
723 assert_eq!(sps.qp_bd_offset_c(), 48);
724 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
725 assert_eq!(sps.min_cb_log2_size_y(), 4);
726 assert_eq!(sps.ctb_log2_size_y(), 5);
727 assert_eq!(sps.min_cb_size_y(), 16);
728 assert_eq!(sps.ctb_size_y().get(), 32);
729 assert_eq!(sps.pic_width_in_min_cbs_y(), 160);
730 assert_eq!(sps.pic_width_in_ctbs_y(), 81);
731 assert_eq!(sps.pic_height_in_min_cbs_y(), 90);
732 assert_eq!(sps.pic_height_in_ctbs_y(), 46);
733 assert_eq!(sps.pic_size_in_min_cbs_y(), 14400);
734 assert_eq!(sps.pic_size_in_ctbs_y(), 3726);
735 assert_eq!(sps.pic_size_in_samples_y(), 3686400);
736 assert_eq!(sps.pic_width_in_samples_c(), 1280);
737 assert_eq!(sps.pic_height_in_samples_c(), 720);
738 assert_eq!(sps.ctb_width_c(), 16);
739 assert_eq!(sps.ctb_height_c(), 16);
740 assert_eq!(sps.min_tb_log2_size_y(), 2);
741 assert_eq!(sps.max_tb_log2_size_y(), 5);
742 assert_eq!(sps.raw_ctu_bits(), 12288);
743 insta::assert_debug_snapshot!(nalu);
744 }
745
746 #[test]
747 fn test_sps_parse2() {
748 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";
750
751 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
752 let sps = &nalu.rbsp;
753
754 assert_eq!(sps.cropped_width(), 1920);
755 assert_eq!(sps.cropped_height(), 1080);
756 assert_eq!(sps.chroma_array_type(), 1);
757 assert_eq!(sps.sub_width_c(), 2);
758 assert_eq!(sps.sub_height_c(), 2);
759 assert_eq!(sps.bit_depth_y(), 8);
760 assert_eq!(sps.qp_bd_offset_y(), 48);
761 assert_eq!(sps.bit_depth_c(), 8);
762 assert_eq!(sps.qp_bd_offset_c(), 48);
763 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
764 assert_eq!(sps.min_cb_log2_size_y(), 4);
765 assert_eq!(sps.ctb_log2_size_y(), 5);
766 assert_eq!(sps.min_cb_size_y(), 16);
767 assert_eq!(sps.ctb_size_y().get(), 32);
768 assert_eq!(sps.pic_width_in_min_cbs_y(), 120);
769 assert_eq!(sps.pic_width_in_ctbs_y(), 61);
770 assert_eq!(sps.pic_height_in_min_cbs_y(), 68);
771 assert_eq!(sps.pic_height_in_ctbs_y(), 35);
772 assert_eq!(sps.pic_size_in_min_cbs_y(), 8160);
773 assert_eq!(sps.pic_size_in_ctbs_y(), 2135);
774 assert_eq!(sps.pic_size_in_samples_y(), 2088960);
775 assert_eq!(sps.pic_width_in_samples_c(), 960);
776 assert_eq!(sps.pic_height_in_samples_c(), 544);
777 assert_eq!(sps.ctb_width_c(), 16);
778 assert_eq!(sps.ctb_height_c(), 16);
779 assert_eq!(sps.min_tb_log2_size_y(), 2);
780 assert_eq!(sps.max_tb_log2_size_y(), 5);
781 assert_eq!(sps.raw_ctu_bits(), 12288);
782 insta::assert_debug_snapshot!(nalu);
783 }
784
785 #[test]
786 fn test_sps_parse3() {
787 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";
789
790 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
791 let sps = &nalu.rbsp;
792
793 assert_eq!(sps.cropped_width(), 3840);
794 assert_eq!(sps.cropped_height(), 2160);
795 assert_eq!(sps.chroma_array_type(), 1);
796 assert_eq!(sps.sub_width_c(), 2);
797 assert_eq!(sps.sub_height_c(), 2);
798 assert_eq!(sps.bit_depth_y(), 10);
799 assert_eq!(sps.qp_bd_offset_y(), 60);
800 assert_eq!(sps.bit_depth_c(), 10);
801 assert_eq!(sps.qp_bd_offset_c(), 60);
802 assert_eq!(sps.max_pic_order_cnt_lsb(), 65536);
803 assert_eq!(sps.min_cb_log2_size_y(), 3);
804 assert_eq!(sps.ctb_log2_size_y(), 6);
805 assert_eq!(sps.min_cb_size_y(), 8);
806 assert_eq!(sps.ctb_size_y().get(), 64);
807 assert_eq!(sps.pic_width_in_min_cbs_y(), 480);
808 assert_eq!(sps.pic_width_in_ctbs_y(), 61);
809 assert_eq!(sps.pic_height_in_min_cbs_y(), 270);
810 assert_eq!(sps.pic_height_in_ctbs_y(), 34);
811 assert_eq!(sps.pic_size_in_min_cbs_y(), 129600);
812 assert_eq!(sps.pic_size_in_ctbs_y(), 2074);
813 assert_eq!(sps.pic_size_in_samples_y(), 8294400);
814 assert_eq!(sps.pic_width_in_samples_c(), 1920);
815 assert_eq!(sps.pic_height_in_samples_c(), 1080);
816 assert_eq!(sps.ctb_width_c(), 32);
817 assert_eq!(sps.ctb_height_c(), 32);
818 assert_eq!(sps.min_tb_log2_size_y(), 2);
819 assert_eq!(sps.max_tb_log2_size_y(), 5);
820 assert_eq!(sps.raw_ctu_bits(), 61440);
821 insta::assert_debug_snapshot!(nalu);
822 }
823
824 #[test]
825 fn test_sps_parse4() {
826 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";
828
829 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
830 let sps = &nalu.rbsp;
831
832 assert_eq!(sps.cropped_width(), 7680);
833 assert_eq!(sps.cropped_height(), 4320);
834 assert_eq!(sps.chroma_array_type(), 1);
835 assert_eq!(sps.sub_width_c(), 2);
836 assert_eq!(sps.sub_height_c(), 2);
837 assert_eq!(sps.bit_depth_y(), 8);
838 assert_eq!(sps.qp_bd_offset_y(), 48);
839 assert_eq!(sps.bit_depth_c(), 8);
840 assert_eq!(sps.qp_bd_offset_c(), 48);
841 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
842 assert_eq!(sps.min_cb_log2_size_y(), 3);
843 assert_eq!(sps.ctb_log2_size_y(), 6);
844 assert_eq!(sps.min_cb_size_y(), 8);
845 assert_eq!(sps.ctb_size_y().get(), 64);
846 assert_eq!(sps.pic_width_in_min_cbs_y(), 960);
847 assert_eq!(sps.pic_width_in_ctbs_y(), 121);
848 assert_eq!(sps.pic_height_in_min_cbs_y(), 540);
849 assert_eq!(sps.pic_height_in_ctbs_y(), 68);
850 assert_eq!(sps.pic_size_in_min_cbs_y(), 518400);
851 assert_eq!(sps.pic_size_in_ctbs_y(), 8228);
852 assert_eq!(sps.pic_size_in_samples_y(), 33177600);
853 assert_eq!(sps.pic_width_in_samples_c(), 3840);
854 assert_eq!(sps.pic_height_in_samples_c(), 2160);
855 assert_eq!(sps.ctb_width_c(), 32);
856 assert_eq!(sps.ctb_height_c(), 32);
857 assert_eq!(sps.min_tb_log2_size_y(), 2);
858 assert_eq!(sps.max_tb_log2_size_y(), 5);
859 assert_eq!(sps.raw_ctu_bits(), 49152);
860 insta::assert_debug_snapshot!(nalu);
861 }
862
863 #[test]
864 fn test_sps_parse5() {
865 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";
867
868 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
869 let sps = &nalu.rbsp;
870
871 assert_eq!(sps.cropped_width(), 1920);
872 assert_eq!(sps.cropped_height(), 1080);
873 assert_eq!(sps.chroma_array_type(), 1);
874 assert_eq!(sps.sub_width_c(), 2);
875 assert_eq!(sps.sub_height_c(), 2);
876 assert_eq!(sps.bit_depth_y(), 8);
877 assert_eq!(sps.qp_bd_offset_y(), 48);
878 assert_eq!(sps.bit_depth_c(), 8);
879 assert_eq!(sps.qp_bd_offset_c(), 48);
880 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
881 assert_eq!(sps.min_cb_log2_size_y(), 3);
882 assert_eq!(sps.ctb_log2_size_y(), 6);
883 assert_eq!(sps.min_cb_size_y(), 8);
884 assert_eq!(sps.ctb_size_y().get(), 64);
885 assert_eq!(sps.pic_width_in_min_cbs_y(), 240);
886 assert_eq!(sps.pic_width_in_ctbs_y(), 31);
887 assert_eq!(sps.pic_height_in_min_cbs_y(), 135);
888 assert_eq!(sps.pic_height_in_ctbs_y(), 17);
889 assert_eq!(sps.pic_size_in_min_cbs_y(), 32400);
890 assert_eq!(sps.pic_size_in_ctbs_y(), 527);
891 assert_eq!(sps.pic_size_in_samples_y(), 2073600);
892 assert_eq!(sps.pic_width_in_samples_c(), 960);
893 assert_eq!(sps.pic_height_in_samples_c(), 540);
894 assert_eq!(sps.ctb_width_c(), 32);
895 assert_eq!(sps.ctb_height_c(), 32);
896 assert_eq!(sps.min_tb_log2_size_y(), 2);
897 assert_eq!(sps.max_tb_log2_size_y(), 5);
898 assert_eq!(sps.raw_ctu_bits(), 49152);
899 insta::assert_debug_snapshot!(nalu);
900 }
901
902 #[test]
903 fn test_sps_parse6() {
904 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";
906
907 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
908 let sps = &nalu.rbsp;
909
910 assert_eq!(sps.cropped_width(), 3840);
911 assert_eq!(sps.cropped_height(), 2160);
912 assert_eq!(sps.chroma_array_type(), 2);
913 assert_eq!(sps.sub_width_c(), 2);
914 assert_eq!(sps.sub_height_c(), 1);
915 assert_eq!(sps.bit_depth_y(), 10);
916 assert_eq!(sps.qp_bd_offset_y(), 60);
917 assert_eq!(sps.bit_depth_c(), 10);
918 assert_eq!(sps.qp_bd_offset_c(), 60);
919 assert_eq!(sps.max_pic_order_cnt_lsb(), 256);
920 assert_eq!(sps.min_cb_log2_size_y(), 3);
921 assert_eq!(sps.ctb_log2_size_y(), 5);
922 assert_eq!(sps.min_cb_size_y(), 8);
923 assert_eq!(sps.ctb_size_y().get(), 32);
924 assert_eq!(sps.pic_width_in_min_cbs_y(), 480);
925 assert_eq!(sps.pic_width_in_ctbs_y(), 121);
926 assert_eq!(sps.pic_height_in_min_cbs_y(), 270);
927 assert_eq!(sps.pic_height_in_ctbs_y(), 68);
928 assert_eq!(sps.pic_size_in_min_cbs_y(), 129600);
929 assert_eq!(sps.pic_size_in_ctbs_y(), 8228);
930 assert_eq!(sps.pic_size_in_samples_y(), 8294400);
931 assert_eq!(sps.pic_width_in_samples_c(), 1920);
932 assert_eq!(sps.pic_height_in_samples_c(), 2160);
933 assert_eq!(sps.ctb_width_c(), 16);
934 assert_eq!(sps.ctb_height_c(), 32);
935 assert_eq!(sps.min_tb_log2_size_y(), 2);
936 assert_eq!(sps.max_tb_log2_size_y(), 4);
937 assert_eq!(sps.raw_ctu_bits(), 20480);
938 insta::assert_debug_snapshot!(nalu);
939 }
940
941 #[test]
942 fn test_sps_parse_inter_ref_prediction() {
943 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";
945
946 let nalu = SpsNALUnit::parse(io::Cursor::new(data)).unwrap();
947 insta::assert_debug_snapshot!(nalu);
948 }
949
950 #[test]
951 fn test_forbidden_zero_bit() {
952 let data = [0x80];
954 let err = SpsNALUnit::parse(io::Cursor::new(data)).unwrap_err();
955 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
956 assert_eq!(err.to_string(), "forbidden_zero_bit is not zero");
957 }
958
959 #[test]
960 fn test_invalid_nalu_type() {
961 #[allow(clippy::unusual_byte_groupings)]
966 let data = [0b0_100000_0, 0b00000_001];
967 let err = SpsNALUnit::parse(io::Cursor::new(data)).unwrap_err();
968 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
969 assert_eq!(err.to_string(), "nal_unit_type is not SPS_NUT");
970 }
971}