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
68fn leb128_encode(mut v: u64, out: &mut Vec<u8>) {
70 loop {
71 let mut byte = (v & 0x7f) as u8;
72 v >>= 7;
73 if v != 0 {
74 byte |= 0x80;
75 }
76 out.push(byte);
77 if v == 0 {
78 break;
79 }
80 }
81}
82
83pub fn av1c_config_record(data: &[u8]) -> Option<(VideoParams, Vec<u8>)> {
92 let seq_payload = iter_obus(data)
93 .find(|o| o.obu_type == OBU_SEQUENCE_HEADER)?
94 .payload;
95 let params = parse_sequence_header(seq_payload)?;
96
97 let mut rec = Vec::with_capacity(4 + 2 + seq_payload.len());
98 rec.push(0x81); rec.push(((params.profile & 0x07) << 5) | (params.level & 0x1f)); rec.push(((params.tier & 1) << 7) | 0x0C);
103 rec.push(0x00); rec.push(0x0A); leb128_encode(seq_payload.len() as u64, &mut rec);
107 rec.extend_from_slice(seq_payload);
108 Some((params, rec))
109}
110
111pub struct Av1;
113
114impl CodecParser for Av1 {
115 const CODEC: CodecId = CodecId::AV1;
116
117 fn parse_config(data: &[u8]) -> Option<VideoParams> {
118 iter_obus(data)
119 .find(|o| o.obu_type == OBU_SEQUENCE_HEADER)
120 .and_then(|o| parse_sequence_header(o.payload))
121 }
122
123 fn is_random_access_point(data: &[u8]) -> bool {
124 iter_obus(data).any(|o| o.obu_type == OBU_SEQUENCE_HEADER)
127 }
128
129 fn carries_config(data: &[u8]) -> bool {
130 Self::is_random_access_point(data)
131 }
132
133 fn hls_codec_string(p: &VideoParams) -> String {
134 format!("av01.{}.{:02}M.{:02}", p.profile, p.level, p.bit_depth)
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::codec::testutil::BitWriter;
143
144 fn seq_header_obu_1920x1080() -> Vec<u8> {
146 let mut w = BitWriter::default();
147 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();
160 let payload = w.bytes();
161
162 let mut obu = vec![0x0A]; obu.push(payload.len() as u8); obu.extend_from_slice(&payload);
165 obu
166 }
167
168 #[test]
169 fn parse_sequence_header_extracts_resolution() {
170 let p = parse_sequence_header(&seq_header_obu_1920x1080()[2..]).expect("parse");
171 assert_eq!((p.width, p.height), (1920, 1080));
172 assert_eq!((p.profile, p.level), (0, 1));
173 }
174
175 #[test]
176 fn classifies_and_extracts_via_obus() {
177 let mut tu = vec![0x12, 0x00]; tu.extend_from_slice(&seq_header_obu_1920x1080());
180 tu.extend_from_slice(&[0x32, 0x02, 0xAA, 0xBB]); assert!(Av1::is_random_access_point(&tu));
183 assert!(Av1::carries_config(&tu));
184 let p = Av1::parse_config(&tu).expect("params");
185 assert_eq!((p.width, p.height), (1920, 1080));
186 assert_eq!(Av1::hls_codec_string(&p), "av01.0.01M.08");
187
188 let no_seq = [0x12, 0x00, 0x32, 0x02, 0xAA, 0xBB];
190 assert!(!Av1::is_random_access_point(&no_seq));
191 }
192
193 #[test]
194 fn av1c_record_carries_profile_level_and_seq_header() {
195 let mut tu = vec![0x12, 0x00]; let seq = seq_header_obu_1920x1080();
197 tu.extend_from_slice(&seq);
198 let (params, rec) = av1c_config_record(&tu).expect("record");
199 assert_eq!((params.width, params.height), (1920, 1080));
200 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..])); }
207}