scuffle_h265/
nal_unit_header.rs1use std::io;
2use std::num::NonZero;
3
4use scuffle_bytes_util::{BitReader, range_check};
5
6use crate::NALUnitType;
7
8#[derive(Debug, Clone, PartialEq)]
13pub struct NALUnitHeader {
14 pub nal_unit_type: NALUnitType,
16 pub nuh_layer_id: u8,
21 pub nuh_temporal_id_plus1: NonZero<u8>,
25}
26
27impl NALUnitHeader {
28 pub fn parse(reader: impl io::Read) -> io::Result<Self> {
29 let mut bit_reader = BitReader::new(reader);
31
32 let forbidden_zero_bit = bit_reader.read_bit()?;
33 if forbidden_zero_bit {
34 return Err(io::Error::new(io::ErrorKind::InvalidData, "forbidden_zero_bit is not zero"));
35 }
36
37 let nal_unit_type = NALUnitType::from(bit_reader.read_bits(6)? as u8);
38 let nuh_layer_id = bit_reader.read_bits(6)? as u8;
39 range_check!(nuh_layer_id, 0, 63)?;
40
41 if nal_unit_type == NALUnitType::EobNut && nuh_layer_id != 0 {
42 return Err(io::Error::new(
43 io::ErrorKind::InvalidData,
44 "nuh_layer_id must be 0 when nal_unit_type is EOB_NUT",
45 ));
46 }
47
48 let nuh_temporal_id_plus1 = bit_reader.read_bits(3)? as u8;
49 let nuh_temporal_id_plus1 = NonZero::new(nuh_temporal_id_plus1)
50 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nuh_temporal_id_plus1 cannot be 0"))?;
51
52 if ((NALUnitType::BlaWLp..=NALUnitType::RsvIrapVcl23).contains(&nal_unit_type)
53 || nal_unit_type == NALUnitType::VpsNut
54 || nal_unit_type == NALUnitType::SpsNut
55 || nal_unit_type == NALUnitType::EosNut
56 || nal_unit_type == NALUnitType::EobNut)
57 && nuh_temporal_id_plus1.get() != 1
58 {
59 return Err(io::Error::new(
60 io::ErrorKind::InvalidData,
61 format!("nuh_temporal_id_plus1 must be 1 (TemporalId = 0) for nal_unit_type {nal_unit_type:?}"),
62 ));
63 }
64
65 if (nal_unit_type == NALUnitType::TsaR || nal_unit_type == NALUnitType::TsaN) && nuh_temporal_id_plus1.get() == 1 {
66 return Err(io::Error::new(
67 io::ErrorKind::InvalidData,
68 format!("nuh_temporal_id_plus1 must not be 1 (TemporalId != 0) for nal_unit_type {nal_unit_type:?}"),
69 ));
70 }
71
72 if nuh_layer_id == 0
73 && (nal_unit_type == NALUnitType::StsaR || nal_unit_type == NALUnitType::StsaN)
74 && nuh_temporal_id_plus1.get() == 1
75 {
76 return Err(io::Error::new(
77 io::ErrorKind::InvalidData,
78 format!(
79 "nuh_temporal_id_plus1 must not be 1 (TemporalId != 0) for nuh_layer_id 0 and nal_unit_type {nal_unit_type:?}"
80 ),
81 ));
82 }
83
84 Ok(Self {
85 nal_unit_type,
86 nuh_layer_id,
87 nuh_temporal_id_plus1,
88 })
89 }
90
91 pub fn temporal_id(&self) -> u8 {
95 self.nuh_temporal_id_plus1.get() - 1
96 }
97}