arcly_stream/codec/
vp9.rs1use super::bitreader::BitReader;
14use super::parser::{CodecParser, VideoParams};
15use crate::CodecId;
16
17const CS_RGB: u32 = 7;
18const VP9_SYNC_CODE: u32 = 0x498342;
19
20fn parse_uncompressed_header(data: &[u8]) -> Option<(VideoParams, bool)> {
23 let mut r = BitReader::new(data);
24 if r.read_bits(2)? != 2 {
25 return None; }
27 let profile_low = r.read_bit()?;
28 let profile_high = r.read_bit()?;
29 let profile = ((profile_high << 1) | profile_low) as u8;
30 if profile == 3 {
31 let _reserved = r.read_bit()?;
32 }
33 if r.read_bit()? == 1 {
34 return Some((VideoParams::default(), false)); }
36 let frame_type = r.read_bit()?; let _show_frame = r.read_bit()?;
38 let _error_resilient = r.read_bit()?;
39 if frame_type != 0 {
40 return Some((
41 VideoParams {
42 profile,
43 ..VideoParams::default()
44 },
45 false,
46 ));
47 }
48
49 if r.read_bits(24)? != VP9_SYNC_CODE {
51 return None;
52 }
53 let mut bit_depth = 8u8;
54 if profile >= 2 {
55 bit_depth = if r.read_bit()? == 1 { 12 } else { 10 };
56 }
57 let color_space = r.read_bits(3)?;
58 if color_space != CS_RGB {
59 let _color_range = r.read_bit()?;
60 if profile == 1 || profile == 3 {
61 let _subsampling_x = r.read_bit()?;
62 let _subsampling_y = r.read_bit()?;
63 let _reserved = r.read_bit()?;
64 }
65 } else if profile == 1 || profile == 3 {
66 let _reserved = r.read_bit()?;
67 }
68
69 let width = r.read_bits(16)? + 1;
70 let height = r.read_bits(16)? + 1;
71 Some((
72 VideoParams {
73 width,
74 height,
75 profile,
76 level: 0,
77 tier: 0,
78 bit_depth,
79 },
80 true,
81 ))
82}
83
84pub struct Vp9;
86
87impl CodecParser for Vp9 {
88 const CODEC: CodecId = CodecId::VP9;
89
90 fn parse_config(data: &[u8]) -> Option<VideoParams> {
91 match parse_uncompressed_header(data) {
92 Some((params, true)) => Some(params),
93 _ => None,
94 }
95 }
96
97 fn is_random_access_point(data: &[u8]) -> bool {
98 matches!(parse_uncompressed_header(data), Some((_, true)))
99 }
100
101 fn carries_config(data: &[u8]) -> bool {
102 Self::is_random_access_point(data)
104 }
105
106 fn hls_codec_string(p: &VideoParams) -> String {
107 let level = if p.level == 0 { 10 } else { p.level };
109 format!("vp09.{:02}.{:02}.{:02}", p.profile, level, p.bit_depth)
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::codec::testutil::BitWriter;
117
118 fn keyframe_1920x1080() -> Vec<u8> {
119 let mut w = BitWriter::default();
120 w.bits(2, 2); w.bit(0); w.bit(0); w.bit(0); w.bit(0); w.bit(1); w.bit(0); w.bits(VP9_SYNC_CODE, 24); w.bits(1, 3); w.bit(0); w.bits(1919, 16); w.bits(1079, 16); w.bytes()
133 }
134
135 #[test]
136 fn parse_keyframe_extracts_resolution() {
137 let (p, is_key) = parse_uncompressed_header(&keyframe_1920x1080()).expect("parse");
138 assert!(is_key);
139 assert_eq!(
140 (p.width, p.height, p.profile, p.bit_depth),
141 (1920, 1080, 0, 8)
142 );
143 }
144
145 #[test]
146 fn classifies_keyframe_and_codec_string() {
147 let kf = keyframe_1920x1080();
148 assert!(Vp9::is_random_access_point(&kf));
149 assert!(Vp9::carries_config(&kf));
150 let p = Vp9::parse_config(&kf).expect("params");
151 assert_eq!(Vp9::hls_codec_string(&p), "vp09.00.10.08");
152
153 let mut w = BitWriter::default();
155 w.bits(2, 2); w.bit(0);
157 w.bit(0); w.bit(0); w.bit(1); let inter = w.bytes();
161 assert!(!Vp9::is_random_access_point(&inter));
162 }
163}