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,
})
}
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));
}
}