#![forbid(unsafe_code)]
use crate::DecodeError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum HevcNalUnitType {
Trail,
Idr,
Cra,
Vps,
Sps,
Pps,
Other(u8),
}
impl HevcNalUnitType {
#[must_use]
const fn from_u8(value: u8) -> Self {
match value {
0..=9 => Self::Trail,
19 | 20 => Self::Idr,
21 => Self::Cra,
32 => Self::Vps,
33 => Self::Sps,
34 => Self::Pps,
other => Self::Other(other),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct HevcNalUnit {
pub(super) unit_type: HevcNalUnitType,
pub(super) is_reference: bool,
pub(super) rbsp: Vec<u8>,
}
impl HevcNalUnit {
pub(super) fn parse(data: &[u8]) -> Result<Self, DecodeError> {
let first = *data.first().ok_or(DecodeError::InvalidInput)?;
let second = *data.get(1).ok_or(DecodeError::InvalidInput)?;
let nal_unit_type = (first >> 1) & 0x3F;
let nuh_layer_id = ((first & 0x1) << 5) | (second >> 3);
if nuh_layer_id != 0 {
return Err(DecodeError::Unsupported);
}
let rbsp = remove_emulation_prevention(data.get(2..).ok_or(DecodeError::InvalidInput)?);
Ok(Self {
unit_type: HevcNalUnitType::from_u8(nal_unit_type),
is_reference: is_reference_nal_unit_type(nal_unit_type),
rbsp,
})
}
}
const fn is_reference_nal_unit_type(nal_unit_type: u8) -> bool {
match nal_unit_type {
0..=9 => nal_unit_type % 2 == 1,
16..=21 => true,
_ => false,
}
}
fn remove_emulation_prevention(data: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(data.len());
let mut zero_run = 0u32;
for &byte in data {
if zero_run >= 2 && byte == 0x03 {
zero_run = 0;
continue;
}
out.push(byte);
zero_run = if byte == 0 { zero_run + 1 } else { 0 };
}
out
}
#[cfg(test)]
#[path = "hevc_nal_tests.rs"]
mod tests;