use alloc::vec::Vec;
use crate::error::{Error, Result};
pub const NAL_LENGTH_SIZE: usize = 4;
pub fn iter_annexb_nals(annexb: &[u8]) -> AnnexBNalIter<'_> {
AnnexBNalIter {
data: annexb,
code_positions: start_code_positions(annexb),
idx: 0,
}
}
fn start_code_positions(data: &[u8]) -> Vec<usize> {
let mut positions = Vec::new();
let n = data.len();
let mut p = 0usize;
while p + 3 <= n {
if data[p] == 0 && data[p + 1] == 0 && data[p + 2] == 1 {
positions.push(p);
p += 3;
} else {
p += 1;
}
}
positions
}
pub struct AnnexBNalIter<'a> {
data: &'a [u8],
code_positions: Vec<usize>,
idx: usize,
}
impl<'a> Iterator for AnnexBNalIter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<&'a [u8]> {
if self.idx >= self.code_positions.len() {
return None;
}
let start = self.code_positions[self.idx] + 3;
let end = self
.code_positions
.get(self.idx + 1)
.copied()
.unwrap_or(self.data.len());
self.idx += 1;
let mut slice = &self.data[start..end];
while let Some((&0, rest)) = slice.split_last() {
slice = rest;
}
if slice.is_empty() {
return self.next();
}
Some(slice)
}
}
pub fn annexb_to_length_prefixed(annexb: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(annexb.len());
for nal in iter_annexb_nals(annexb) {
out.extend_from_slice(&(nal.len() as u32).to_be_bytes());
out.extend_from_slice(nal);
}
out
}
pub fn iter_length_prefixed_nals(lp: &[u8]) -> Result<Vec<&[u8]>> {
let mut nals = Vec::new();
let mut off = 0usize;
while off < lp.len() {
if off + NAL_LENGTH_SIZE > lp.len() {
return Err(Error::BufferTooShort {
need: off + NAL_LENGTH_SIZE,
have: lp.len(),
what: "NAL length prefix",
});
}
let len = u32::from_be_bytes([lp[off], lp[off + 1], lp[off + 2], lp[off + 3]]) as usize;
let start = off + NAL_LENGTH_SIZE;
let end = start + len;
if end > lp.len() {
return Err(Error::BufferTooShort {
need: end,
have: lp.len(),
what: "NAL payload",
});
}
nals.push(&lp[start..end]);
off = end;
}
Ok(nals)
}
pub fn length_prefixed_to_annexb(lp: &[u8]) -> Result<Vec<u8>> {
let nals = iter_length_prefixed_nals(lp)?;
let mut out = Vec::with_capacity(lp.len());
for nal in nals {
out.extend_from_slice(&[0, 0, 0, 1]);
out.extend_from_slice(nal);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn splits_mixed_start_codes_and_strips_trailing_zeros() {
let annexb = [
0x00, 0x00, 0x00, 0x01, 0x67, 0x42,
0x00, 0x00, 0x00, 0x01, 0x65, 0x00, 0x00, 0x03, 0x88, 0x00,
0x00, ];
let nals: Vec<&[u8]> = iter_annexb_nals(&annexb).collect();
assert_eq!(nals.len(), 2);
assert_eq!(nals[0], &[0x67, 0x42]);
assert_eq!(nals[1], &[0x65, 0x00, 0x00, 0x03, 0x88]);
}
#[test]
fn annexb_to_length_prefixed_bijection() {
let annexb = [
0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x00, 0x00, 0x01, 0x65, 0x88, 0x99,
];
let lp = annexb_to_length_prefixed(&annexb);
assert_eq!(&lp[0..4], &2u32.to_be_bytes());
assert_eq!(&lp[4..6], &[0x67, 0x42]);
assert_eq!(&lp[6..10], &3u32.to_be_bytes());
assert_eq!(&lp[10..13], &[0x65, 0x88, 0x99]);
let back = length_prefixed_to_annexb(&lp).unwrap();
let lp2 = annexb_to_length_prefixed(&back);
assert_eq!(lp, lp2, "length↔annexb round-trip must be canonical");
}
#[test]
fn length_prefixed_rejects_overrun() {
let lp = [0x00, 0x00, 0x00, 0x63, 0xAA, 0xBB];
assert!(iter_length_prefixed_nals(&lp).is_err());
}
}