#![forbid(unsafe_code)]
use super::error::H264Error;
use mediaway_common::Bytes;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum NalUnitType {
NonIdrSlice,
SliceDataPartition,
IdrSlice,
Sei,
Sps,
Pps,
AccessUnitDelimiter,
EndOfSequence,
EndOfStream,
FillerData,
Other(u8),
}
impl NalUnitType {
#[must_use]
pub const fn from_u8(value: u8) -> Self {
match value {
1 => Self::NonIdrSlice,
2..=4 => Self::SliceDataPartition,
5 => Self::IdrSlice,
6 => Self::Sei,
7 => Self::Sps,
8 => Self::Pps,
9 => Self::AccessUnitDelimiter,
10 => Self::EndOfSequence,
11 => Self::EndOfStream,
12 => Self::FillerData,
other => Self::Other(other),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NalUnit {
pub ref_idc: u8,
pub unit_type: NalUnitType,
pub rbsp: Bytes,
}
impl NalUnit {
pub fn parse(data: &[u8]) -> Result<Self, H264Error> {
let header = *data.first().ok_or(H264Error::UnexpectedEof)?;
let ref_idc = (header >> 5) & 0b11;
let unit_type = NalUnitType::from_u8(header & 0b1_1111);
let rbsp = remove_emulation_prevention(&data[1..]);
Ok(Self {
ref_idc,
unit_type,
rbsp: Bytes::from(rbsp),
})
}
}
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
}
fn find_start_codes(data: &[u8]) -> Vec<usize> {
let mut positions = Vec::new();
let mut index = 0usize;
while index + 3 <= data.len() {
if data[index] == 0 && data[index + 1] == 0 && data[index + 2] == 1 {
positions.push(index);
index += 3;
} else {
index += 1;
}
}
positions
}
fn trim_trailing_zeros(data: &[u8]) -> &[u8] {
let mut end = data.len();
while end > 0 && data[end - 1] == 0 {
end -= 1;
}
&data[..end]
}
pub fn split_annex_b(data: &[u8]) -> Result<Vec<&[u8]>, H264Error> {
let marks = find_start_codes(data);
if marks.is_empty() {
return Err(H264Error::NoStartCode);
}
let mut units = Vec::with_capacity(marks.len());
for pair in marks.windows(2) {
let content_begin = pair[0] + 3;
let content_end = pair[1];
units.push(trim_trailing_zeros(&data[content_begin..content_end]));
}
if let Some(&last_mark) = marks.last() {
units.push(trim_trailing_zeros(&data[last_mark + 3..]));
}
Ok(units)
}
pub fn split_avcc(data: &[u8], length_size: u8) -> Result<Vec<&[u8]>, H264Error> {
if !(1..=4).contains(&length_size) {
return Err(H264Error::InvalidLengthSize);
}
let length_size = usize::from(length_size);
let mut units = Vec::new();
let mut pos = 0usize;
while pos < data.len() {
let prefix = data
.get(pos..pos + length_size)
.ok_or(H264Error::InvalidNalLength)?;
let len = prefix
.iter()
.fold(0usize, |acc, &byte| (acc << 8) | usize::from(byte));
pos += length_size;
let unit = data
.get(pos..pos + len)
.ok_or(H264Error::InvalidNalLength)?;
units.push(unit);
pos += len;
}
Ok(units)
}
#[cfg(test)]
#[path = "nal_tests.rs"]
mod tests;