use crate::avc::AvcConfig;
pub fn parse_hvcc(hvcc: &[u8]) -> Option<AvcConfig> {
if hvcc.len() < 23 || hvcc[0] != 1 {
return None;
}
let nal_len = (hvcc[21] & 0x03) as usize + 1;
let num_arrays = hvcc[22] as usize;
let mut headers = Vec::new();
let mut i = 23;
for _ in 0..num_arrays {
let nal_type = *hvcc.get(i)? & 0x3f;
i += 1;
let count = be16(hvcc, i)? as usize;
i += 2;
for _ in 0..count {
let len = be16(hvcc, i)? as usize;
i += 2;
let nal = hvcc.get(i..i + len)?;
if matches!(nal_type, 32 | 33 | 34) {
headers.extend_from_slice(&[0, 0, 0, 1]);
headers.extend_from_slice(nal);
}
i += len;
}
}
if headers.is_empty() {
return None;
}
Some(AvcConfig {
nal_len,
headers_annexb: headers,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SpsInfo {
pub profile_space: u8,
pub tier_flag: bool,
pub profile_idc: u8,
pub profile_compatibility_flags: u32,
pub constraint_flags: [u8; 6],
pub level_idc: u8,
pub chroma_format_idc: u8,
pub bit_depth_luma: u8,
pub bit_depth_chroma: u8,
pub max_sub_layers: u8,
pub temporal_id_nesting: bool,
pub width: u32,
pub height: u32,
}
pub fn sps_info(nal: &[u8]) -> Option<SpsInfo> {
let payload = if !nal.is_empty() && (nal[0] >> 1) & 0x3f == 33 {
nal.get(2..)?
} else {
nal
};
let rbsp = unescape(payload);
let mut r = Bits::new(&rbsp);
r.bits(4)?; let max_sub_layers_minus1 = r.bits(3)? as u8;
let temporal_id_nesting = r.bit()?;
let profile_space = r.bits(2)? as u8;
let tier_flag = r.bit()?;
let profile_idc = r.bits(5)? as u8;
let profile_compatibility_flags = r.bits(32)?;
let mut constraint_flags = [0u8; 6];
for c in constraint_flags.iter_mut() {
*c = r.bits(8)? as u8;
}
let level_idc = r.bits(8)? as u8;
let mut sub_profile = [false; 8];
let mut sub_level = [false; 8];
for i in 0..max_sub_layers_minus1 as usize {
sub_profile[i] = r.bit()?;
sub_level[i] = r.bit()?;
}
if max_sub_layers_minus1 > 0 {
for _ in max_sub_layers_minus1 as usize..8 {
r.bits(2)?;
}
}
for i in 0..max_sub_layers_minus1 as usize {
if sub_profile[i] {
r.bits(32)?;
r.bits(32)?;
r.bits(24)?;
}
if sub_level[i] {
r.bits(8)?;
}
}
r.ue()?; let chroma_format_idc = r.ue()? as u8;
if chroma_format_idc == 3 {
r.bit()?; }
let width = r.ue()?;
let height = r.ue()?;
let (mut cl, mut cr, mut ct, mut cb) = (0, 0, 0, 0);
if r.bit()? {
cl = r.ue()?;
cr = r.ue()?;
ct = r.ue()?;
cb = r.ue()?;
}
let bit_depth_luma = r.ue()? as u8 + 8;
let bit_depth_chroma = r.ue()? as u8 + 8;
let (sw, sh) = match chroma_format_idc {
1 => (2, 2),
2 => (2, 1),
_ => (1, 1),
};
Some(SpsInfo {
profile_space,
tier_flag,
profile_idc,
profile_compatibility_flags,
constraint_flags,
level_idc,
chroma_format_idc,
bit_depth_luma,
bit_depth_chroma,
max_sub_layers: max_sub_layers_minus1 + 1,
temporal_id_nesting,
width: width.saturating_sub(sw * (cl + cr)),
height: height.saturating_sub(sh * (ct + cb)),
})
}
pub fn sps_dimensions(nal: &[u8]) -> Option<(u32, u32)> {
sps_info(nal).map(|s| (s.width, s.height))
}
pub fn build_hvcc_record(vps: &[u8], sps: &[u8], pps: &[u8]) -> Option<Vec<u8>> {
let info = sps_info(sps)?;
let mut out = Vec::with_capacity(64 + vps.len() + sps.len() + pps.len());
out.push(1); out.push((info.profile_space << 6) | ((info.tier_flag as u8) << 5) | info.profile_idc);
out.extend_from_slice(&info.profile_compatibility_flags.to_be_bytes());
out.extend_from_slice(&info.constraint_flags);
out.push(info.level_idc);
out.extend_from_slice(&[0xf0, 0x00]); out.push(0xfc); out.push(0xfc | info.chroma_format_idc);
out.push(0xf8 | (info.bit_depth_luma - 8));
out.push(0xf8 | (info.bit_depth_chroma - 8));
out.extend_from_slice(&[0, 0]); out.push((info.max_sub_layers << 3) | ((info.temporal_id_nesting as u8) << 2) | 3);
let arrays: [(u8, &[u8]); 3] = [(32, vps), (33, sps), (34, pps)];
let present = arrays.iter().filter(|(_, n)| !n.is_empty()).count();
out.push(present as u8);
for (t, nal) in arrays {
if nal.is_empty() {
continue;
}
out.push(0x80 | t); out.extend_from_slice(&1u16.to_be_bytes());
out.extend_from_slice(&(nal.len() as u16).to_be_bytes());
out.extend_from_slice(nal);
}
Some(out)
}
pub fn find_nal_annexb(data: &[u8], nal_type: u8) -> Option<&[u8]> {
for nal in crate::avc::split_annexb(data) {
if !nal.is_empty() && (nal[0] >> 1) & 0x3f == nal_type {
return Some(nal);
}
}
None
}
fn be16(d: &[u8], i: usize) -> Option<u16> {
Some(u16::from_be_bytes([*d.get(i)?, *d.get(i + 1)?]))
}
fn unescape(ebsp: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(ebsp.len());
let mut zeros = 0;
for &b in ebsp {
if zeros >= 2 && b == 3 {
zeros = 0;
continue;
}
out.push(b);
zeros = if b == 0 { zeros + 1 } else { 0 };
}
out
}
struct Bits<'a> {
d: &'a [u8],
p: usize,
}
impl<'a> Bits<'a> {
fn new(d: &'a [u8]) -> Self {
Bits { d, p: 0 }
}
fn bit(&mut self) -> Option<bool> {
let b = *self.d.get(self.p / 8)?;
let v = (b >> (7 - self.p % 8)) & 1;
self.p += 1;
Some(v == 1)
}
fn bits(&mut self, n: u32) -> Option<u32> {
let mut v = 0u32;
for _ in 0..n {
v = (v << 1) | self.bit()? as u32;
}
Some(v)
}
fn ue(&mut self) -> Option<u32> {
let mut lz = 0;
while !self.bit()? {
lz += 1;
if lz > 31 {
return None;
}
}
if lz == 0 {
return Some(0);
}
Some((1u32 << lz) - 1 + self.bits(lz)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
const VPS: &[u8] = &[
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x99, 0xf0, 0x24,
];
const SPS: &[u8] = &[
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x99, 0xa0, 0x06, 0x82, 0x01, 0xe1, 0xfe, 0x5f, 0x92, 0x46, 0xd9, 0x6c,
0x80,
];
const PPS: &[u8] = &[0x44, 0x01, 0xc1, 0x90, 0x95, 0x81, 0x12];
#[test]
fn reads_geometry_and_profile() {
let i = sps_info(SPS).expect("sps");
assert_eq!((i.width, i.height), (832, 480));
assert_eq!(i.level_idc, 153);
assert_eq!(i.max_sub_layers, 1);
assert_eq!(i.chroma_format_idc, 1);
assert_eq!(i.bit_depth_luma, 8);
assert_eq!(i.bit_depth_chroma, 8);
assert_eq!(i.profile_idc, 1);
assert_eq!(sps_dimensions(SPS), Some((832, 480)));
assert_eq!(sps_dimensions(&SPS[2..]), Some((832, 480)));
}
#[test]
fn hvcc_roundtrip() {
let (vps, pps) = (VPS, PPS);
let rec = build_hvcc_record(vps, SPS, pps).expect("build");
assert_eq!(rec[0], 1);
assert_eq!(rec[21] & 3, 3, "4-byte NAL length");
assert_eq!(rec[22], 3, "three arrays");
let cfg = parse_hvcc(&rec).expect("parse");
assert_eq!(cfg.nal_len, 4);
let mut want = Vec::new();
for n in [vps, SPS, pps] {
want.extend_from_slice(&[0, 0, 0, 1]);
want.extend_from_slice(n);
}
assert_eq!(cfg.headers_annexb, want);
}
#[test]
fn rejects_junk() {
assert!(parse_hvcc(&[]).is_none());
assert!(parse_hvcc(&[0u8; 40]).is_none());
assert!(sps_info(&[0x42, 0x01]).is_none());
}
}