#![forbid(unsafe_code)]
use bytes::Bytes;
use memchr::memmem;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HvccOut {
pub payload: Bytes,
pub hvcc: Option<Bytes>,
}
#[must_use]
pub fn to_hvcc(data: &[u8]) -> HvccOut {
if !is_annex_b(data) {
return HvccOut {
payload: Bytes::copy_from_slice(data),
hvcc: None,
};
}
let mut out = Vec::with_capacity(data.len());
let mut vps: Option<&[u8]> = None;
let mut sps: Option<&[u8]> = None;
let mut pps: Option<&[u8]> = None;
for nal in NalIter::new(data) {
if nal.len() < 2 {
continue;
}
match nal_unit_type(nal) {
VPS_NUT => vps = Some(nal),
SPS_NUT => sps = Some(nal),
PPS_NUT => pps = Some(nal),
_ => {}
}
let len = u32::try_from(nal.len()).unwrap_or(u32::MAX);
out.extend_from_slice(&len.to_be_bytes());
out.extend_from_slice(nal);
}
let hvcc = match (vps, sps, pps) {
(Some(v), Some(s), Some(p)) => Some(build_hvcc(v, s, p)),
_ => None,
};
HvccOut {
payload: Bytes::from(out),
hvcc,
}
}
const VPS_NUT: u8 = 32;
const SPS_NUT: u8 = 33;
const PPS_NUT: u8 = 34;
const fn nal_unit_type(nal: &[u8]) -> u8 {
(nal[0] >> 1) & 0x3f
}
#[must_use]
pub fn is_annex_b(data: &[u8]) -> bool {
matches!(find_start_code(data), Some((0, _)))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HevcDecoderConfig {
pub nal_length_size: u8,
pub vps: Vec<Bytes>,
pub sps: Vec<Bytes>,
pub pps: Vec<Bytes>,
}
#[must_use]
pub fn parse_hevc_decoder_config(record: &[u8]) -> Option<HevcDecoderConfig> {
if record.len() < 23 || record[0] != 1 {
return None;
}
let nal_length_size = (record[21] & 0x03) + 1;
let num_arrays = record[22];
let mut pos = 23usize;
let mut vps = Vec::new();
let mut sps = Vec::new();
let mut pps = Vec::new();
for _ in 0..num_arrays {
let array_header = *record.get(pos)?;
let array_nal_type = array_header & 0x3f;
pos += 1;
let num_nalus_bytes = record.get(pos..pos + 2)?;
let num_nalus = u16::from_be_bytes([num_nalus_bytes[0], num_nalus_bytes[1]]);
pos += 2;
for _ in 0..num_nalus {
let (nal, next) = read_length_prefixed(record, pos)?;
pos = next;
match array_nal_type {
VPS_NUT => vps.push(nal),
SPS_NUT => sps.push(nal),
PPS_NUT => pps.push(nal),
_ => {}
}
}
}
Some(HevcDecoderConfig {
nal_length_size,
vps,
sps,
pps,
})
}
fn read_length_prefixed(data: &[u8], pos: usize) -> Option<(Bytes, usize)> {
let len_bytes = data.get(pos..pos + 2)?;
let len = usize::from(u16::from_be_bytes([len_bytes[0], len_bytes[1]]));
let start = pos + 2;
let nal = data.get(start..start + len)?;
Some((Bytes::copy_from_slice(nal), start + len))
}
#[must_use]
pub fn annex_b_sequence_header(config: &HevcDecoderConfig) -> Bytes {
let mut out = Vec::new();
for nal in config
.vps
.iter()
.chain(config.sps.iter())
.chain(config.pps.iter())
{
out.extend_from_slice(&[0, 0, 0, 1]);
out.extend_from_slice(nal);
}
Bytes::from(out)
}
#[must_use]
pub fn hvcc_payload_to_annex_b(data: &[u8], nal_length_size: u8) -> Bytes {
let nls = usize::from(nal_length_size).clamp(1, 4);
let mut out = Vec::with_capacity(data.len() + 16);
let mut pos = 0usize;
while pos + nls <= data.len() {
let Some(len) = read_nal_length(&data[pos..pos + nls]) else {
break;
};
pos += nls;
let Some(nal) = data.get(pos..pos + len) else {
break;
};
out.extend_from_slice(&[0, 0, 0, 1]);
out.extend_from_slice(nal);
pos += len;
}
Bytes::from(out)
}
fn read_nal_length(len_bytes: &[u8]) -> Option<usize> {
match len_bytes.len() {
1 => Some(usize::from(len_bytes[0])),
2 => Some(usize::from(u16::from_be_bytes([
len_bytes[0],
len_bytes[1],
]))),
4 => Some(
usize::try_from(u32::from_be_bytes([
len_bytes[0],
len_bytes[1],
len_bytes[2],
len_bytes[3],
]))
.unwrap_or(usize::MAX),
),
_ => None,
}
}
fn build_hvcc(vps: &[u8], sps: &[u8], pps: &[u8]) -> Bytes {
let mut v = Vec::with_capacity(23 + 3 * 5 + vps.len() + sps.len() + pps.len());
v.push(1);
let rbsp = unescape_rbsp(sps);
if rbsp.len() >= 15 {
v.push(rbsp[3]); v.extend_from_slice(&rbsp[4..8]); v.extend_from_slice(&rbsp[8..14]); v.push(rbsp[14]); } else {
v.extend_from_slice(&[0u8; 12]);
}
v.extend_from_slice(&[0xf0, 0x00]); v.push(0xfc); v.push(0xfd); v.push(0xf8); v.push(0xf8); v.extend_from_slice(&[0, 0]); v.push(temporal_layer_byte(&rbsp));
v.push(3);
for (nal_type, nal) in [(VPS_NUT, vps), (SPS_NUT, sps), (PPS_NUT, pps)] {
v.push(0x80 | nal_type); v.extend_from_slice(&1u16.to_be_bytes()); v.extend_from_slice(&(u16::try_from(nal.len()).unwrap_or(u16::MAX)).to_be_bytes());
v.extend_from_slice(nal);
}
Bytes::from(v)
}
fn temporal_layer_byte(rbsp: &[u8]) -> u8 {
let (num_temporal_layers, temporal_id_nested) = rbsp
.get(2)
.map_or((1, 0), |byte| (((byte >> 1) & 0x07) + 1, byte & 0x01));
((num_temporal_layers & 0x07) << 3) | ((temporal_id_nested & 0x01) << 2) | 0x03
}
fn unescape_rbsp(ebsp: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(ebsp.len());
let mut zeros = 0u8;
for &byte in ebsp {
if zeros >= 2 && byte == 0x03 {
zeros = 0;
continue;
}
zeros = if byte == 0 {
zeros.saturating_add(1)
} else {
0
};
out.push(byte);
}
out
}
fn find_start_code(hay: &[u8]) -> Option<(usize, usize)> {
let i4 = memmem::find(hay, &[0, 0, 0, 1]);
let i3 = memmem::find(hay, &[0, 0, 1]);
match (i4, i3) {
(Some(a), Some(b)) if a <= b => Some((a, 4)),
(Some(a), None) => Some((a, 4)),
(_, Some(b)) => Some((b, 3)),
(None, None) => None,
}
}
struct NalIter<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> NalIter<'a> {
const fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
}
impl<'a> Iterator for NalIter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
let data = self.data;
loop {
if self.pos >= data.len() {
return None;
}
let (sc_at, sc_len) = find_start_code(&data[self.pos..])?;
let start = self.pos + sc_at + sc_len;
let end = match find_start_code(&data[start..]) {
Some((rel, _)) => start + rel,
None => data.len(),
};
self.pos = end;
if start < end {
return Some(&data[start..end]);
}
}
}
}
#[cfg(test)]
#[path = "hevc_tests.rs"]
mod tests;