use std::borrow::Cow;
use std::fmt::Debug;
use std::io::Cursor;
use std::io::Seek;
use std::io::SeekFrom;
#[allow(clippy::len_without_is_empty)]
pub trait Header: Sized {
fn parse<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<Self, String>;
fn is_end(&self) -> bool;
fn len(&self) -> usize;
}
#[derive(Debug)]
pub struct Nalu<'a, U> {
pub header: U,
pub data: Cow<'a, [u8]>,
pub size: usize,
pub offset: usize,
}
impl<'a, U> Nalu<'a, U>
where
U: Debug + Header,
{
pub fn next(cursor: &mut Cursor<&'a [u8]>) -> Result<Nalu<'a, U>, String> {
let bitstream = cursor.clone().into_inner();
let pos = usize::try_from(cursor.position()).map_err(|err| err.to_string())?;
let current_nalu_offset = match Nalu::<'a, U>::find_start_code(cursor, pos) {
Some(offset) => offset,
None => return Err("No NAL found".into()),
};
let mut start_code_offset = pos + current_nalu_offset;
if start_code_offset > 0 && cursor.get_ref()[start_code_offset - 1] == 00 {
start_code_offset -= 1;
}
let nalu_offset = pos + current_nalu_offset + 3;
cursor.set_position(u64::try_from(nalu_offset).map_err(|err| err.to_string())?);
let hdr = U::parse(cursor)?;
let mut next_nalu_offset = match Nalu::<'a, U>::find_start_code(cursor, nalu_offset) {
Some(offset) => offset,
None => {
let cur_pos = cursor.position();
let end_pos = cursor.seek(SeekFrom::End(0)).map_err(|err| err.to_string())?;
let _ = cursor.seek(SeekFrom::Start(cur_pos)).map_err(|err| err.to_string())?;
(end_pos - cur_pos) as usize
} };
while next_nalu_offset > 0 && cursor.get_ref()[nalu_offset + next_nalu_offset - 1] == 00 {
next_nalu_offset -= 1;
}
let nal_size = if hdr.is_end() {
hdr.len()
} else {
next_nalu_offset
};
Ok(Nalu {
header: hdr,
data: Cow::from(&bitstream[start_code_offset..nalu_offset + nal_size]),
size: nal_size,
offset: nalu_offset - start_code_offset,
})
}
}
impl<'a, U> Nalu<'a, U>
where
U: Debug,
{
fn find_start_code(data: &mut Cursor<&'a [u8]>, offset: usize) -> Option<usize> {
data.get_ref()[offset..].windows(3).position(|window| window == [0x00, 0x00, 0x01])
}
pub fn into_owned(self) -> Nalu<'static, U> {
Nalu {
header: self.header,
size: self.size,
offset: self.offset,
data: Cow::Owned(self.data.into_owned()),
}
}
}
impl<'a, U> AsRef<[u8]> for Nalu<'a, U> {
fn as_ref(&self) -> &[u8] {
&self.data[self.offset..self.offset + self.size]
}
}