#![forbid(unsafe_code)]
use bytes::Bytes;
use memchr::memmem;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AvccOut {
pub payload: Bytes,
pub avcc: Option<Bytes>,
}
#[must_use]
pub fn to_avcc(data: &[u8]) -> AvccOut {
if !is_annex_b(data) {
return AvccOut {
payload: Bytes::copy_from_slice(data),
avcc: None,
};
}
let mut out = Vec::with_capacity(data.len());
let mut sps: Option<&[u8]> = None;
let mut pps: Option<&[u8]> = None;
for nal in NalIter::new(data) {
if nal.is_empty() {
continue;
}
match nal[0] & 0x1f {
7 => sps = Some(nal),
8 => 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 avcc = match (sps, pps) {
(Some(s), Some(p)) => Some(build_avcc(s, p)),
_ => None,
};
AvccOut {
payload: Bytes::from(out),
avcc,
}
}
fn is_annex_b(data: &[u8]) -> bool {
matches!(find_start_code(data), Some((0, _)))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AvcDecoderConfig {
pub nal_length_size: u8,
pub sps: Vec<Bytes>,
pub pps: Vec<Bytes>,
}
#[must_use]
pub fn parse_avc_decoder_config(record: &[u8]) -> Option<AvcDecoderConfig> {
if record.len() < 7 || record[0] != 1 {
return None;
}
let nal_length_size = (record[4] & 0x03) + 1;
let num_sps = record[5] & 0x1f;
let mut pos = 6usize;
let mut sps = Vec::new();
for _ in 0..num_sps {
let (nal, next) = read_length_prefixed(record, pos)?;
sps.push(nal);
pos = next;
}
let pps_count = *record.get(pos)?;
pos += 1;
let mut pps = Vec::new();
for _ in 0..pps_count {
let (nal, next) = read_length_prefixed(record, pos)?;
pps.push(nal);
pos = next;
}
Some(AvcDecoderConfig {
nal_length_size,
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: &AvcDecoderConfig) -> Bytes {
let mut out = Vec::new();
for nal in 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 avcc_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_avcc(sps: &[u8], pps: &[u8]) -> Bytes {
let mut v = Vec::with_capacity(11 + sps.len() + pps.len());
v.push(1);
if sps.len() >= 4 {
v.extend_from_slice(&sps[1..4]);
} else {
v.extend_from_slice(&[0x42, 0x00, 0x1e]);
}
v.push(0xff);
v.push(0xe1);
v.extend_from_slice(&(u16::try_from(sps.len()).unwrap_or(u16::MAX)).to_be_bytes());
v.extend_from_slice(sps);
v.push(1);
v.extend_from_slice(&(u16::try_from(pps.len()).unwrap_or(u16::MAX)).to_be_bytes());
v.extend_from_slice(pps);
Bytes::from(v)
}
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 = "avc_tests.rs"]
mod tests;