arcly_stream/codec/
av1.rs1use super::bitreader::BitReader;
5use super::obu::iter_obus;
6use super::parser::{CodecParser, VideoParams};
7use crate::CodecId;
8
9pub const OBU_SEQUENCE_HEADER: u8 = 1;
11
12fn parse_sequence_header(payload: &[u8]) -> Option<VideoParams> {
18 let mut r = BitReader::new(payload);
19 let seq_profile = r.read_bits(3)? as u8;
20 let _still_picture = r.read_bit()?;
21 let reduced_still_picture_header = r.read_bit()?;
22
23 let level;
24 if reduced_still_picture_header == 1 {
25 level = r.read_bits(5)? as u8; } else {
27 let timing_info_present_flag = r.read_bit()?;
28 if timing_info_present_flag == 1 {
29 return None; }
31 let initial_display_delay_present_flag = r.read_bit()?;
32 let operating_points_cnt_minus1 = r.read_bits(5)?;
33 let mut level0 = 0u8;
34 for i in 0..=operating_points_cnt_minus1 {
35 let _operating_point_idc = r.read_bits(12)?;
36 let seq_level_idx = r.read_bits(5)?;
37 if seq_level_idx > 7 {
38 let _seq_tier = r.read_bit()?;
39 }
40 if initial_display_delay_present_flag == 1 {
41 let present = r.read_bit()?;
42 if present == 1 {
43 let _initial_display_delay_minus_1 = r.read_bits(4)?;
44 }
45 }
46 if i == 0 {
47 level0 = seq_level_idx as u8;
48 }
49 }
50 level = level0;
51 }
52
53 let frame_width_bits_minus_1 = r.read_bits(4)?;
54 let frame_height_bits_minus_1 = r.read_bits(4)?;
55 let max_frame_width_minus_1 = r.read_bits(frame_width_bits_minus_1 + 1)?;
56 let max_frame_height_minus_1 = r.read_bits(frame_height_bits_minus_1 + 1)?;
57
58 Some(VideoParams {
59 width: max_frame_width_minus_1 + 1,
60 height: max_frame_height_minus_1 + 1,
61 profile: seq_profile,
62 level,
63 tier: 0,
64 bit_depth: 8,
65 })
66}
67
68pub struct Av1;
70
71impl CodecParser for Av1 {
72 const CODEC: CodecId = CodecId::AV1;
73
74 fn parse_config(data: &[u8]) -> Option<VideoParams> {
75 iter_obus(data)
76 .find(|o| o.obu_type == OBU_SEQUENCE_HEADER)
77 .and_then(|o| parse_sequence_header(o.payload))
78 }
79
80 fn is_random_access_point(data: &[u8]) -> bool {
81 iter_obus(data).any(|o| o.obu_type == OBU_SEQUENCE_HEADER)
84 }
85
86 fn carries_config(data: &[u8]) -> bool {
87 Self::is_random_access_point(data)
88 }
89
90 fn hls_codec_string(p: &VideoParams) -> String {
91 format!("av01.{}.{:02}M.{:02}", p.profile, p.level, p.bit_depth)
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::codec::testutil::BitWriter;
100
101 fn seq_header_obu_1920x1080() -> Vec<u8> {
103 let mut w = BitWriter::default();
104 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();
117 let payload = w.bytes();
118
119 let mut obu = vec![0x0A]; obu.push(payload.len() as u8); obu.extend_from_slice(&payload);
122 obu
123 }
124
125 #[test]
126 fn parse_sequence_header_extracts_resolution() {
127 let p = parse_sequence_header(&seq_header_obu_1920x1080()[2..]).expect("parse");
128 assert_eq!((p.width, p.height), (1920, 1080));
129 assert_eq!((p.profile, p.level), (0, 1));
130 }
131
132 #[test]
133 fn classifies_and_extracts_via_obus() {
134 let mut tu = vec![0x12, 0x00]; tu.extend_from_slice(&seq_header_obu_1920x1080());
137 tu.extend_from_slice(&[0x32, 0x02, 0xAA, 0xBB]); assert!(Av1::is_random_access_point(&tu));
140 assert!(Av1::carries_config(&tu));
141 let p = Av1::parse_config(&tu).expect("params");
142 assert_eq!((p.width, p.height), (1920, 1080));
143 assert_eq!(Av1::hls_codec_string(&p), "av01.0.01M.08");
144
145 let no_seq = [0x12, 0x00, 0x32, 0x02, 0xAA, 0xBB];
147 assert!(!Av1::is_random_access_point(&no_seq));
148 }
149}