mod import;
pub use import::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct FrameHeader {
pub keyframe: bool,
pub dimensions: Option<(u16, u16)>,
}
impl FrameHeader {
pub fn parse(data: &[u8]) -> anyhow::Result<Self> {
anyhow::ensure!(data.len() >= 3, "VP8 frame too short for tag");
let tag = u32::from(data[0]) | (u32::from(data[1]) << 8) | (u32::from(data[2]) << 16);
let keyframe = tag & 0x1 == 0;
if !keyframe {
return Ok(Self {
keyframe: false,
dimensions: None,
});
}
anyhow::ensure!(data.len() >= 10, "VP8 key frame too short for header");
anyhow::ensure!(
data[3] == 0x9d && data[4] == 0x01 && data[5] == 0x2a,
"VP8 key frame start code mismatch"
);
let width = (u16::from(data[6]) | (u16::from(data[7]) << 8)) & 0x3fff;
let height = (u16::from(data[8]) | (u16::from(data[9]) << 8)) & 0x3fff;
Ok(Self {
keyframe: true,
dimensions: Some((width, height)),
})
}
}
pub(crate) fn vpcc() -> mp4_atom::VpcC {
mp4_atom::VpcC {
profile: 0,
level: 0,
bit_depth: 8,
..Default::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_keyframe_dimensions() {
let frame = [0x10, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0x40, 0x01, 0xf0, 0x00];
let header = FrameHeader::parse(&frame).expect("parse key frame");
assert!(header.keyframe);
assert_eq!(header.dimensions, Some((320, 240)));
}
#[test]
fn parses_interframe() {
let frame = [0x31, 0x00, 0x00];
let header = FrameHeader::parse(&frame).expect("parse interframe");
assert!(!header.keyframe);
assert_eq!(header.dimensions, None);
}
#[test]
fn rejects_bad_start_code() {
let frame = [0x10, 0x00, 0x00, 0xde, 0xad, 0xbe, 0x40, 0x01, 0xf0, 0x00];
assert!(FrameHeader::parse(&frame).is_err());
}
#[test]
fn rejects_short_frame() {
assert!(FrameHeader::parse(&[0x10, 0x00]).is_err());
}
}