mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-29 20:47:00 -06:00
VP8 WebM showed black video: there was no VP8 decoder. New L0 crate filmcraft-vp8, implemented from RFC 6386 sections 1-19 (tables extracted from the RFC text, nothing from the attached reference decoder): every coding tool, versions 0-3, hidden altref frames, size changes. Bit-exact with libvpx on 62 libvpx VP8 test vectors, real-world FATE samples and generated fixtures; never panics on corrupted input. Wired into codecs for Matroska / WebM (V_VP8, including the alpha layer of transparent WebM) and MP4 (vp08). VP8 colour is BT.601 (RFC 6386 9.2). A hidden altref frame is a WebM block of its own with the time of the frame that follows it: the VP8 time lookup now moves on to that shown frame instead of repeating the previous one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
92 lines
3.3 KiB
Rust
92 lines
3.3 KiB
Rust
//! Bit-exactness against libvpx (through ffmpeg) on generated fixtures covering every coding
|
|
//! tool: skipped when ffmpeg or its libvpx encoder is absent.
|
|
|
|
mod common;
|
|
|
|
fn check(name: &str) {
|
|
let f = common::fixture(name);
|
|
let Some((ivf, yuv)) = common::ensure(f) else { return };
|
|
let data = std::fs::read(&ivf).unwrap();
|
|
let reference = std::fs::read(&yuv).unwrap();
|
|
let pictures = common::decode_all(&common::ivf_frames(&data));
|
|
let (w, h) = (f.width as usize, f.height as usize);
|
|
let size = w * h + 2 * w.div_ceil(2) * h.div_ceil(2);
|
|
assert_eq!(pictures.len(), reference.len() / size, "{name}: picture count");
|
|
for (i, p) in pictures.iter().enumerate() {
|
|
assert_eq!((p.width as usize, p.height as usize), (w, h), "{name}: size of picture {i}");
|
|
assert!(common::i420(p) == reference[i * size..(i + 1) * size], "{name}: picture {i} differs from libvpx");
|
|
}
|
|
}
|
|
|
|
macro_rules! fixture_tests {
|
|
($($name:ident),* $(,)?) => {
|
|
$(
|
|
#[test]
|
|
fn $name() {
|
|
check(stringify!($name));
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
fixture_tests!(
|
|
default,
|
|
odd_size,
|
|
tiny,
|
|
intra_only,
|
|
noise_high_rate,
|
|
near_lossless,
|
|
coarse,
|
|
partitions8,
|
|
altref_2pass,
|
|
sharpness7,
|
|
version1_bilinear_simple,
|
|
version2,
|
|
version3_fullpixel,
|
|
realtime_segments,
|
|
screen,
|
|
temporal_layers,
|
|
hd_1080p,
|
|
);
|
|
|
|
/// Hidden (altref) frames produce no picture, and leaving out the frames the decoder calls
|
|
/// disposable changes none of the other pictures.
|
|
#[test]
|
|
fn hidden_and_disposable_frames() {
|
|
for name in ["altref_2pass", "temporal_layers"] {
|
|
let f = common::fixture(name);
|
|
let Some((ivf, _)) = common::ensure(f) else { return };
|
|
let data = std::fs::read(&ivf).unwrap();
|
|
let frames = common::ivf_frames(&data);
|
|
// every picture, and which frames could be left out (asked right before each is decoded)
|
|
let mut dec = filmcraft_vp8::Decoder::new();
|
|
let mut all = Vec::new();
|
|
let mut disposable = Vec::new();
|
|
for (i, f) in frames.iter().enumerate() {
|
|
disposable.push(dec.is_disposable(f));
|
|
if let Some(p) = dec.decode(f, i as i64).unwrap() {
|
|
all.push((p.pts, common::i420(&p)));
|
|
}
|
|
}
|
|
let stats = dec.stats();
|
|
assert_eq!(stats.frames as usize, frames.len());
|
|
assert_eq!(all.len() + stats.hidden_frames as usize, frames.len(), "{name}: one picture per shown frame");
|
|
if name == "altref_2pass" {
|
|
assert!(stats.hidden_frames > 0, "{name}: has hidden altref frames");
|
|
}
|
|
if name == "temporal_layers" {
|
|
assert!(disposable.iter().filter(|&&d| d).count() >= frames.len() / 3, "{name}: the enhancement layer is disposable: {disposable:?}");
|
|
}
|
|
let mut dec = filmcraft_vp8::Decoder::new();
|
|
for (i, f) in frames.iter().enumerate() {
|
|
if disposable[i] {
|
|
continue;
|
|
}
|
|
if let Some(p) = dec.decode(f, i as i64).unwrap() {
|
|
let reference = all.iter().find(|(pts, _)| *pts == p.pts).expect("picture of a shown frame");
|
|
assert!(common::i420(&p) == reference.1, "{name}: picture {} changed after leaving out disposable frames", p.pts);
|
|
}
|
|
}
|
|
}
|
|
}
|