Files
craft-libs/crates/video/tests/av1_decode/oracle_inter.rs
Dylan M. Taylor 16564be34b craft-video: AV1 decoder from FilmCraft, with DeckCraft's hardening (av1-decode) (#836)
* craft-video: AV1 decoder from FilmCraft (`av1-decode` feature)

FilmCraft's crates/av1 at 4c0d2ad7 becomes craft_video::av1::decode. The encoder moves to
av1/encode and is still exported from craft_video::av1. Feature av1 now turns on av1-decode and
av1-encode, so either half can be built without the other.

The ignored speed tests no longer read the thread CPU clock (FilmCraft used rustix for it).
craft_video::budget (the picture memory budget from DeckCraft's bitstream crate) is added for the
next commit. craft-video is built at opt-level 3 in the dev profile, as FilmCraft built its
codecs.

* craft-video: AV1 decoder hardening from DeckCraft's copy

- The picture store (eight reference slots, the new frame, frames in flight) is checked against
  a memory budget before a frame is allocated; Decoder::set_memory_budget.
- Non-uniform tile spacing is refused past MAX_TILE_COLS columns or MAX_TILE_ROWS rows.

* craft-video: AV1 fuzz seeds are 64x64 so SVT-AV1 2.x can encode them

FilmCraft's seeds were 64x48; the SVT-AV1 2.3 on the Ubuntu runners refuses a source under 64
lines, and an encoder that exists but fails is a test failure, not a skip.
2026-11-11 17:43:10 -05:00

122 lines
5.1 KiB
Rust

//! Inter-frame bit-exactness against libdav1d (run as an external ffmpeg process): SVT-AV1
//! GOPs with hierarchical references, compound prediction, OBMC / warped motion, global motion,
//! motion-field projection and all loop filters.
use crate::common;
use common::*;
fn specs() -> Vec<Spec> {
let p = |preset: &'static str, crf: &'static str| -> Vec<&'static str> { vec!["-preset", preset, "-crf", crf] };
vec![
Spec::new("inter_testsrc_p8", "testsrc2=s=320x240:r=25", 10, "yuv420p", &p("8", "35")),
Spec::new("inter_testsrc_p3", "testsrc2=s=352x288:r=25", 24, "yuv420p", &p("3", "30")),
Spec::new("inter_mandel_10bit_p5", "mandelbrot=s=416x240:r=25", 16, "yuv420p10le", &p("5", "40")),
Spec::new("inter_noise_odd_p6", "testsrc2=s=203x117:r=25,noise=alls=15:allf=t", 12, "yuv420p", &p("6", "45")),
]
}
/// Super-resolution (denominator 12 on the inter frames), film grain synthesis and quantiser
/// matrices.
fn specs_tools() -> Vec<Spec> {
let p = |params: &'static str| -> Vec<&'static str> { vec!["-preset", "6", "-crf", "35", "-svtav1-params", params] };
vec![
Spec::new("inter_superres", "testsrc2=s=416x240:r=25", 8, "yuv420p", &p("superres-mode=1:superres-denom=12:superres-kf-denom=13")),
Spec::new("inter_film_grain", "testsrc2=s=352x288:r=25,noise=alls=20:allf=t", 8, "yuv420p", &p("film-grain=10")),
Spec::new("inter_qmatrix_10bit", "mandelbrot=s=352x288:r=25", 8, "yuv420p10le", &p("enable-qm=1:qm-min=0:qm-max=8")),
]
}
#[test]
fn inter_superres_and_grain() {
let Some(ff) = ffmpeg() else { return };
for s in specs_tools() {
check_bit_exact(&ff, &s);
}
}
#[test]
fn inter_gops() {
let Some(ff) = ffmpeg() else { return };
for s in specs() {
check_bit_exact(&ff, &s);
}
}
/// Draft mode: frames that refresh no reference slot skip the in-loop filters and are flagged;
/// every other picture stays bit-exact, with 1 and several threads.
#[test]
fn draft_mode_changes_only_flagged_non_reference_frames() {
let Some(ff) = ffmpeg() else { return };
let s = &specs()[0];
let path = make(&ff, s);
let first = decode_all_threads(&path, 1).expect("decode");
let raw = reference(&ff, &path, pix_fmt_for(&first[0]));
let per = picture_samples(&first[0]);
for threads in [1, 4] {
let pics = decode_all_opts(&path, threads, true).expect("decode");
assert_eq!(pics.len(), first.len(), "threads {threads}");
let (mut draft, mut changed) = (0, 0);
for (i, p) in pics.iter().enumerate() {
let (_, count) = compare(p, &raw[i * per..(i + 1) * per]);
if p.draft {
draft += 1;
changed += (count > 0) as usize;
} else {
assert_eq!(count, 0, "threads {threads}: unflagged frame {i} differs");
}
}
assert!(draft > 0, "threads {threads}: no non-reference frames");
assert!(changed > 0, "threads {threads}: draft frames identical to the filtered ones");
}
}
/// Several tiles (decoded in parallel into private buffers, then merged).
fn specs_tiles() -> Vec<Spec> {
let p = |preset: &'static str, params: &'static str| -> Vec<&'static str> { vec!["-preset", preset, "-crf", "32", "-svtav1-params", params] };
vec![
Spec::new("inter_tiles_4x2", "testsrc2=s=1280x720:r=25,noise=alls=4:allf=t", 8, "yuv420p", &p("8", "tile-columns=2:tile-rows=1")),
Spec::new("inter_tiles_odd_10bit", "mandelbrot=s=650x370:r=25", 8, "yuv420p10le", &p("6", "tile-columns=1:tile-rows=1")),
]
}
#[test]
fn inter_tiles() {
let Some(ff) = ffmpeg() else { return };
for s in specs_tiles() {
check_bit_exact(&ff, &s);
}
}
/// The 1080p performance fixtures (slow; part of the extended run).
#[test]
#[ignore]
fn inter_1080p() {
let Some(ff) = ffmpeg() else { return };
let p = |crf: &'static str, params: &'static str| -> Vec<&'static str> { vec!["-preset", "8", "-crf", crf, "-svtav1-params", params] };
let src = "testsrc2=s=1920x1080:r=25,noise=alls=6:allf=t";
for s in [
Spec::new("perf_1080p_gop", src, 60, "yuv420p", &p("30", "keyint=60")),
Spec::new("perf_1080p_gop_10bit", src, 60, "yuv420p10le", &p("30", "keyint=60")),
Spec::new("perf_1080p_gop_hq", src, 60, "yuv420p", &p("18", "keyint=60")),
Spec::new("perf_1080p_gop_tiles", src, 60, "yuv420p", &p("30", "keyint=60:tile-columns=2:tile-rows=1")),
] {
check_bit_exact(&ff, &s);
}
}
/// 4:2:2 and 4:4:4 (libaom; SVT-AV1 only makes 4:2:0): the post-filters run in row bands whose
/// chroma rows depend on the vertical subsampling, so these check the band edges there too.
#[test]
fn inter_chroma_444_422() {
let Some(ff) = ffmpeg_libaom() else { return };
let p = ["-cpu-used", "8", "-crf", "40", "-enable-restoration", "1"];
let src = "testsrc2=s=416x400:r=25,noise=alls=8:allf=t";
for s in [
Spec::new("inter_aom_444", src, 6, "yuv444p", &p).encoder("libaom-av1"),
Spec::new("inter_aom_422", src, 6, "yuv422p", &p).encoder("libaom-av1"),
Spec::new("inter_aom_444_10bit", src, 6, "yuv444p10le", &p).encoder("libaom-av1"),
] {
check_bit_exact(&ff, &s);
}
}