use crate::error::{Error, Result};
use crate::identify::Encoding;
const START_CODE: [u8; 4] = [0, 0, 0, 1];
fn malformed(reason: impl Into<String>) -> Error {
Error::Decode {
encoding: Encoding::H264,
reason: reason.into(),
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AvcConfig {
pub nal_length_size: usize,
pub sequence_parameter_sets: Vec<Vec<u8>>,
pub picture_parameter_sets: Vec<Vec<u8>>,
}
impl AvcConfig {
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 7 {
return Err(malformed(format!(
"an avcC record is at least 7 bytes; this one is {}",
data.len()
)));
}
let nal_length_size = (data[4] & 0b11) as usize + 1;
if !matches!(nal_length_size, 1 | 2 | 4) {
return Err(malformed(format!(
"avcC claims a {nal_length_size}-byte NAL length, which is not 1, 2, or 4"
)));
}
let mut cursor = 5;
let sps_count = (data[cursor] & 0b0001_1111) as usize;
cursor += 1;
let sequence_parameter_sets = read_sets(data, &mut cursor, sps_count, "SPS")?;
if cursor >= data.len() {
return Err(malformed("avcC ends before its PPS count"));
}
let pps_count = data[cursor] as usize;
cursor += 1;
let picture_parameter_sets = read_sets(data, &mut cursor, pps_count, "PPS")?;
Ok(AvcConfig {
nal_length_size,
sequence_parameter_sets,
picture_parameter_sets,
})
}
pub fn to_annex_b(&self) -> Vec<u8> {
let mut output = Vec::new();
for set in self
.sequence_parameter_sets
.iter()
.chain(&self.picture_parameter_sets)
{
output.extend_from_slice(&START_CODE);
output.extend_from_slice(set);
}
output
}
}
fn read_sets(data: &[u8], cursor: &mut usize, count: usize, what: &str) -> Result<Vec<Vec<u8>>> {
let mut sets = Vec::with_capacity(count);
for index in 0..count {
if *cursor + 2 > data.len() {
return Err(malformed(format!(
"avcC ends inside {what} {index}'s length"
)));
}
let length = u16::from_be_bytes([data[*cursor], data[*cursor + 1]]) as usize;
*cursor += 2;
let end = cursor
.checked_add(length)
.filter(|end| *end <= data.len())
.ok_or_else(|| {
malformed(format!(
"{what} {index} claims {length} bytes, past the end of the record"
))
})?;
sets.push(data[*cursor..end].to_vec());
*cursor = end;
}
Ok(sets)
}
pub fn length_prefixed_to_annex_b(data: &[u8], nal_length_size: usize) -> Result<Vec<u8>> {
if !matches!(nal_length_size, 1 | 2 | 4) {
return Err(malformed(format!(
"a NAL length field is 1, 2, or 4 bytes, not {nal_length_size}"
)));
}
let mut output = Vec::with_capacity(data.len() + 8);
let mut cursor = 0;
while cursor < data.len() {
if cursor + nal_length_size > data.len() {
return Err(malformed(format!(
"a NAL length field is cut short {} bytes from the end",
data.len() - cursor
)));
}
let mut length = 0_usize;
for byte in &data[cursor..cursor + nal_length_size] {
length = (length << 8) | *byte as usize;
}
cursor += nal_length_size;
let end = cursor
.checked_add(length)
.filter(|end| *end <= data.len())
.ok_or_else(|| {
malformed(format!(
"a NAL unit claims {length} bytes with only {} left",
data.len() - cursor
))
})?;
if length > 0 {
output.extend_from_slice(&START_CODE);
output.extend_from_slice(&data[cursor..end]);
}
cursor = end;
}
Ok(output)
}
pub fn annex_b_to_length_prefixed(data: &[u8], nal_length_size: usize) -> Result<Vec<u8>> {
if !matches!(nal_length_size, 1 | 2 | 4) {
return Err(malformed(format!(
"a NAL length field is 1, 2, or 4 bytes, not {nal_length_size}"
)));
}
let mut output = Vec::with_capacity(data.len());
for unit in annex_b_units(data) {
let length = unit.len();
let ceiling = match nal_length_size {
1 => u8::MAX as usize,
2 => u16::MAX as usize,
_ => u32::MAX as usize,
};
if length > ceiling {
return Err(malformed(format!(
"a {length}-byte NAL unit does not fit in a {nal_length_size}-byte length field"
)));
}
output.extend_from_slice(&length.to_be_bytes()[8 - nal_length_size..]);
output.extend_from_slice(unit);
}
Ok(output)
}
pub fn annex_b_units(data: &[u8]) -> impl Iterator<Item = &[u8]> {
let mut starts = Vec::new();
let mut index = 0;
while index + 3 <= data.len() {
if data[index] == 0 && data[index + 1] == 0 && data[index + 2] == 1 {
starts.push(index + 3);
index += 3;
} else {
index += 1;
}
}
let ends: Vec<usize> = starts
.iter()
.skip(1)
.map(|next| {
let code_start = next - 3;
if code_start > 0 && data[code_start - 1] == 0 {
code_start - 1
} else {
code_start
}
})
.chain(std::iter::once(data.len()))
.collect();
starts
.into_iter()
.zip(ends)
.filter(|(start, end)| end > start)
.map(move |(start, end)| &data[start..end])
}
impl AvcConfig {
pub fn new(sps: Vec<u8>, pps: Vec<u8>) -> Self {
AvcConfig {
nal_length_size: 4,
sequence_parameter_sets: vec![sps],
picture_parameter_sets: vec![pps],
}
}
pub fn to_record(&self) -> Vec<u8> {
let Some(sps) = self.sequence_parameter_sets.first().filter(|sps| sps.len() >= 4) else {
return Vec::new();
};
let mut record = vec![
1, sps[1], sps[2], sps[3], 0xFC | (self.nal_length_size.clamp(1, 4) as u8 - 1),
0xE0 | (self.sequence_parameter_sets.len().min(31) as u8),
];
for set in self.sequence_parameter_sets.iter().take(31) {
record.extend_from_slice(&(set.len() as u16).to_be_bytes());
record.extend_from_slice(set);
}
record.push(self.picture_parameter_sets.len().min(255) as u8);
for set in self.picture_parameter_sets.iter().take(255) {
record.extend_from_slice(&(set.len() as u16).to_be_bytes());
record.extend_from_slice(set);
}
record
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct AccessUnit {
pub sps: Option<Vec<u8>>,
pub pps: Option<Vec<u8>>,
pub data: Vec<u8>,
pub is_keyframe: bool,
}
impl AccessUnit {
pub fn split(annex_b: &[u8]) -> Self {
let mut unit = AccessUnit::default();
for nal in annex_b_units(annex_b) {
match nal[0] & 0x1F {
7 => unit.sps = Some(nal.to_vec()),
8 => unit.pps = Some(nal.to_vec()),
9 => {}
kind => {
unit.is_keyframe |= kind == 5;
unit.data.extend_from_slice(&(nal.len() as u32).to_be_bytes());
unit.data.extend_from_slice(nal);
}
}
}
unit
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SequenceParameterSet {
pub profile_idc: u8,
pub level_idc: u8,
pub chroma_format_idc: u32,
pub bit_depth: u32,
pub width: u32,
pub height: u32,
pub full_range: Option<bool>,
pub primaries: Option<u8>,
pub transfer: Option<u8>,
pub matrix: Option<u8>,
pub max_num_reorder_frames: Option<u32>,
}
impl SequenceParameterSet {
pub fn parse(nal: &[u8]) -> Result<Self> {
let Some((&header, payload)) = nal.split_first() else {
return Err(malformed("an empty NAL unit is not a sequence parameter set"));
};
if header & 0x1F != 7 {
return Err(malformed(format!(
"NAL unit type {} is not a sequence parameter set (7)",
header & 0x1F
)));
}
let rbsp = unescape(payload);
let mut bits = Bits::new(&rbsp);
let short = |_| malformed("the sequence parameter set ends before the picture size");
let profile_idc = bits.u(8).map_err(short)? as u8;
bits.u(8).map_err(short)?; let level_idc = bits.u(8).map_err(short)? as u8;
bits.ue().map_err(short)?;
let mut chroma_format_idc = 1;
let mut separate_colour_plane = false;
let mut bit_depth = 8;
if matches!(
profile_idc,
100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135
) {
chroma_format_idc = bits.ue().map_err(short)?;
if chroma_format_idc == 3 {
separate_colour_plane = bits.flag().map_err(short)?;
}
bit_depth = 8 + bits.ue().map_err(short)?;
bits.ue().map_err(short)?; bits.flag().map_err(short)?;
if bits.flag().map_err(short)? {
let lists = if chroma_format_idc == 3 { 12 } else { 8 };
for list in 0..lists {
if bits.flag().map_err(short)? {
bits.skip_scaling_list(if list < 6 { 16 } else { 64 })
.map_err(short)?;
}
}
}
}
bits.ue().map_err(short)?;
match bits.ue().map_err(short)? {
0 => {
bits.ue().map_err(short)?; }
1 => {
bits.flag().map_err(short)?; bits.se().map_err(short)?; bits.se().map_err(short)?; let cycle = bits.ue().map_err(short)?;
for _ in 0..cycle.min(255) {
bits.se().map_err(short)?;
}
}
_ => {}
}
bits.ue().map_err(short)?; bits.flag().map_err(short)?;
let width_in_mbs = bits.ue().map_err(short)? + 1;
let height_in_map_units = bits.ue().map_err(short)? + 1;
let frame_mbs_only = bits.flag().map_err(short)?;
if !frame_mbs_only {
bits.flag().map_err(short)?; }
bits.flag().map_err(short)?;
let mut crop = [0_u32; 4];
if bits.flag().map_err(short)? {
for edge in &mut crop {
*edge = bits.ue().map_err(short)?;
}
}
let chroma_array_type = if separate_colour_plane { 0 } else { chroma_format_idc };
let field = 2 - u32::from(frame_mbs_only);
let (crop_x, crop_y) = match chroma_array_type {
0 => (1, field),
1 => (2, 2 * field),
2 => (2, field),
_ => (1, field),
};
let width = (width_in_mbs * 16).saturating_sub(crop_x * (crop[0] + crop[1]));
let height = (field * height_in_map_units * 16).saturating_sub(crop_y * (crop[2] + crop[3]));
let mut sps = SequenceParameterSet {
profile_idc,
level_idc,
chroma_format_idc,
bit_depth,
width,
height,
full_range: None,
primaries: None,
transfer: None,
matrix: None,
max_num_reorder_frames: None,
};
if bits.flag().unwrap_or(false) {
let _ = sps.read_vui(&mut bits);
}
Ok(sps)
}
fn read_vui(&mut self, bits: &mut Bits<'_>) -> std::result::Result<(), Short> {
if bits.flag()? {
if bits.u(8)? == 255 {
bits.u(16)?;
bits.u(16)?;
}
}
if bits.flag()? {
bits.flag()?; }
if bits.flag()? {
bits.u(3)?; self.full_range = Some(bits.flag()?);
if bits.flag()? {
self.primaries = Some(bits.u(8)? as u8);
self.transfer = Some(bits.u(8)? as u8);
self.matrix = Some(bits.u(8)? as u8);
}
}
if bits.flag()? {
bits.ue()?; bits.ue()?; }
if bits.flag()? {
bits.u(32)?; bits.u(32)?; bits.flag()?; }
let nal_hrd = bits.flag()?;
if nal_hrd {
bits.skip_hrd()?;
}
let vcl_hrd = bits.flag()?;
if vcl_hrd {
bits.skip_hrd()?;
}
if nal_hrd || vcl_hrd {
bits.flag()?; }
bits.flag()?;
if bits.flag()? {
bits.flag()?; bits.ue()?; bits.ue()?; bits.ue()?; bits.ue()?; self.max_num_reorder_frames = Some(bits.ue()?);
}
Ok(())
}
}
fn unescape(data: &[u8]) -> Vec<u8> {
let mut output = Vec::with_capacity(data.len());
let mut zeros = 0;
for &byte in data {
if zeros >= 2 && byte == 3 {
zeros = 0;
continue;
}
zeros = if byte == 0 { zeros + 1 } else { 0 };
output.push(byte);
}
output
}
#[derive(Debug)]
struct Short;
struct Bits<'a> {
data: &'a [u8],
position: usize,
}
impl<'a> Bits<'a> {
fn new(data: &'a [u8]) -> Self {
Bits { data, position: 0 }
}
fn bit(&mut self) -> std::result::Result<u32, Short> {
let byte = self.data.get(self.position / 8).ok_or(Short)?;
let bit = (byte >> (7 - self.position % 8)) & 1;
self.position += 1;
Ok(u32::from(bit))
}
fn flag(&mut self) -> std::result::Result<bool, Short> {
Ok(self.bit()? == 1)
}
fn u(&mut self, count: u32) -> std::result::Result<u32, Short> {
let mut value = 0_u64;
for _ in 0..count {
value = (value << 1) | u64::from(self.bit()?);
}
Ok(value as u32)
}
fn ue(&mut self) -> std::result::Result<u32, Short> {
let mut zeros = 0;
while self.bit()? == 0 {
zeros += 1;
if zeros > 31 {
return Err(Short);
}
}
let rest = self.u(zeros)?;
Ok(((1_u64 << zeros) - 1 + u64::from(rest)) as u32)
}
fn se(&mut self) -> std::result::Result<i32, Short> {
let code = i64::from(self.ue()?);
Ok(if code % 2 == 1 { (code + 1) / 2 } else { -code / 2 } as i32)
}
fn skip_scaling_list(&mut self, size: usize) -> std::result::Result<(), Short> {
let (mut last, mut next) = (8_i32, 8_i32);
for _ in 0..size {
if next != 0 {
next = (last + self.se()? + 256) % 256;
}
if next != 0 {
last = next;
}
}
Ok(())
}
fn skip_hrd(&mut self) -> std::result::Result<(), Short> {
let count = self.ue()? + 1;
self.u(4)?; self.u(4)?; for _ in 0..count.min(32) {
self.ue()?; self.ue()?; self.flag()?; }
self.u(5)?; self.u(5)?; self.u(5)?; self.u(5)?; Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn avcc(nal_length_size: u8) -> Vec<u8> {
let mut record = vec![
1, 0x64, 0x00, 0x28, 0xFC | (nal_length_size - 1), 0xE0 | 1, ];
record.extend_from_slice(&4_u16.to_be_bytes());
record.extend_from_slice(&[0x67, 0x64, 0x00, 0x28]);
record.push(1); record.extend_from_slice(&3_u16.to_be_bytes());
record.extend_from_slice(&[0x68, 0xEE, 0x38]);
record
}
#[test]
fn exp_golomb_reads_both_ways() {
let data = [0b1010_0110, 0b0100_0100, 0b1100_1000];
let mut bits = Bits::new(&data);
assert_eq!(bits.ue().unwrap(), 0);
assert_eq!(bits.ue().unwrap(), 1);
assert_eq!(bits.ue().unwrap(), 2);
assert_eq!(bits.ue().unwrap(), 3);
assert_eq!(bits.se().unwrap(), 1);
assert_eq!(bits.se().unwrap(), -1);
assert_eq!(bits.se().unwrap(), 2);
assert!(bits.u(8).is_err(), "out of bits");
}
#[test]
fn an_annex_b_access_unit_is_taken_apart() {
let stream = [
0, 0, 0, 1, 0x09, 0xF0, 0, 0, 0, 1, 0x67, 0x64, 0x00, 0x29, 0, 0, 1, 0x68, 0xEE, 0, 0, 0, 1, 0x65, 0x88, 0x80, ];
let unit = AccessUnit::split(&stream);
assert_eq!(unit.sps.as_deref(), Some(&[0x67, 0x64, 0x00, 0x29][..]));
assert_eq!(unit.pps.as_deref(), Some(&[0x68, 0xEE][..]));
assert_eq!(unit.data, [0, 0, 0, 3, 0x65, 0x88, 0x80]);
assert!(unit.is_keyframe);
assert!(!AccessUnit::split(&[0, 0, 1, 0x41, 0x9A]).is_keyframe, "a P slice");
}
#[test]
fn emulation_prevention_bytes_are_removed() {
assert_eq!(unescape(&[0, 0, 3, 1, 0, 0, 3, 0, 7]), [0, 0, 1, 0, 0, 0, 7]);
assert_eq!(unescape(&[0, 3, 0, 3]), [0, 3, 0, 3], "one zero is not two");
}
#[test]
fn only_a_sequence_parameter_set_is_parsed_as_one() {
assert!(SequenceParameterSet::parse(&[0x68, 0xEE]).is_err(), "a PPS");
assert!(SequenceParameterSet::parse(&[]).is_err());
assert!(SequenceParameterSet::parse(&[0x67, 0x64]).is_err(), "cut short");
}
#[test]
fn an_avcc_record_gives_up_its_parameter_sets() {
let config = AvcConfig::parse(&avcc(4)).unwrap();
assert_eq!(config.nal_length_size, 4);
assert_eq!(config.sequence_parameter_sets.len(), 1);
assert_eq!(config.picture_parameter_sets.len(), 1);
assert_eq!(config.sequence_parameter_sets[0][0], 0x67);
let annex_b = config.to_annex_b();
assert_eq!(&annex_b[..4], &START_CODE);
assert_eq!(annex_b.len(), 4 + 4 + 4 + 3);
}
#[test]
fn a_two_byte_length_size_is_read_from_the_low_bits() {
let config = AvcConfig::parse(&avcc(2)).unwrap();
assert_eq!(config.nal_length_size, 2);
}
#[test]
fn a_truncated_avcc_is_refused_rather_than_read_past() {
assert!(AvcConfig::parse(&[1, 0x64, 0, 0x28]).is_err());
let mut cut = avcc(4);
cut.truncate(9);
assert!(AvcConfig::parse(&cut).is_err());
}
#[test]
fn a_parameter_set_claiming_more_than_it_has_is_refused() {
let mut record = vec![1, 0x64, 0x00, 0x28, 0xFF, 0xE1];
record.extend_from_slice(&9999_u16.to_be_bytes());
record.extend_from_slice(&[0x67, 0x64]);
let Err(Error::Decode { reason, .. }) = AvcConfig::parse(&record) else {
panic!("that SPS runs off the end");
};
assert!(reason.contains("past the end"), "{reason}");
}
#[test]
fn the_two_bitstream_shapes_round_trip() {
let mut length_prefixed = Vec::new();
for unit in [vec![0x67, 0x42, 0x00], vec![0x68, 0xCE], vec![0x65; 40]] {
length_prefixed.extend_from_slice(&(unit.len() as u32).to_be_bytes());
length_prefixed.extend_from_slice(&unit);
}
let annex_b = length_prefixed_to_annex_b(&length_prefixed, 4).unwrap();
assert_eq!(&annex_b[..4], &START_CODE);
let back = annex_b_to_length_prefixed(&annex_b, 4).unwrap();
assert_eq!(back, length_prefixed);
}
#[test]
fn three_and_four_byte_start_codes_are_both_understood() {
let mut stream = vec![0, 0, 0, 1, 0x67, 0x42];
stream.extend_from_slice(&[0, 0, 1, 0x68, 0xCE, 0x01]);
stream.extend_from_slice(&[0, 0, 0, 1, 0x65, 0xAA]);
let units: Vec<&[u8]> = annex_b_units(&stream).collect();
assert_eq!(units.len(), 3);
assert_eq!(units[0], &[0x67, 0x42]);
assert_eq!(units[1], &[0x68, 0xCE, 0x01]);
assert_eq!(units[2], &[0x65, 0xAA]);
}
#[test]
fn a_length_running_past_the_data_is_refused() {
let hostile = [0x00, 0x00, 0xFF, 0xFF, 0x67, 0x42];
let Err(Error::Decode { reason, .. }) = length_prefixed_to_annex_b(&hostile, 4) else {
panic!("that NAL claims 65535 bytes and has two");
};
assert!(reason.contains("claims"), "{reason}");
}
#[test]
fn a_nal_too_large_for_its_length_field_is_refused() {
let mut stream = vec![0, 0, 0, 1];
stream.extend(std::iter::repeat_n(0x65, 300));
assert!(annex_b_to_length_prefixed(&stream, 1).is_err());
assert!(annex_b_to_length_prefixed(&stream, 2).is_ok());
}
#[test]
fn zero_length_units_are_skipped_rather_than_written_as_bare_start_codes() {
let mut stream = Vec::new();
stream.extend_from_slice(&0_u32.to_be_bytes());
stream.extend_from_slice(&2_u32.to_be_bytes());
stream.extend_from_slice(&[0x67, 0x42]);
let annex_b = length_prefixed_to_annex_b(&stream, 4).unwrap();
assert_eq!(annex_b, vec![0, 0, 0, 1, 0x67, 0x42]);
}
#[test]
fn an_impossible_length_size_is_refused_both_ways() {
assert!(length_prefixed_to_annex_b(&[0; 8], 3).is_err());
assert!(annex_b_to_length_prefixed(&[0, 0, 1, 5], 3).is_err());
}
}