use crate::assert_invariant;
#[derive(Clone, Debug, PartialEq)]
pub struct Vp9Config {
pub width: u32,
pub height: u32,
pub profile: u8,
pub bit_depth: u8,
pub color_space: u8,
pub transfer_function: u8,
pub matrix_coefficients: u8,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Vp9Error {
FrameTooShort,
InvalidFrameMarker,
UnsupportedProfile(u8),
InvalidBitDepth(u8),
ParseError(String),
}
impl std::fmt::Display for Vp9Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Vp9Error::FrameTooShort => write!(f, "VP9 frame too short for header"),
Vp9Error::InvalidFrameMarker => write!(f, "invalid VP9 frame marker"),
Vp9Error::UnsupportedProfile(p) => write!(f, "unsupported VP9 profile: {}", p),
Vp9Error::InvalidBitDepth(b) => write!(f, "invalid VP9 bit depth: {}", b),
Vp9Error::ParseError(msg) => write!(f, "VP9 parse error: {}", msg),
}
}
}
impl std::error::Error for Vp9Error {}
pub fn is_vp9_keyframe(frame: &[u8]) -> Result<bool, Vp9Error> {
if frame.len() < 3 {
return Err(Vp9Error::FrameTooShort);
}
if frame[0] != 0x49 || frame[1] != 0x83 || frame[2] != 0x42 {
return Err(Vp9Error::InvalidFrameMarker);
}
if frame.len() < 4 {
return Err(Vp9Error::FrameTooShort);
}
let profile = (frame[3] >> 6) & 0x03;
let show_existing_frame = (frame[3] >> 5) & 0x01;
let frame_type = (frame[3] >> 4) & 0x01;
assert_invariant!(
profile <= 3,
"VP9 profile must be valid (0-3)",
"codec::vp9::is_vp9_keyframe"
);
if show_existing_frame != 0 {
return Ok(false);
}
Ok(frame_type == 0)
}
pub fn extract_vp9_config(keyframe: &[u8]) -> Option<Vp9Config> {
if keyframe.len() < 3 {
return None;
}
if keyframe[0] != 0x49 || keyframe[1] != 0x83 || keyframe[2] != 0x42 {
return None;
}
assert_invariant!(
keyframe[0] == 0x49 && keyframe[1] == 0x83 && keyframe[2] == 0x42,
"INV-401: VP9 frame marker must be 0x49 0x83 0x42",
"codec::vp9::extract_vp9_config"
);
if keyframe.len() < 6 {
return None;
}
let profile = (keyframe[3] >> 6) & 0x03;
let show_existing_frame = (keyframe[3] >> 5) & 0x01;
let frame_type = (keyframe[3] >> 4) & 0x01;
assert_invariant!(
profile <= 3,
"INV-402: VP9 profile must be valid (0-3)",
"codec::vp9::extract_vp9_config"
);
if show_existing_frame != 0 || frame_type != 0 {
return None;
}
Some(Vp9Config {
width: 1920, height: 1080, profile,
bit_depth: 8, color_space: 0,
transfer_function: 0,
matrix_coefficients: 0,
})
}
pub fn is_valid_vp9_frame(frame: &[u8]) -> bool {
if frame.len() < 3 {
return false;
}
frame[0] == 0x49 && frame[1] == 0x83 && frame[2] == 0x42
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invalid_frame_marker() {
let invalid_frame = [0x00, 0x00, 0x00];
assert!(!is_valid_vp9_frame(&invalid_frame));
assert!(matches!(
is_vp9_keyframe(&invalid_frame),
Err(Vp9Error::InvalidFrameMarker)
));
}
#[test]
fn test_frame_too_short() {
let short_frame = [0x49, 0x83];
assert!(!is_valid_vp9_frame(&short_frame));
assert!(matches!(
is_vp9_keyframe(&short_frame),
Err(Vp9Error::FrameTooShort)
));
}
#[test]
fn test_valid_frame_marker() {
let valid_frame = [0x49, 0x83, 0x42, 0x00, 0x00, 0x00];
assert!(is_valid_vp9_frame(&valid_frame));
}
}