pub(crate) struct AvcConfig {
pub length_size: u8,
pub parameter_sets: Vec<Vec<u8>>,
}
pub(crate) struct HevcConfig {
pub length_size: u8,
pub parameter_sets: Vec<Vec<u8>>,
}
pub(crate) fn parse_avcc(avcc: &[u8]) -> Option<AvcConfig> {
if avcc.len() < 7 {
return None;
}
let length_size = (avcc[4] & 0x03) + 1;
if !matches!(length_size, 1 | 2 | 4) {
return None;
}
let num_sps = (avcc[5] & 0x1F) as usize;
let mut out: Vec<Vec<u8>> = Vec::new();
let mut cur = 6;
for _ in 0..num_sps {
if cur + 2 > avcc.len() {
return None;
}
let nalu_len = u16::from_be_bytes([avcc[cur], avcc[cur + 1]]) as usize;
cur += 2;
if cur + nalu_len > avcc.len() {
return None;
}
out.push(avcc[cur..cur + nalu_len].to_vec());
cur += nalu_len;
}
if cur >= avcc.len() {
return Some(AvcConfig {
length_size,
parameter_sets: out,
});
}
let num_pps = avcc[cur] as usize;
cur += 1;
for _ in 0..num_pps {
if cur + 2 > avcc.len() {
return None;
}
let nalu_len = u16::from_be_bytes([avcc[cur], avcc[cur + 1]]) as usize;
cur += 2;
if cur + nalu_len > avcc.len() {
return None;
}
out.push(avcc[cur..cur + nalu_len].to_vec());
cur += nalu_len;
}
Some(AvcConfig {
length_size,
parameter_sets: out,
})
}
pub(crate) fn parse_hvcc(hvcc: &[u8]) -> Option<HevcConfig> {
if hvcc.len() < 23 {
return None;
}
let length_size = (hvcc[21] & 0x03) + 1;
if !matches!(length_size, 1 | 2 | 4) {
return None;
}
let num_arrays = hvcc[22] as usize;
let mut out: Vec<Vec<u8>> = Vec::new();
let mut cur = 23;
for _ in 0..num_arrays {
if cur + 3 > hvcc.len() {
return None;
}
let _array_hdr = hvcc[cur];
let num_nalus = u16::from_be_bytes([hvcc[cur + 1], hvcc[cur + 2]]) as usize;
cur += 3;
for _ in 0..num_nalus {
if cur + 2 > hvcc.len() {
return None;
}
let nalu_len = u16::from_be_bytes([hvcc[cur], hvcc[cur + 1]]) as usize;
cur += 2;
if cur + nalu_len > hvcc.len() {
return None;
}
out.push(hvcc[cur..cur + nalu_len].to_vec());
cur += nalu_len;
}
}
Some(HevcConfig {
length_size,
parameter_sets: out,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum NaluCodec {
Avc,
Hevc,
}
pub(crate) struct ParamSetTracker {
codec: NaluCodec,
sps_emitted: bool,
pps_emitted: bool,
vps_emitted: bool,
}
impl ParamSetTracker {
pub(crate) fn new(codec: NaluCodec) -> Self {
Self {
codec,
sps_emitted: false,
pps_emitted: false,
vps_emitted: matches!(codec, NaluCodec::Avc),
}
}
#[allow(dead_code)]
pub(crate) fn note_param_sets_emitted(&mut self, nalus: &[Vec<u8>]) {
for n in nalus {
self.observe(n);
}
}
fn observe(&mut self, nalu: &[u8]) {
match self.classify(nalu) {
Some(NalKind::Vps) => self.vps_emitted = true,
Some(NalKind::Sps) => self.sps_emitted = true,
Some(NalKind::Pps) => self.pps_emitted = true,
_ => {}
}
}
fn classify(&self, nalu: &[u8]) -> Option<NalKind> {
if nalu.is_empty() {
return None;
}
match self.codec {
NaluCodec::Avc => {
let t = nalu[0] & 0x1F;
match t {
5 => Some(NalKind::Idr),
7 => Some(NalKind::Sps),
8 => Some(NalKind::Pps),
_ => Some(NalKind::Other),
}
}
NaluCodec::Hevc => {
if nalu.is_empty() {
return None;
}
let t = (nalu[0] >> 1) & 0x3F;
match t {
16..=23 => Some(NalKind::Idr),
32 => Some(NalKind::Vps),
33 => Some(NalKind::Sps),
34 => Some(NalKind::Pps),
_ => Some(NalKind::Other),
}
}
}
}
fn fully_emitted(&self) -> bool {
self.sps_emitted && self.pps_emitted && self.vps_emitted
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum NalKind {
Vps,
Sps,
Pps,
Idr,
Other,
}
pub(crate) fn length_prefixed_to_annexb_tracked(
sample: &[u8],
length_size: u8,
tracker: &mut ParamSetTracker,
param_sets: &[Vec<u8>],
) -> Vec<u8> {
const START_CODE: [u8; 4] = [0x00, 0x00, 0x00, 0x01];
let ls = length_size as usize;
debug_assert!(ls == 1 || ls == 2 || ls == 4, "invalid length_size {ls}");
let mut nalus: Vec<(usize, usize, NalKind)> = Vec::new();
{
let mut pos = 0;
while pos + ls <= sample.len() {
let nal_size = read_be_uint(&sample[pos..pos + ls], ls);
pos += ls;
if nal_size == 0 || pos + nal_size > sample.len() {
break;
}
let kind = tracker
.classify(&sample[pos..pos + nal_size])
.unwrap_or(NalKind::Other);
nalus.push((pos, nal_size, kind));
pos += nal_size;
}
}
let has_irap = nalus.iter().any(|(_, _, k)| matches!(k, NalKind::Idr));
let prepend_now = has_irap && !tracker.fully_emitted();
let mut out = Vec::with_capacity(sample.len() + 4 * param_sets.len() + 16);
if prepend_now {
let irap_idx = nalus
.iter()
.position(|(_, _, k)| matches!(k, NalKind::Idr))
.expect("has_irap implies position is Some");
for (off, len, kind) in &nalus[..irap_idx] {
out.extend_from_slice(&START_CODE);
out.extend_from_slice(&sample[*off..*off + *len]);
tracker_observe(tracker, *kind);
}
for nalu in param_sets {
let kind = tracker.classify(nalu).unwrap_or(NalKind::Other);
let needed = match kind {
NalKind::Vps => !tracker.vps_emitted,
NalKind::Sps => !tracker.sps_emitted,
NalKind::Pps => !tracker.pps_emitted,
_ => false,
};
if needed {
out.extend_from_slice(&START_CODE);
out.extend_from_slice(nalu);
tracker_observe(tracker, kind);
}
}
for (off, len, kind) in &nalus[irap_idx..] {
out.extend_from_slice(&START_CODE);
out.extend_from_slice(&sample[*off..*off + *len]);
tracker_observe(tracker, *kind);
}
} else {
for (off, len, kind) in &nalus {
out.extend_from_slice(&START_CODE);
out.extend_from_slice(&sample[*off..*off + *len]);
tracker_observe(tracker, *kind);
}
}
out
}
fn tracker_observe(tracker: &mut ParamSetTracker, kind: NalKind) {
match kind {
NalKind::Vps => tracker.vps_emitted = true,
NalKind::Sps => tracker.sps_emitted = true,
NalKind::Pps => tracker.pps_emitted = true,
_ => {}
}
}
fn read_be_uint(buf: &[u8], n: usize) -> usize {
let mut v: usize = 0;
for &b in &buf[..n] {
v = (v << 8) | b as usize;
}
v
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn avcc_roundtrip_length_size_four() {
let sps = [0x67u8, 0x42, 0x00, 0x1e, 0xab];
let pps = [0x68u8, 0xce, 0x3c, 0x80];
let mut avcc = vec![
0x01, 0x42, 0x00, 0x1e,
0xff, 0xe1, ];
avcc.extend_from_slice(&(sps.len() as u16).to_be_bytes());
avcc.extend_from_slice(&sps);
avcc.push(0x01);
avcc.extend_from_slice(&(pps.len() as u16).to_be_bytes());
avcc.extend_from_slice(&pps);
let cfg = parse_avcc(&avcc).expect("parse avcc");
assert_eq!(cfg.length_size, 4);
assert_eq!(cfg.parameter_sets.len(), 2);
assert_eq!(&cfg.parameter_sets[0], &sps);
assert_eq!(&cfg.parameter_sets[1], &pps);
}
#[test]
fn avcc_honors_length_size_two() {
let mut avcc = vec![
0x01, 0x42, 0x00, 0x1e, 0xfd, 0xe0, ];
avcc.push(0x00); let cfg = parse_avcc(&avcc).expect("parse avcc ls=2");
assert_eq!(cfg.length_size, 2);
assert!(cfg.parameter_sets.is_empty());
}
fn lp4_sample(nalus: &[&[u8]]) -> Vec<u8> {
let mut s = Vec::new();
for n in nalus {
s.extend_from_slice(&(n.len() as u32).to_be_bytes());
s.extend_from_slice(n);
}
s
}
fn avc_sps_nal() -> Vec<u8> {
vec![0x67, 0x42, 0x00, 0x1e, 0xaa]
}
fn avc_pps_nal() -> Vec<u8> {
vec![0x68, 0xce, 0x3c, 0x80]
}
fn avc_idr_nal() -> Vec<u8> {
vec![0x65, 0x88, 0x84, 0x00]
}
fn avc_p_slice_nal() -> Vec<u8> {
vec![0x41, 0x9a, 0x00, 0x00]
}
fn avc_sei_nal() -> Vec<u8> {
vec![0x06, 0x00, 0x80]
}
#[test]
fn tracked_avc_exoplayer_sps_only_then_idr() {
let sps = avc_sps_nal();
let pps = avc_pps_nal();
let avc_param_sets = vec![sps.clone(), pps.clone()];
let mut tracker = ParamSetTracker::new(NaluCodec::Avc);
let s0 = lp4_sample(&[&sps, &avc_p_slice_nal()]);
let out0 = length_prefixed_to_annexb_tracked(&s0, 4, &mut tracker, &avc_param_sets);
assert!(
!out0.windows(pps.len()).any(|w| w == pps.as_slice()),
"PPS must not be prepended on a non-IRAP sample"
);
assert!(tracker.sps_emitted);
assert!(!tracker.pps_emitted);
let idr = avc_idr_nal();
let s1 = lp4_sample(&[&idr]);
let out1 = length_prefixed_to_annexb_tracked(&s1, 4, &mut tracker, &avc_param_sets);
let pps_count = out1
.windows(pps.len())
.filter(|w| *w == pps.as_slice())
.count();
assert_eq!(
pps_count, 1,
"PPS must be prepended exactly once on first IDR"
);
let sps_count = out1
.windows(sps.len())
.filter(|w| *w == sps.as_slice())
.count();
assert_eq!(
sps_count, 0,
"SPS must not be re-emitted (already inline at sample 0)"
);
assert!(tracker.pps_emitted);
}
#[test]
fn tracked_avc_avcc_only_first_idr_prepends_both() {
let sps = avc_sps_nal();
let pps = avc_pps_nal();
let avc_param_sets = vec![sps.clone(), pps.clone()];
let mut tracker = ParamSetTracker::new(NaluCodec::Avc);
let idr = avc_idr_nal();
let s0 = lp4_sample(&[&idr]);
let out0 = length_prefixed_to_annexb_tracked(&s0, 4, &mut tracker, &avc_param_sets);
let sps_idx = out0.windows(sps.len()).position(|w| w == sps.as_slice());
let pps_idx = out0.windows(pps.len()).position(|w| w == pps.as_slice());
let idr_idx = out0.windows(idr.len()).position(|w| w == idr.as_slice());
assert!(sps_idx.is_some() && pps_idx.is_some() && idr_idx.is_some());
assert!(sps_idx.unwrap() < pps_idx.unwrap());
assert!(pps_idx.unwrap() < idr_idx.unwrap());
}
#[test]
fn tracked_avc_inline_sps_pps_idr_no_duplication() {
let sps = avc_sps_nal();
let pps = avc_pps_nal();
let idr = avc_idr_nal();
let avc_param_sets = vec![sps.clone(), pps.clone()];
let mut tracker = ParamSetTracker::new(NaluCodec::Avc);
let s0 = lp4_sample(&[&sps, &pps, &idr]);
let out0 = length_prefixed_to_annexb_tracked(&s0, 4, &mut tracker, &avc_param_sets);
let sps_count = out0
.windows(sps.len())
.filter(|w| *w == sps.as_slice())
.count();
let pps_count = out0
.windows(pps.len())
.filter(|w| *w == pps.as_slice())
.count();
assert_eq!(sps_count, 1, "SPS must appear exactly once when inline");
assert_eq!(pps_count, 1, "PPS must appear exactly once when inline");
}
#[test]
fn tracked_avc_sei_then_idr_inserts_params_between() {
let sps = avc_sps_nal();
let pps = avc_pps_nal();
let sei = avc_sei_nal();
let idr = avc_idr_nal();
let avc_param_sets = vec![sps.clone(), pps.clone()];
let mut tracker = ParamSetTracker::new(NaluCodec::Avc);
let s0 = lp4_sample(&[&sei, &idr]);
let out0 = length_prefixed_to_annexb_tracked(&s0, 4, &mut tracker, &avc_param_sets);
let sei_pos = out0
.windows(sei.len())
.position(|w| w == sei.as_slice())
.unwrap();
let sps_pos = out0
.windows(sps.len())
.position(|w| w == sps.as_slice())
.unwrap();
let pps_pos = out0
.windows(pps.len())
.position(|w| w == pps.as_slice())
.unwrap();
let idr_pos = out0
.windows(idr.len())
.position(|w| w == idr.as_slice())
.unwrap();
assert!(sei_pos < sps_pos);
assert!(sps_pos < pps_pos);
assert!(pps_pos < idr_pos);
}
#[test]
fn tracked_hevc_first_idr_prepends_vps_sps_pps() {
let vps: Vec<u8> = vec![0x40, 0x01, 0xAA];
let sps: Vec<u8> = vec![0x42, 0x01, 0xBB];
let pps: Vec<u8> = vec![0x44, 0x01, 0xCC];
let idr: Vec<u8> = vec![0x26, 0x01, 0xDD];
let hevc_param_sets = vec![vps.clone(), sps.clone(), pps.clone()];
let mut tracker = ParamSetTracker::new(NaluCodec::Hevc);
let s0 = lp4_sample(&[&idr]);
let out0 = length_prefixed_to_annexb_tracked(&s0, 4, &mut tracker, &hevc_param_sets);
let vps_pos = out0
.windows(vps.len())
.position(|w| w == vps.as_slice())
.unwrap();
let sps_pos = out0
.windows(sps.len())
.position(|w| w == sps.as_slice())
.unwrap();
let pps_pos = out0
.windows(pps.len())
.position(|w| w == pps.as_slice())
.unwrap();
let idr_pos = out0
.windows(idr.len())
.position(|w| w == idr.as_slice())
.unwrap();
assert!(vps_pos < sps_pos);
assert!(sps_pos < pps_pos);
assert!(pps_pos < idr_pos);
}
#[test]
fn tracked_avc_mixed_inline_sps_avcc_pps_only() {
let sps = avc_sps_nal();
let pps = avc_pps_nal();
let avc_param_sets = vec![sps.clone(), pps.clone()];
let mut tracker = ParamSetTracker::new(NaluCodec::Avc);
let s0 = lp4_sample(&[&sps, &avc_p_slice_nal()]);
let _ = length_prefixed_to_annexb_tracked(&s0, 4, &mut tracker, &avc_param_sets);
assert!(tracker.sps_emitted && !tracker.pps_emitted);
let idr = avc_idr_nal();
let s1 = lp4_sample(&[&idr]);
let out1 = length_prefixed_to_annexb_tracked(&s1, 4, &mut tracker, &avc_param_sets);
let sps_count = out1
.windows(sps.len())
.filter(|w| *w == sps.as_slice())
.count();
let pps_count = out1
.windows(pps.len())
.filter(|w| *w == pps.as_slice())
.count();
assert_eq!(sps_count, 0);
assert_eq!(pps_count, 1);
}
#[test]
fn tracked_avc_second_idr_no_reprepend() {
let sps = avc_sps_nal();
let pps = avc_pps_nal();
let idr = avc_idr_nal();
let avc_param_sets = vec![sps.clone(), pps.clone()];
let mut tracker = ParamSetTracker::new(NaluCodec::Avc);
let s0 = lp4_sample(&[&idr]);
let _ = length_prefixed_to_annexb_tracked(&s0, 4, &mut tracker, &avc_param_sets);
let s1 = lp4_sample(&[&idr]);
let out1 = length_prefixed_to_annexb_tracked(&s1, 4, &mut tracker, &avc_param_sets);
let sps_count = out1
.windows(sps.len())
.filter(|w| *w == sps.as_slice())
.count();
let pps_count = out1
.windows(pps.len())
.filter(|w| *w == pps.as_slice())
.count();
assert_eq!(sps_count, 0);
assert_eq!(pps_count, 0);
}
#[test]
fn tracked_avc_length_size_two() {
let sps = avc_sps_nal();
let pps = avc_pps_nal();
let idr = avc_idr_nal();
let avc_param_sets = vec![sps.clone(), pps.clone()];
let mut s = Vec::new();
s.extend_from_slice(&(idr.len() as u16).to_be_bytes());
s.extend_from_slice(&idr);
let mut tracker = ParamSetTracker::new(NaluCodec::Avc);
let out = length_prefixed_to_annexb_tracked(&s, 2, &mut tracker, &avc_param_sets);
assert!(out.windows(sps.len()).any(|w| w == sps.as_slice()));
assert!(out.windows(pps.len()).any(|w| w == pps.as_slice()));
assert!(out.windows(idr.len()).any(|w| w == idr.as_slice()));
}
#[test]
fn hvcc_extracts_vps_sps_pps_in_array_order() {
let vps = [0x40u8, 0x01, 0x0c];
let sps = [0x42u8, 0x01, 0x01];
let pps = [0x44u8, 0x01];
let mut hvcc = vec![0u8; 23];
hvcc[0] = 1; hvcc[21] = 0xf3; hvcc[22] = 3;
hvcc.push(32);
hvcc.extend_from_slice(&1u16.to_be_bytes()); hvcc.extend_from_slice(&(vps.len() as u16).to_be_bytes());
hvcc.extend_from_slice(&vps);
hvcc.push(33);
hvcc.extend_from_slice(&1u16.to_be_bytes());
hvcc.extend_from_slice(&(sps.len() as u16).to_be_bytes());
hvcc.extend_from_slice(&sps);
hvcc.push(34);
hvcc.extend_from_slice(&1u16.to_be_bytes());
hvcc.extend_from_slice(&(pps.len() as u16).to_be_bytes());
hvcc.extend_from_slice(&pps);
let cfg = parse_hvcc(&hvcc).expect("parse hvcc");
assert_eq!(cfg.length_size, 4);
assert_eq!(cfg.parameter_sets.len(), 3);
assert_eq!(&cfg.parameter_sets[0], &vps);
assert_eq!(&cfg.parameter_sets[1], &sps);
assert_eq!(&cfg.parameter_sets[2], &pps);
}
}