use super::bitreader::BitReader;
use super::parser::{CodecParser, VideoParams};
use crate::CodecId;
const CS_RGB: u32 = 7;
const VP9_SYNC_CODE: u32 = 0x498342;
fn parse_uncompressed_header(data: &[u8]) -> Option<(VideoParams, bool)> {
let mut r = BitReader::new(data);
if r.read_bits(2)? != 2 {
return None; }
let profile_low = r.read_bit()?;
let profile_high = r.read_bit()?;
let profile = ((profile_high << 1) | profile_low) as u8;
if profile == 3 {
let _reserved = r.read_bit()?;
}
if r.read_bit()? == 1 {
return Some((VideoParams::default(), false)); }
let frame_type = r.read_bit()?; let _show_frame = r.read_bit()?;
let _error_resilient = r.read_bit()?;
if frame_type != 0 {
return Some((
VideoParams {
profile,
..VideoParams::default()
},
false,
));
}
if r.read_bits(24)? != VP9_SYNC_CODE {
return None;
}
let mut bit_depth = 8u8;
if profile >= 2 {
bit_depth = if r.read_bit()? == 1 { 12 } else { 10 };
}
let color_space = r.read_bits(3)?;
if color_space != CS_RGB {
let _color_range = r.read_bit()?;
if profile == 1 || profile == 3 {
let _subsampling_x = r.read_bit()?;
let _subsampling_y = r.read_bit()?;
let _reserved = r.read_bit()?;
}
} else if profile == 1 || profile == 3 {
let _reserved = r.read_bit()?;
}
let width = r.read_bits(16)? + 1;
let height = r.read_bits(16)? + 1;
Some((
VideoParams {
width,
height,
profile,
level: 0,
tier: 0,
bit_depth,
},
true,
))
}
pub struct Vp9;
impl CodecParser for Vp9 {
const CODEC: CodecId = CodecId::VP9;
fn parse_config(data: &[u8]) -> Option<VideoParams> {
match parse_uncompressed_header(data) {
Some((params, true)) => Some(params),
_ => None,
}
}
fn is_random_access_point(data: &[u8]) -> bool {
matches!(parse_uncompressed_header(data), Some((_, true)))
}
fn carries_config(data: &[u8]) -> bool {
Self::is_random_access_point(data)
}
fn hls_codec_string(p: &VideoParams) -> String {
let level = if p.level == 0 { 10 } else { p.level };
format!("vp09.{:02}.{:02}.{:02}", p.profile, level, p.bit_depth)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::testutil::BitWriter;
fn keyframe_1920x1080() -> Vec<u8> {
let mut w = BitWriter::default();
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()
}
#[test]
fn parse_keyframe_extracts_resolution() {
let (p, is_key) = parse_uncompressed_header(&keyframe_1920x1080()).expect("parse");
assert!(is_key);
assert_eq!(
(p.width, p.height, p.profile, p.bit_depth),
(1920, 1080, 0, 8)
);
}
#[test]
fn classifies_keyframe_and_codec_string() {
let kf = keyframe_1920x1080();
assert!(Vp9::is_random_access_point(&kf));
assert!(Vp9::carries_config(&kf));
let p = Vp9::parse_config(&kf).expect("params");
assert_eq!(Vp9::hls_codec_string(&p), "vp09.00.10.08");
let mut w = BitWriter::default();
w.bits(2, 2); w.bit(0);
w.bit(0); w.bit(0); w.bit(1); let inter = w.bytes();
assert!(!Vp9::is_random_access_point(&inter));
}
}