use bytes::{Bytes, BytesMut};
use super::Error;
use crate::Result;
use crate::codec::annexb::{NalIterator, START_CODE};
pub struct Split {
tail: BytesMut,
current: Avc3Frame,
sps: Vec<Bytes>,
pps: Vec<Bytes>,
zero: Option<tokio::time::Instant>,
pending: Vec<crate::container::Frame>,
}
#[derive(Default)]
struct Avc3Frame {
chunks: BytesMut,
contains_idr: bool,
contains_slice: bool,
sps_seen: Vec<Bytes>,
pps_seen: Vec<Bytes>,
}
impl Default for Split {
fn default() -> Self {
Self::new()
}
}
impl Split {
pub fn new() -> Self {
Self {
tail: BytesMut::new(),
current: Avc3Frame::default(),
sps: Vec::new(),
pps: Vec::new(),
zero: None,
pending: Vec::new(),
}
}
pub fn decode(
&mut self,
data: &[u8],
pts: impl Into<Option<crate::container::Timestamp>>,
) -> Result<Vec<crate::container::Frame>> {
let pts = self.pts(pts.into())?;
self.tail.extend_from_slice(data);
let nals = NalIterator::new(&mut self.tail);
let mut parsed = Vec::new();
for nal in nals {
parsed.push(nal?);
}
for nal in parsed {
self.decode_nal(nal, pts)?;
}
Ok(std::mem::take(&mut self.pending))
}
pub fn flush(
&mut self,
pts: impl Into<Option<crate::container::Timestamp>>,
) -> Result<Vec<crate::container::Frame>> {
let pts = self.pts(pts.into())?;
if let Some(nal) = NalIterator::new(&mut self.tail).flush()? {
self.decode_nal(nal, pts)?;
}
self.tail.clear();
self.maybe_start_frame(pts)?;
Ok(std::mem::take(&mut self.pending))
}
fn decode_nal(&mut self, nal: Bytes, pts: crate::container::Timestamp) -> Result<()> {
let header = nal.first().ok_or(Error::NalTooShort)?;
let forbidden_zero_bit = (header >> 7) & 1;
if forbidden_zero_bit != 0 {
return Err(Error::ForbiddenZeroBit.into());
}
let nal_unit_type = header & 0b11111;
let nal_type = Avc3NalType::try_from(nal_unit_type).ok();
match nal_type {
Some(Avc3NalType::Sps) => {
self.maybe_start_frame(pts)?;
crate::codec::annexb::push_distinct(&mut self.current.sps_seen, &nal);
}
Some(Avc3NalType::Pps) => {
self.maybe_start_frame(pts)?;
crate::codec::annexb::push_distinct(&mut self.current.pps_seen, &nal);
}
Some(Avc3NalType::Aud) | Some(Avc3NalType::Sei) => {
self.maybe_start_frame(pts)?;
}
Some(Avc3NalType::IdrSlice) => {
if nal.get(1).ok_or(Error::NalTooShort)? & 0x80 != 0 {
self.maybe_start_frame(pts)?;
}
crate::codec::annexb::reconcile_keyframe_params(
&mut self.current.chunks,
&mut self.sps,
&mut self.current.sps_seen,
);
crate::codec::annexb::reconcile_keyframe_params(
&mut self.current.chunks,
&mut self.pps,
&mut self.current.pps_seen,
);
self.current.contains_idr = true;
self.current.contains_slice = true;
}
Some(Avc3NalType::NonIdrSlice)
| Some(Avc3NalType::DataPartitionA)
| Some(Avc3NalType::DataPartitionB)
| Some(Avc3NalType::DataPartitionC) => {
if nal.get(1).ok_or(Error::NalTooShort)? & 0x80 != 0 {
self.maybe_start_frame(pts)?;
}
self.current.contains_slice = true;
}
_ => {}
}
tracing::trace!(kind = ?nal_type, "parsed NAL");
self.current.chunks.extend_from_slice(&START_CODE);
self.current.chunks.extend_from_slice(&nal);
Ok(())
}
fn maybe_start_frame(&mut self, pts: crate::container::Timestamp) -> Result<()> {
if !self.current.contains_slice {
return Ok(());
}
let payload = std::mem::take(&mut self.current.chunks).freeze();
let keyframe = self.current.contains_idr;
self.current.contains_idr = false;
self.current.contains_slice = false;
self.current.sps_seen.clear();
self.current.pps_seen.clear();
self.pending.push(crate::container::Frame {
timestamp: pts,
payload,
keyframe,
duration: None,
});
Ok(())
}
pub fn reset(&mut self) {
self.current = Avc3Frame::default();
self.tail.clear();
}
fn pts(&mut self, hint: Option<crate::container::Timestamp>) -> Result<crate::container::Timestamp> {
if let Some(pts) = hint {
return Ok(pts);
}
let zero = self.zero.get_or_insert_with(tokio::time::Instant::now);
Ok(crate::container::Timestamp::from_micros(
zero.elapsed().as_micros() as u64
)?)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::TryFromPrimitive)]
#[repr(u8)]
enum Avc3NalType {
Unspecified = 0,
NonIdrSlice = 1,
DataPartitionA = 2,
DataPartitionB = 3,
DataPartitionC = 4,
IdrSlice = 5,
Sei = 6,
Sps = 7,
Pps = 8,
Aud = 9,
EndOfSeq = 10,
EndOfStream = 11,
Filler = 12,
SpsExt = 13,
Prefix = 14,
SubsetSps = 15,
DepthParameterSet = 16,
}
#[cfg(test)]
mod tests {
use super::*;
const SC4: &[u8] = &[0, 0, 0, 1];
fn annexb(nals: &[&[u8]]) -> BytesMut {
let mut buf = BytesMut::new();
for nal in nals {
buf.extend_from_slice(SC4);
buf.extend_from_slice(nal);
}
buf
}
fn ts() -> crate::container::Timestamp {
crate::container::Timestamp::from_micros(0).unwrap()
}
fn decode_one(
split: &mut Split,
buf: &mut BytesMut,
pts: crate::container::Timestamp,
) -> Vec<crate::container::Frame> {
let mut frames = split.decode(buf, pts).unwrap();
frames.extend(split.flush(pts).unwrap());
frames
}
#[tokio::test(start_paused = true)]
async fn decode_packages_keyframe() {
let sps: &[u8] = &[0x67, 0x42, 0xc0, 0x1f];
let pps: &[u8] = &[0x68, 0xce, 0x3c, 0x80];
let idr: &[u8] = &[0x65, 0x88, 0x84, 0x21];
let mut split = Split::new();
let frames = decode_one(&mut split, &mut annexb(&[sps, pps, idr]), ts());
assert_eq!(frames.len(), 1);
assert!(frames[0].keyframe);
assert_eq!(&frames[0].payload[..SC4.len()], SC4);
assert!(frames[0].payload.windows(sps.len()).any(|w| w == sps));
assert!(frames[0].payload.windows(idr.len()).any(|w| w == idr));
}
#[tokio::test(start_paused = true)]
async fn params_then_bare_keyframe_self_contained() {
let sps: &[u8] = &[0x67, 0x42, 0xc0, 0x1f];
let pps: &[u8] = &[0x68, 0xce, 0x3c, 0x80];
let idr: &[u8] = &[0x65, 0x88, 0x84, 0x21];
let mut split = Split::new();
assert!(split.decode(&annexb(&[sps, pps]), ts()).unwrap().is_empty());
let frames = decode_one(&mut split, &mut annexb(&[idr]), ts());
assert_eq!(frames.len(), 1);
assert!(frames[0].keyframe);
assert!(frames[0].payload.windows(sps.len()).any(|w| w == sps));
assert!(frames[0].payload.windows(pps.len()).any(|w| w == pps));
}
#[tokio::test(start_paused = true)]
async fn decode_emits_on_next_boundary() {
let sps: &[u8] = &[0x67, 0x42, 0xc0, 0x1f];
let pps: &[u8] = &[0x68, 0xce, 0x3c, 0x80];
let idr: &[u8] = &[0x65, 0x88, 0x84, 0x21];
let pslice: &[u8] = &[0x61, 0xe0, 0x12, 0x34];
let aud: &[u8] = &[0x09, 0x10];
let mut split = Split::new();
let frames = split.decode(&annexb(&[sps, pps, idr, pslice, aud]), ts()).unwrap();
assert_eq!(frames.len(), 1);
assert!(frames[0].keyframe);
let tail = split.flush(ts()).unwrap();
assert_eq!(tail.len(), 1);
assert!(!tail[0].keyframe);
}
#[tokio::test(start_paused = true)]
async fn reinjects_all_cached_pps_on_keyframe() {
let sps: &[u8] = &[0x67, 0x42, 0xc0, 0x1f];
let pps0: &[u8] = &[0x68, 0xce, 0x3c, 0x80];
let pps1: &[u8] = &[0x68, 0xce, 0x3c, 0x81];
let idr: &[u8] = &[0x65, 0x88, 0x84, 0x21];
let mut split = Split::new();
let first = decode_one(&mut split, &mut annexb(&[sps, pps0, pps1, idr]), ts());
assert_eq!(first.len(), 1);
assert!(first[0].keyframe);
let second = decode_one(&mut split, &mut annexb(&[idr]), ts());
assert_eq!(second.len(), 1);
assert!(second[0].keyframe);
assert_eq!(
second[0].payload.as_ref(),
annexb(&[sps, pps0, pps1, idr]).freeze().as_ref()
);
}
#[tokio::test(start_paused = true)]
async fn bare_idr_after_delta_splits() {
let sps: &[u8] = &[0x67, 0x42, 0xc0, 0x1f];
let pps: &[u8] = &[0x68, 0xce, 0x3c, 0x80];
let idr: &[u8] = &[0x65, 0x88, 0x84, 0x21];
let pslice: &[u8] = &[0x61, 0xe0, 0x12, 0x34];
let aud: &[u8] = &[0x09, 0x10];
let mut split = Split::new();
let frames = split.decode(&annexb(&[sps, pps, idr, pslice, idr, aud]), ts()).unwrap();
assert_eq!(frames.len(), 2);
assert!(frames[0].keyframe, "first AU is the keyframe");
assert!(!frames[1].keyframe, "the delta picture must not be flagged a keyframe");
assert_eq!(frames[1].payload.as_ref(), annexb(&[pslice]).freeze().as_ref());
let tail = split.flush(ts()).unwrap();
assert_eq!(tail.len(), 1);
assert!(tail[0].keyframe);
assert_eq!(tail[0].payload.as_ref(), annexb(&[sps, pps, idr]).freeze().as_ref());
}
#[tokio::test(start_paused = true)]
async fn reinit_drops_superseded_pps_on_keyframe() {
let sps: &[u8] = &[0x67, 0x42, 0xc0, 0x1f];
let pps0: &[u8] = &[0x68, 0xce, 0x3c, 0x80];
let pps1: &[u8] = &[0x68, 0xce, 0x3c, 0x81];
let idr: &[u8] = &[0x65, 0x88, 0x84, 0x21];
let mut split = Split::new();
let _ = decode_one(&mut split, &mut annexb(&[sps, pps0, pps1, idr]), ts());
let _ = decode_one(&mut split, &mut annexb(&[sps, pps0, idr]), ts());
let third = decode_one(&mut split, &mut annexb(&[idr]), ts());
assert_eq!(third.len(), 1);
assert!(third[0].keyframe);
assert_eq!(third[0].payload.as_ref(), annexb(&[sps, pps0, idr]).freeze().as_ref());
}
}