pub struct AvcConfig {
pub nal_len: usize,
pub headers_annexb: Vec<u8>,
}
pub fn parse_avcc(avcc: &[u8]) -> Option<AvcConfig> {
if avcc.len() < 6 {
return None;
}
let nal_len = (avcc[4] & 0x03) as usize + 1;
let mut headers = Vec::new();
let mut i = 5;
let num_sps = avcc[i] & 0x1F;
i += 1;
for _ in 0..num_sps {
let len = be16(avcc, i)? as usize;
i += 2;
let nal = avcc.get(i..i + len)?;
headers.extend_from_slice(&[0, 0, 0, 1]);
headers.extend_from_slice(nal);
i += len;
}
let num_pps = *avcc.get(i)?;
i += 1;
for _ in 0..num_pps {
let len = be16(avcc, i)? as usize;
i += 2;
let nal = avcc.get(i..i + len)?;
headers.extend_from_slice(&[0, 0, 0, 1]);
headers.extend_from_slice(nal);
i += len;
}
Some(AvcConfig {
nal_len,
headers_annexb: headers,
})
}
pub fn avcc_to_annexb(sample: &[u8], nal_len: usize, out: &mut Vec<u8>) {
let mut i = 0;
while i + nal_len <= sample.len() {
let mut len = 0usize;
for _ in 0..nal_len {
len = (len << 8) | sample[i] as usize;
i += 1;
}
if i + len > sample.len() {
break;
}
out.extend_from_slice(&[0, 0, 0, 1]);
out.extend_from_slice(&sample[i..i + len]);
i += len;
}
}
pub fn find_sps_annexb(data: &[u8]) -> Option<&[u8]> {
let mut i = 0;
let mut nal_start: Option<usize> = None;
while i + 3 <= data.len() {
if data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 {
if let Some(s) = nal_start {
let end = if i > 0 && data[i - 1] == 0 { i - 1 } else { i };
if data[s] & 0x1F == 7 {
return Some(&data[s..end]);
}
}
nal_start = Some(i + 3);
i += 3;
} else {
i += 1;
}
}
let s = nal_start?;
(*data.get(s)? & 0x1F == 7).then(|| &data[s..])
}
pub fn sps_dimensions(nal: &[u8]) -> Option<(u32, u32)> {
if nal.first()? & 0x1F != 7 {
return None;
}
let mut rbsp = Vec::with_capacity(nal.len());
let mut zeros = 0u32;
for &b in &nal[1..] {
if zeros >= 2 && b == 3 {
zeros = 0;
continue;
}
zeros = if b == 0 { zeros + 1 } else { 0 };
rbsp.push(b);
}
let mut r = Bits::new(&rbsp);
let profile_idc = r.bits(8)?;
r.bits(16)?; r.ue()?;
let mut chroma_format_idc = 1; let mut separate_colour_plane = false;
if matches!(
profile_idc,
100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135
) {
chroma_format_idc = r.ue()?;
if chroma_format_idc == 3 {
separate_colour_plane = r.bit()? == 1;
}
r.ue()?; r.ue()?; r.bit()?; if r.bit()? == 1 {
let lists = if chroma_format_idc != 3 { 8 } else { 12 };
for i in 0..lists {
if r.bit()? == 1 {
skip_scaling_list(&mut r, if i < 6 { 16 } else { 64 })?;
}
}
}
}
r.ue()?; match r.ue()? {
0 => {
r.ue()?; }
1 => {
r.bit()?; r.se()?; r.se()?; let n = r.ue()?;
for _ in 0..n {
r.se()?; }
}
_ => {}
}
r.ue()?; r.bit()?;
let width_mbs = r.ue()? + 1;
let height_map_units = r.ue()? + 1;
let frame_mbs_only = r.bit()?;
if frame_mbs_only == 0 {
r.bit()?; }
r.bit()?;
let mut width = width_mbs.checked_mul(16)?;
let mut height = (2 - frame_mbs_only)
.checked_mul(height_map_units)?
.checked_mul(16)?;
if r.bit()? == 1 {
let (left, right, top, bottom) = (r.ue()?, r.ue()?, r.ue()?, r.ue()?);
let chroma_array_type = if separate_colour_plane {
0
} else {
chroma_format_idc
};
let (sub_w, sub_h) = match chroma_array_type {
1 => (2, 2), 2 => (2, 1), _ => (1, 1), };
let crop_y = sub_h * (2 - frame_mbs_only);
width = width.checked_sub((left + right).checked_mul(sub_w)?)?;
height = height.checked_sub((top + bottom).checked_mul(crop_y)?)?;
}
(width > 0 && height > 0).then_some((width, height))
}
pub fn split_annexb(data: &[u8]) -> Vec<&[u8]> {
let mut nals = Vec::new();
let mut i = 0;
let mut nal_start: Option<usize> = None;
while i + 3 <= data.len() {
if data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 {
if let Some(s) = nal_start {
let end = if i > s && data[i - 1] == 0 { i - 1 } else { i };
if end > s {
nals.push(&data[s..end]);
}
}
i += 3;
nal_start = Some(i);
} else {
i += 1;
}
}
if let Some(s) = nal_start {
if s < data.len() {
nals.push(&data[s..]);
}
}
nals
}
pub fn build_avcc_record(sps: &[u8], pps: &[u8]) -> Vec<u8> {
let mut b = Vec::new();
b.push(1); b.push(*sps.get(1).unwrap_or(&0x42)); b.push(*sps.get(2).unwrap_or(&0)); b.push(*sps.get(3).unwrap_or(&30)); b.push(0xFF); b.push(0xE1); b.extend_from_slice(&(sps.len() as u16).to_be_bytes());
b.extend_from_slice(sps);
b.push(1); b.extend_from_slice(&(pps.len() as u16).to_be_bytes());
b.extend_from_slice(pps);
b
}
fn skip_scaling_list(r: &mut Bits, size: u32) -> Option<()> {
let mut next = 8i32;
for _ in 0..size {
if next != 0 {
next = (next + r.se()? + 256) % 256;
}
if next == 0 {
break; }
}
Some(())
}
struct Bits<'a> {
d: &'a [u8],
pos: usize, }
impl<'a> Bits<'a> {
fn new(d: &'a [u8]) -> Bits<'a> {
Bits { d, pos: 0 }
}
fn bit(&mut self) -> Option<u32> {
let b = *self.d.get(self.pos / 8)?;
let v = (b >> (7 - self.pos % 8)) & 1;
self.pos += 1;
Some(v as u32)
}
fn bits(&mut self, n: u32) -> Option<u32> {
let mut v = 0;
for _ in 0..n {
v = (v << 1) | self.bit()?;
}
Some(v)
}
fn ue(&mut self) -> Option<u32> {
let mut zeros = 0;
while self.bit()? == 0 {
zeros += 1;
if zeros > 31 {
return None;
}
}
Some((1u32 << zeros) - 1 + if zeros > 0 { self.bits(zeros)? } else { 0 })
}
fn se(&mut self) -> Option<i32> {
let k = self.ue()? as i64;
Some(if k % 2 == 1 { (k + 1) / 2 } else { -(k / 2) } as i32)
}
}
fn be16(d: &[u8], i: usize) -> Option<u16> {
Some(((*d.get(i)? as u16) << 8) | *d.get(i + 1)? as u16)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn avcc_roundtrip_to_annexb() {
let avcc = [
1, 100, 0, 31, 0xFF, 0xE1, 0, 3, 0x67, 0xAA, 0xBB, 1, 0, 2, 0x68, 0xCC,
];
let cfg = parse_avcc(&avcc).unwrap();
assert_eq!(cfg.nal_len, 4);
assert_eq!(
cfg.headers_annexb,
&[0, 0, 0, 1, 0x67, 0xAA, 0xBB, 0, 0, 0, 1, 0x68, 0xCC]
);
let sample = [0, 0, 0, 2, 0x41, 0x9A, 0, 0, 0, 1, 0x06];
let mut out = Vec::new();
avcc_to_annexb(&sample, 4, &mut out);
assert_eq!(out, &[0, 0, 0, 1, 0x41, 0x9A, 0, 0, 0, 1, 0x06]);
}
#[test]
fn finds_sps_between_start_codes() {
let data = [
0, 0, 0, 1, 0x09, 0xF0, 0, 0, 0, 1, 0x67, 0xAA, 0, 0, 1, 0x68,
];
assert_eq!(find_sps_annexb(&data), Some(&[0x67u8, 0xAA][..]));
let data = [0, 0, 1, 0x67, 0xAA, 0xBB];
assert_eq!(find_sps_annexb(&data), Some(&[0x67u8, 0xAA, 0xBB][..]));
assert_eq!(find_sps_annexb(&[0, 0, 1, 0x68, 0x00]), None);
}
#[test]
fn sps_dimensions_x264_640x360() {
let sps = [
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xA0, 0x2F, 0xF9, 0x70, 0x11, 0x00, 0x00,
0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x30, 0x0F, 0x16, 0x2D, 0x96,
];
assert_eq!(sps_dimensions(&sps), Some((640, 360)));
}
#[test]
fn sps_dimensions_baseline_176x144() {
let sps = [
0x67, 0x42, 0xC0, 0x0D, 0x8C, 0x8D, 0x41, 0x62, 0x64, 0x0A, 0x60, 0x80,
];
assert_eq!(sps_dimensions(&sps), Some((176, 144)));
}
}