pub mod obu_type {
pub const SEQUENCE_HEADER: u8 = 1;
pub const TEMPORAL_DELIMITER: u8 = 2;
pub const FRAME_HEADER: u8 = 3;
pub const TILE_GROUP: u8 = 4;
pub const METADATA: u8 = 5;
pub const FRAME: u8 = 6;
pub const REDUNDANT_FRAME_HEADER: u8 = 7;
pub const TILE_LIST: u8 = 8;
pub const PADDING: u8 = 15;
}
pub mod frame_type {
pub const KEY_FRAME: u8 = 0;
pub const INTER_FRAME: u8 = 1;
pub const INTRA_ONLY_FRAME: u8 = 2;
pub const SWITCH_FRAME: u8 = 3;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Av1Config {
pub sequence_header: Vec<u8>,
pub seq_profile: u8,
pub seq_level_idx: u8,
pub seq_tier: u8,
pub high_bitdepth: bool,
pub twelve_bit: bool,
pub monochrome: bool,
pub chroma_subsampling_x: bool,
pub chroma_subsampling_y: bool,
pub chroma_sample_position: u8,
}
impl Default for Av1Config {
fn default() -> Self {
Self {
sequence_header: Vec::new(),
seq_profile: 0,
seq_level_idx: 0,
seq_tier: 0,
high_bitdepth: false,
twelve_bit: false,
monochrome: false,
chroma_subsampling_x: true,
chroma_subsampling_y: true,
chroma_sample_position: 0,
}
}
}
#[inline]
pub fn obu_type(header_byte: u8) -> u8 {
(header_byte >> 3) & 0x0f
}
#[inline]
pub fn obu_has_extension(header_byte: u8) -> bool {
(header_byte & 0x04) != 0
}
#[inline]
pub fn obu_has_size(header_byte: u8) -> bool {
(header_byte & 0x02) != 0
}
pub fn read_leb128(data: &[u8]) -> Option<(u64, usize)> {
let mut value: u64 = 0;
let mut shift = 0;
for (i, &byte) in data.iter().take(8).enumerate() {
value |= ((byte & 0x7F) as u64) << shift;
if (byte & 0x80) == 0 {
return Some((value, i + 1));
}
shift += 7;
}
None
}
#[derive(Debug, Clone)]
pub struct ObuInfo {
pub obu_type: u8,
pub has_extension: bool,
pub header_size: usize,
pub payload_size: usize,
pub total_size: usize,
}
pub fn parse_obu_header(data: &[u8]) -> Option<ObuInfo> {
if data.is_empty() {
return None;
}
let header_byte = data[0];
if (header_byte & 0x80) != 0 {
return None;
}
let obu_type_val = obu_type(header_byte);
let has_extension = obu_has_extension(header_byte);
let has_size = obu_has_size(header_byte);
let mut header_size = 1;
if has_extension {
if data.len() < 2 {
return None;
}
header_size = 2;
}
let payload_size = if has_size {
if data.len() <= header_size {
return None;
}
let (size, leb_len) = read_leb128(&data[header_size..])?;
header_size += leb_len;
size as usize
} else {
data.len().saturating_sub(header_size)
};
Some(ObuInfo {
obu_type: obu_type_val,
has_extension,
header_size,
payload_size,
total_size: header_size + payload_size,
})
}
pub struct ObuIter<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> ObuIter<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
}
impl<'a> Iterator for ObuIter<'a> {
type Item = (ObuInfo, &'a [u8]);
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.data.len() {
return None;
}
let remaining = &self.data[self.pos..];
let info = parse_obu_header(remaining)?;
if self.pos + info.total_size > self.data.len() {
return None;
}
let obu_data = &remaining[..info.total_size];
self.pos += info.total_size;
Some((info, obu_data))
}
}
pub fn extract_av1_config(data: &[u8]) -> Option<Av1Config> {
for (info, obu_data) in ObuIter::new(data) {
if info.obu_type == obu_type::SEQUENCE_HEADER {
return parse_sequence_header(obu_data, info.header_size);
}
}
None
}
fn parse_sequence_header(obu_data: &[u8], header_size: usize) -> Option<Av1Config> {
let payload = &obu_data[header_size..];
if payload.is_empty() {
return None;
}
let mut reader = BitReader::new(payload);
let seq_profile = reader.read_bits(3)? as u8;
let _still_picture = reader.read_bit()?;
let reduced_still_picture_header = reader.read_bit()?;
let (seq_level_idx, seq_tier) = if reduced_still_picture_header {
(reader.read_bits(5)? as u8, 0u8)
} else {
let timing_info_present = reader.read_bit()?;
if timing_info_present {
reader.skip_bits(32)?; reader.skip_bits(32)?; let equal_picture_interval = reader.read_bit()?;
if equal_picture_interval {
skip_uvlc(&mut reader)?;
}
}
let decoder_model_info_present = reader.read_bit()?;
let mut buffer_delay_length = 0;
if decoder_model_info_present {
buffer_delay_length = reader.read_bits(5)? as u8 + 1;
reader.skip_bits(32)?; reader.skip_bits(5)?; reader.skip_bits(5)?; }
let initial_display_delay_present = reader.read_bit()?;
let op_cnt = reader.read_bits(5)? as usize + 1;
let mut first_seq_level_idx = 0u8;
let mut first_seq_tier = 0u8;
for i in 0..op_cnt {
reader.skip_bits(12)?; let level_idx = reader.read_bits(5)? as u8;
let tier = if level_idx > 7 {
reader.read_bit()? as u8
} else {
0
};
if i == 0 {
first_seq_level_idx = level_idx;
first_seq_tier = tier;
}
if decoder_model_info_present {
let decoder_model_present = reader.read_bit()?;
if decoder_model_present {
reader.skip_bits(buffer_delay_length as usize)?; reader.skip_bits(buffer_delay_length as usize)?; reader.read_bit()?; }
}
if initial_display_delay_present {
let display_delay_present = reader.read_bit()?;
if display_delay_present {
reader.skip_bits(4)?; }
}
}
(first_seq_level_idx, first_seq_tier)
};
let frame_width_bits = reader.read_bits(4)? as usize + 1;
let frame_height_bits = reader.read_bits(4)? as usize + 1;
let _max_width = reader.read_bits(frame_width_bits)? + 1;
let _max_height = reader.read_bits(frame_height_bits)? + 1;
if !reduced_still_picture_header {
let frame_id_numbers_present = reader.read_bit()?;
if frame_id_numbers_present {
let delta_frame_id_length = reader.read_bits(4)? as usize + 2;
let _frame_id_length = reader.read_bits(3)? as usize + delta_frame_id_length + 1;
}
}
reader.read_bit()?;
reader.read_bit()?;
reader.read_bit()?;
if !reduced_still_picture_header {
reader.read_bit()?;
reader.read_bit()?;
reader.read_bit()?;
reader.read_bit()?;
let enable_order_hint = reader.read_bit()?;
if enable_order_hint {
reader.read_bit()?;
reader.read_bit()?;
}
let seq_choose_screen_content_tools = reader.read_bit()?;
let seq_force_screen_content_tools = if seq_choose_screen_content_tools {
2 } else {
reader.read_bit()? as u8
};
if seq_force_screen_content_tools > 0 {
let seq_choose_integer_mv = reader.read_bit()?;
if !seq_choose_integer_mv {
reader.read_bit()?;
}
}
if enable_order_hint {
reader.skip_bits(3)?;
}
}
reader.read_bit()?;
reader.read_bit()?;
reader.read_bit()?;
let (
high_bitdepth,
twelve_bit,
monochrome,
chroma_subsampling_x,
chroma_subsampling_y,
chroma_sample_position,
) = parse_color_config(&mut reader, seq_profile)?;
reader.read_bit()?;
Some(Av1Config {
sequence_header: obu_data.to_vec(),
seq_profile,
seq_level_idx,
seq_tier,
high_bitdepth,
twelve_bit,
monochrome,
chroma_subsampling_x,
chroma_subsampling_y,
chroma_sample_position,
})
}
fn parse_color_config(
reader: &mut BitReader,
seq_profile: u8,
) -> Option<(bool, bool, bool, bool, bool, u8)> {
let high_bitdepth = reader.read_bit()?;
let twelve_bit = if seq_profile == 2 && high_bitdepth {
reader.read_bit()?
} else {
false
};
let bit_depth = if seq_profile == 2 && twelve_bit {
12
} else if high_bitdepth {
10
} else {
8
};
let monochrome = if seq_profile == 1 {
false
} else {
reader.read_bit()?
};
let color_description_present = reader.read_bit()?;
let (color_primaries, transfer_characteristics, matrix_coefficients) =
if color_description_present {
let cp = reader.read_bits(8)? as u8;
let tc = reader.read_bits(8)? as u8;
let mc = reader.read_bits(8)? as u8;
(cp, tc, mc)
} else {
(2, 2, 2) };
let (chroma_subsampling_x, chroma_subsampling_y, _chroma_sample_position) = if monochrome {
reader.read_bit()?;
(true, true, 0)
} else if color_primaries == 1 && transfer_characteristics == 13 && matrix_coefficients == 0 {
(false, false, 0)
} else {
reader.read_bit()?;
if seq_profile == 0 {
(true, true, 0)
} else if seq_profile == 1 {
(false, false, 0)
} else if bit_depth == 12 {
let subsampling_x = reader.read_bit()?;
let subsampling_y = if subsampling_x {
reader.read_bit()?
} else {
false
};
(subsampling_x, subsampling_y, 0)
} else {
(true, false, 0)
}
};
let chroma_sample_position = if chroma_subsampling_x && chroma_subsampling_y {
reader.read_bits(2)? as u8
} else {
0
};
if !monochrome {
reader.read_bit()?;
}
Some((
high_bitdepth,
twelve_bit,
monochrome,
chroma_subsampling_x,
chroma_subsampling_y,
chroma_sample_position,
))
}
fn skip_uvlc(reader: &mut BitReader) -> Option<()> {
let mut leading_zeros = 0;
while !reader.read_bit()? {
leading_zeros += 1;
if leading_zeros > 32 {
return None;
}
}
if leading_zeros > 0 {
reader.skip_bits(leading_zeros)?;
}
Some(())
}
struct BitReader<'a> {
data: &'a [u8],
byte_pos: usize,
bit_pos: usize,
}
impl<'a> BitReader<'a> {
fn new(data: &'a [u8]) -> Self {
Self {
data,
byte_pos: 0,
bit_pos: 0,
}
}
fn read_bit(&mut self) -> Option<bool> {
if self.byte_pos >= self.data.len() {
return None;
}
let bit = (self.data[self.byte_pos] >> (7 - self.bit_pos)) & 1;
self.bit_pos += 1;
if self.bit_pos == 8 {
self.bit_pos = 0;
self.byte_pos += 1;
}
Some(bit != 0)
}
fn read_bits(&mut self, count: usize) -> Option<u64> {
if count > 64 {
return None;
}
let mut value = 0u64;
for _ in 0..count {
value = (value << 1) | (self.read_bit()? as u64);
}
Some(value)
}
fn skip_bits(&mut self, count: usize) -> Option<()> {
for _ in 0..count {
self.read_bit()?;
}
Some(())
}
}
pub fn is_av1_keyframe(data: &[u8]) -> bool {
for (info, obu_data) in ObuIter::new(data) {
if info.obu_type == obu_type::FRAME || info.obu_type == obu_type::FRAME_HEADER {
let payload = &obu_data[info.header_size..];
if !payload.is_empty() {
let mut reader = BitReader::new(payload);
if let Some(show_existing) = reader.read_bit() {
if show_existing {
continue;
}
if let Some(frame_type_val) = reader.read_bits(2) {
if frame_type_val as u8 == frame_type::KEY_FRAME {
return true;
}
}
}
}
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
fn bits_to_bytes(bits: &str) -> Vec<u8> {
let mut out = Vec::new();
let mut acc = 0u8;
let mut n = 0;
for ch in bits.chars() {
if ch != '0' && ch != '1' {
continue;
}
acc <<= 1;
if ch == '1' {
acc |= 1;
}
n += 1;
if n == 8 {
out.push(acc);
acc = 0;
n = 0;
}
}
if n != 0 {
acc <<= 8 - n;
out.push(acc);
}
out
}
#[test]
fn test_obu_type_extraction() {
let seq_header = 0x08;
assert_eq!(obu_type(seq_header), 1);
let frame = 0x30;
assert_eq!(obu_type(frame), 6);
}
#[test]
fn test_obu_flags() {
let with_ext = 0x04;
assert!(obu_has_extension(with_ext));
assert!(!obu_has_extension(0x00));
let with_size = 0x02;
assert!(obu_has_size(with_size));
assert!(!obu_has_size(0x00));
}
#[test]
fn test_read_leb128() {
assert_eq!(read_leb128(&[0x00]), Some((0, 1)));
assert_eq!(read_leb128(&[0x7F]), Some((127, 1)));
assert_eq!(read_leb128(&[0x80, 0x01]), Some((128, 2)));
assert_eq!(read_leb128(&[0xFF, 0x01]), Some((255, 2)));
}
#[test]
fn test_parse_obu_header() {
let data = [0x0A, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00];
let info = parse_obu_header(&data).unwrap();
assert_eq!(info.obu_type, 1);
assert!(!info.has_extension);
assert_eq!(info.header_size, 2); assert_eq!(info.payload_size, 5);
assert_eq!(info.total_size, 7);
}
#[test]
fn test_obu_iterator() {
let data = [0x12, 0x00, 0x0A, 0x03, 0xAA, 0xBB, 0xCC];
let obus: Vec<_> = ObuIter::new(&data).collect();
assert_eq!(obus.len(), 2);
assert_eq!(obus[0].0.obu_type, 2); assert_eq!(obus[1].0.obu_type, 1); }
#[test]
fn test_is_av1_keyframe() {
let keyframe = [0x32, 0x02, 0x00, 0x00];
assert!(is_av1_keyframe(&keyframe));
let interframe = [0x32, 0x02, 0x20, 0x00];
assert!(!is_av1_keyframe(&interframe));
}
#[test]
fn test_is_av1_keyframe_skips_show_existing_frame() {
let show_existing = [0x1A, 0x02, 0x80, 0x00];
let keyframe = [0x1A, 0x02, 0x00, 0x00];
let mut stream = Vec::new();
stream.extend_from_slice(&show_existing);
stream.extend_from_slice(&keyframe);
assert!(is_av1_keyframe(&stream));
}
#[test]
fn test_av1_config_default_values() {
let cfg = Av1Config::default();
assert!(cfg.sequence_header.is_empty());
assert_eq!(cfg.seq_profile, 0);
assert_eq!(cfg.seq_level_idx, 0);
assert_eq!(cfg.seq_tier, 0);
assert!(!cfg.high_bitdepth);
assert!(!cfg.twelve_bit);
assert!(!cfg.monochrome);
assert!(cfg.chroma_subsampling_x);
assert!(cfg.chroma_subsampling_y);
assert_eq!(cfg.chroma_sample_position, 0);
}
#[test]
fn test_read_leb128_rejects_overlong_encoding() {
let bytes = [0x80u8; 8];
assert!(read_leb128(&bytes).is_none());
}
#[test]
fn test_parse_obu_header_rejects_forbidden_bit() {
assert!(parse_obu_header(&[0x80]).is_none());
}
#[test]
fn test_parse_obu_header_extension_requires_second_byte() {
assert!(parse_obu_header(&[0x0C]).is_none());
}
#[test]
fn test_parse_obu_header_size_requires_bytes() {
assert!(parse_obu_header(&[0x0A]).is_none());
let info = parse_obu_header(&[0x0A, 0x01]).expect("OBU header should parse");
assert_eq!(info.header_size, 2);
assert_eq!(info.payload_size, 1);
assert_eq!(info.total_size, 3);
}
#[test]
fn test_bit_reader_read_bits_over_64_returns_none() {
let data = [0u8; 16];
let mut reader = BitReader::new(&data);
assert!(reader.read_bits(65).is_none());
}
#[test]
fn test_skip_uvlc_rejects_too_many_leading_zeros() {
let data = [0u8; 8]; let mut reader = BitReader::new(&data);
assert!(skip_uvlc(&mut reader).is_none());
}
#[test]
fn test_parse_color_config_monochrome_twelve_bit_path() {
let bytes = bits_to_bytes("1 1 1 0 1 01");
let mut reader = BitReader::new(&bytes);
let parsed = parse_color_config(&mut reader, 2).unwrap();
assert!(parsed.0); assert!(parsed.1); assert!(parsed.2); assert!(parsed.3); assert!(parsed.4); assert_eq!(parsed.5, 1); }
#[test]
fn test_parse_color_config_srgb_path() {
let bytes = bits_to_bytes("0 0 1 00000001 00001101 00000000 0");
let mut reader = BitReader::new(&bytes);
let parsed = parse_color_config(&mut reader, 0).unwrap();
assert!(!parsed.2); assert!(!parsed.3); assert!(!parsed.4); assert_eq!(parsed.5, 0); }
#[test]
fn test_extract_av1_config_empty() {
assert!(extract_av1_config(&[]).is_none());
}
#[test]
fn test_bit_reader() {
let data = [0b10110100, 0b01100011];
let mut reader = BitReader::new(&data);
assert_eq!(reader.read_bits(4), Some(11));
assert_eq!(reader.read_bits(4), Some(4));
assert_eq!(reader.read_bits(2), Some(1));
}
#[test]
fn test_parse_sequence_header_basic() {
let seq_header = [
0x0A, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
let config = parse_sequence_header(&seq_header[2..], 2);
assert!(config.is_some());
let cfg = config.unwrap();
assert_eq!(cfg.seq_profile, 0);
assert_eq!(cfg.seq_level_idx, 0);
assert_eq!(cfg.seq_tier, 0);
assert!(!cfg.high_bitdepth);
assert!(!cfg.twelve_bit);
assert!(!cfg.monochrome);
}
}