use std::sync::LazyLock;
use bytes::{BufMut, BytesMut};
use memchr::memmem::Finder;
#[derive(Debug, Default)]
pub(crate) struct NALUSplitter {
buffer: BytesMut,
pts: Option<u64>,
previous_search_end: usize,
}
fn find_start_of_next_nalu(buf: &[u8]) -> Option<usize> {
static FINDER: LazyLock<Finder> = LazyLock::new(|| Finder::new(&[0, 0, 1]));
if buf.len() < 4 {
return None;
};
if buf[0] != 0 && buf[1..4] == [0, 0, 1] {
return Some(5);
}
FINDER.find(&buf[2..]).map(|i| i + 5)
}
impl NALUSplitter {
pub(crate) fn push(
&mut self,
bytestream: &[u8],
pts: Option<u64>,
) -> Vec<(Vec<u8>, Option<u64>)> {
let mut output_pts = if self.buffer.is_empty() {
pts
} else {
self.pts
};
self.buffer.put(bytestream);
let mut result = Vec::new();
while let Some(i) = find_start_of_next_nalu(&self.buffer[self.previous_search_end..]) {
let nalu = self.buffer.split_to(self.previous_search_end + i);
self.previous_search_end = 0;
result.push((nalu.to_vec(), output_pts));
output_pts = pts;
}
self.previous_search_end = self.buffer.len().saturating_sub(3);
self.pts = pts;
result
}
pub(crate) fn flush(&mut self) -> Vec<(Vec<u8>, Option<u64>)> {
if self.buffer.is_empty() {
return vec![];
}
let mut result = Vec::new();
while let Some(i) = find_start_of_next_nalu(&self.buffer[self.previous_search_end..]) {
let nalu = self.buffer.split_to(self.previous_search_end + i);
self.previous_search_end = 0;
result.push((nalu.to_vec(), self.pts));
}
result.push((
[self.buffer.to_vec(), [0, 0, 1].to_vec()].concat(),
self.pts,
));
self.buffer = BytesMut::new();
self.previous_search_end = 0;
result
}
}