use bytes::{Bytes, BytesMut};
use scuffle_h265::NALUnitType;
use super::Error;
use crate::Result;
use crate::codec::annexb::{NalIterator, START_CODE};
pub struct Split {
tail: BytesMut,
current: Au,
vps: Vec<Bytes>,
sps: Vec<Bytes>,
pps: Vec<Bytes>,
zero: Option<tokio::time::Instant>,
pending: Vec<crate::container::Frame>,
}
#[derive(Default)]
struct Au {
chunks: BytesMut,
contains_idr: bool,
contains_slice: bool,
vps_seen: Vec<Bytes>,
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: Au::default(),
vps: Vec::new(),
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<()> {
if nal.len() < 2 {
return Err(Error::NalTooShort.into());
}
let header = nal.first().ok_or(Error::NalTooShort)?;
if (header >> 7) & 1 != 0 {
return Err(Error::ForbiddenZeroBit.into());
}
let nal_type = nal_unit_type(*header);
match nal_type {
NALUnitType::VpsNut => {
self.maybe_start_frame(pts)?;
crate::codec::annexb::push_distinct(&mut self.current.vps_seen, &nal);
}
NALUnitType::SpsNut => {
self.maybe_start_frame(pts)?;
crate::codec::annexb::push_distinct(&mut self.current.sps_seen, &nal);
}
NALUnitType::PpsNut => {
self.maybe_start_frame(pts)?;
crate::codec::annexb::push_distinct(&mut self.current.pps_seen, &nal);
}
NALUnitType::AudNut | NALUnitType::PrefixSeiNut | NALUnitType::SuffixSeiNut => {
self.maybe_start_frame(pts)?;
}
NALUnitType::IdrWRadl
| NALUnitType::IdrNLp
| NALUnitType::BlaNLp
| NALUnitType::BlaWRadl
| NALUnitType::BlaWLp
| NALUnitType::CraNut => {
if nal.get(2).ok_or(Error::NalTooShort)? & 0x80 != 0 {
self.maybe_start_frame(pts)?;
}
crate::codec::annexb::reconcile_keyframe_params(
&mut self.current.chunks,
&mut self.vps,
&mut self.current.vps_seen,
);
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;
}
NALUnitType::TrailN
| NALUnitType::TrailR
| NALUnitType::TsaN
| NALUnitType::TsaR
| NALUnitType::StsaN
| NALUnitType::StsaR
| NALUnitType::RadlN
| NALUnitType::RadlR
| NALUnitType::RaslN
| NALUnitType::RaslR => {
if nal.get(2).ok_or(Error::NalTooShort)? & 0x80 != 0 {
self.maybe_start_frame(pts)?;
}
self.current.contains_slice = true;
}
_ => {}
}
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.vps_seen.clear();
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 = Au::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
)?)
}
}
pub(super) fn nal_unit_type(header: u8) -> NALUnitType {
NALUnitType::from((header >> 1) & 0b111111)
}
#[cfg(test)]
mod tests {
use super::*;
const SC4: &[u8] = &[0, 0, 0, 1];
const VPS: &[u8] = &[0x40, 0x01, 0x0c]; const SPS: &[u8] = &[0x42, 0x01, 0x01]; const PPS: &[u8] = &[0x44, 0x01, 0xc0]; const IDR: &[u8] = &[0x26, 0x01, 0x80, 0xaa];
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 contains(haystack: &[u8], needle: &[u8]) -> bool {
haystack.windows(needle.len()).any(|w| w == needle)
}
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 mut split = Split::new();
let frames = decode_one(&mut split, &mut annexb(&[VPS, SPS, PPS, IDR]), ts());
assert_eq!(frames.len(), 1);
assert!(frames[0].keyframe);
assert!(contains(&frames[0].payload, VPS));
assert!(contains(&frames[0].payload, SPS));
assert!(contains(&frames[0].payload, PPS));
assert!(contains(&frames[0].payload, IDR));
}
#[tokio::test(start_paused = true)]
async fn params_then_bare_keyframe_self_contained() {
let mut split = Split::new();
assert!(split.decode(&annexb(&[VPS, 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!(contains(&frames[0].payload, VPS));
assert!(contains(&frames[0].payload, SPS));
assert!(contains(&frames[0].payload, PPS));
}
#[tokio::test(start_paused = true)]
async fn reinjects_all_cached_pps_on_keyframe() {
const PPS1: &[u8] = &[0x44, 0x01, 0xc1];
let mut split = Split::new();
let first = decode_one(&mut split, &mut annexb(&[VPS, SPS, PPS, 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(&[VPS, SPS, PPS, PPS1, IDR]).freeze().as_ref()
);
}
#[tokio::test(start_paused = true)]
async fn bare_idr_after_delta_splits() {
const TRAIL: &[u8] = &[0x02, 0x01, 0x80, 0x33];
const AUD: &[u8] = &[0x46, 0x01, 0x50];
let mut split = Split::new();
let frames = split
.decode(&annexb(&[VPS, SPS, PPS, IDR, TRAIL, 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(&[TRAIL]).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(&[VPS, SPS, PPS, IDR]).freeze().as_ref()
);
}
#[tokio::test(start_paused = true)]
async fn reinit_drops_superseded_pps_on_keyframe() {
const PPS1: &[u8] = &[0x44, 0x01, 0xc1];
let mut split = Split::new();
let _ = decode_one(&mut split, &mut annexb(&[VPS, SPS, PPS, PPS1, IDR]), ts());
let _ = decode_one(&mut split, &mut annexb(&[VPS, SPS, PPS, 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(&[VPS, SPS, PPS, IDR]).freeze().as_ref()
);
}
}