scuffle_h265/
nal_unit_header.rs

1use std::io;
2use std::num::NonZero;
3
4use scuffle_bytes_util::{BitReader, range_check};
5
6use crate::NALUnitType;
7
8/// NAL unit header.
9///
10/// - ISO/IEC 23008-2 - 7.3.1.2
11/// - ISO/IEC 23008-2 - 7.4.2.2
12#[derive(Debug, Clone, PartialEq)]
13pub struct NALUnitHeader {
14    /// Specifies the type of RBSP data structure contained in the NAL unit as specified in ISO/IEC 23008-2 Table 7-1.
15    pub nal_unit_type: NALUnitType,
16    /// Specifies the identifier of the layer to which a VCL NAL unit belongs or the identifier of a
17    /// layer to which a non-VCL NAL unit applies.
18    ///
19    /// This value is in range \[0, 63\], with 63 being reserved for future use.
20    pub nuh_layer_id: u8,
21    /// This value minus 1 specifies a temporal identifier for the NAL unit.
22    ///
23    /// This value is in range from \[1, 7\].
24    pub nuh_temporal_id_plus1: NonZero<u8>,
25}
26
27impl NALUnitHeader {
28    pub fn parse(reader: impl io::Read) -> io::Result<Self> {
29        // The header is exactly 2 bytes
30        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    /// Returns the temporal id of the NAL unit.
92    ///
93    /// Defined as `TemporalId` (7-1) by ISO/IEC 23008-2 - 7.4.2.2.
94    pub fn temporal_id(&self) -> u8 {
95        self.nuh_temporal_id_plus1.get() - 1
96    }
97}