use super::bitreader::BitReader;
use super::obu::iter_obus;
use super::parser::{CodecParser, VideoParams};
use crate::CodecId;
pub const OBU_SEQUENCE_HEADER: u8 = 1;
fn parse_sequence_header(payload: &[u8]) -> Option<VideoParams> {
let mut r = BitReader::new(payload);
let seq_profile = r.read_bits(3)? as u8;
let _still_picture = r.read_bit()?;
let reduced_still_picture_header = r.read_bit()?;
let level;
if reduced_still_picture_header == 1 {
level = r.read_bits(5)? as u8; } else {
let timing_info_present_flag = r.read_bit()?;
if timing_info_present_flag == 1 {
return None; }
let initial_display_delay_present_flag = r.read_bit()?;
let operating_points_cnt_minus1 = r.read_bits(5)?;
let mut level0 = 0u8;
for i in 0..=operating_points_cnt_minus1 {
let _operating_point_idc = r.read_bits(12)?;
let seq_level_idx = r.read_bits(5)?;
if seq_level_idx > 7 {
let _seq_tier = r.read_bit()?;
}
if initial_display_delay_present_flag == 1 {
let present = r.read_bit()?;
if present == 1 {
let _initial_display_delay_minus_1 = r.read_bits(4)?;
}
}
if i == 0 {
level0 = seq_level_idx as u8;
}
}
level = level0;
}
let frame_width_bits_minus_1 = r.read_bits(4)?;
let frame_height_bits_minus_1 = r.read_bits(4)?;
let max_frame_width_minus_1 = r.read_bits(frame_width_bits_minus_1 + 1)?;
let max_frame_height_minus_1 = r.read_bits(frame_height_bits_minus_1 + 1)?;
Some(VideoParams {
width: max_frame_width_minus_1 + 1,
height: max_frame_height_minus_1 + 1,
profile: seq_profile,
level,
tier: 0,
bit_depth: 8,
})
}
fn leb128_encode(mut v: u64, out: &mut Vec<u8>) {
loop {
let mut byte = (v & 0x7f) as u8;
v >>= 7;
if v != 0 {
byte |= 0x80;
}
out.push(byte);
if v == 0 {
break;
}
}
}
pub fn av1c_config_record(data: &[u8]) -> Option<(VideoParams, Vec<u8>)> {
let seq_payload = iter_obus(data)
.find(|o| o.obu_type == OBU_SEQUENCE_HEADER)?
.payload;
let params = parse_sequence_header(seq_payload)?;
let mut rec = Vec::with_capacity(4 + 2 + seq_payload.len());
rec.push(0x81); rec.push(((params.profile & 0x07) << 5) | (params.level & 0x1f)); rec.push(((params.tier & 1) << 7) | 0x0C);
rec.push(0x00); rec.push(0x0A); leb128_encode(seq_payload.len() as u64, &mut rec);
rec.extend_from_slice(seq_payload);
Some((params, rec))
}
pub struct Av1;
impl CodecParser for Av1 {
const CODEC: CodecId = CodecId::AV1;
fn parse_config(data: &[u8]) -> Option<VideoParams> {
iter_obus(data)
.find(|o| o.obu_type == OBU_SEQUENCE_HEADER)
.and_then(|o| parse_sequence_header(o.payload))
}
fn is_random_access_point(data: &[u8]) -> bool {
iter_obus(data).any(|o| o.obu_type == OBU_SEQUENCE_HEADER)
}
fn carries_config(data: &[u8]) -> bool {
Self::is_random_access_point(data)
}
fn hls_codec_string(p: &VideoParams) -> String {
format!("av01.{}.{:02}M.{:02}", p.profile, p.level, p.bit_depth)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::testutil::BitWriter;
fn seq_header_obu_1920x1080() -> Vec<u8> {
let mut w = BitWriter::default();
w.bits(0, 3); w.bit(0); w.bit(0); w.bit(0); w.bit(0); w.bits(0, 5); w.bits(0, 12); w.bits(1, 5); w.bits(11, 4); w.bits(11, 4); w.bits(1919, 12); w.bits(1079, 12); w.align();
let payload = w.bytes();
let mut obu = vec![0x0A]; obu.push(payload.len() as u8); obu.extend_from_slice(&payload);
obu
}
#[test]
fn parse_sequence_header_extracts_resolution() {
let p = parse_sequence_header(&seq_header_obu_1920x1080()[2..]).expect("parse");
assert_eq!((p.width, p.height), (1920, 1080));
assert_eq!((p.profile, p.level), (0, 1));
}
#[test]
fn classifies_and_extracts_via_obus() {
let mut tu = vec![0x12, 0x00]; tu.extend_from_slice(&seq_header_obu_1920x1080());
tu.extend_from_slice(&[0x32, 0x02, 0xAA, 0xBB]);
assert!(Av1::is_random_access_point(&tu));
assert!(Av1::carries_config(&tu));
let p = Av1::parse_config(&tu).expect("params");
assert_eq!((p.width, p.height), (1920, 1080));
assert_eq!(Av1::hls_codec_string(&p), "av01.0.01M.08");
let no_seq = [0x12, 0x00, 0x32, 0x02, 0xAA, 0xBB];
assert!(!Av1::is_random_access_point(&no_seq));
}
#[test]
fn av1c_record_carries_profile_level_and_seq_header() {
let mut tu = vec![0x12, 0x00]; let seq = seq_header_obu_1920x1080();
tu.extend_from_slice(&seq);
let (params, rec) = av1c_config_record(&tu).expect("record");
assert_eq!((params.width, params.height), (1920, 1080));
assert_eq!(rec[0], 0x81); assert_eq!(rec[1], 0x01); assert_eq!(rec[2], 0x0C); assert_eq!(rec[4], 0x0A); assert!(rec.ends_with(&seq[2..])); }
}