mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-26 02:59:51 -06:00
Extends the Windows backend (#141) to VP9 (profiles 0 and 2) and AV1 (main, 8/10-bit), 4:2:0. - codecs::hw: FrameStreamInfo (vpcC / av1C): profile, bit depth, chroma, picture size and colour as the software decoders report them (vp9_color / av1_color are now shared with them), random-access samples, in-band format changes; StreamInfo lets HybridDecoder handle NAL and frame-coded streams. - platform: media_foundation::stream (what each codec needs: DXVA profile, MFT request, input format), MfDecoder generalised; VP9 frames / AV1 temporal units go in as they are; the MFT's declared picture size (display aperture) is checked against the bitstream's. - Declined up front: VP9 profiles 1/3, AV1 profiles 1/2, 4:2:2/4:4:4, mono, 12-bit, no codec extension, no DXVA profile. A key frame of another size or a different sequence header falls back to the software decoder. - Tests: bit-exact parity (360p / 1080p / 2160p, seeks, resets, flush, hidden frames), forced failure, damaged samples and av1C, format change fallback, WebM / Matroska through the media stack. - Bench: AV1 fixtures with libaom when ffmpeg lacks SVT-AV1. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
99 lines
3.3 KiB
Rust
99 lines
3.3 KiB
Rust
//! VP9 and AV1 in WebM / Matroska through the media stack with Settings > Playback > Hardware
|
|
//! decoding Auto and Off (Windows). Its own test binary: the switch is process-wide.
|
|
#![cfg(target_os = "windows")]
|
|
|
|
mod common;
|
|
|
|
use common::*;
|
|
|
|
/// WebM / Matroska VP9 and AV1 through the media stack: the same pictures with hardware decoding
|
|
/// Auto as with it Off, and the hardware decoder really ran.
|
|
#[test]
|
|
fn webm_and_mkv_match_software() {
|
|
use filmcraft_media::FrameRequest;
|
|
let ff = filmcraft_testkit::require_ffmpeg!();
|
|
let src = "testsrc2=s=640x360:r=24:d=2,noise=alls=12:allf=t";
|
|
let cases: [(&str, Vec<&str>); 2] = [
|
|
(
|
|
"vp9.webm",
|
|
vec![
|
|
"-f",
|
|
"lavfi",
|
|
"-i",
|
|
src,
|
|
"-c:v",
|
|
"libvpx-vp9",
|
|
"-b:v",
|
|
"0",
|
|
"-crf",
|
|
"32",
|
|
"-deadline",
|
|
"good",
|
|
"-cpu-used",
|
|
"8",
|
|
"-g",
|
|
"24",
|
|
"-auto-alt-ref",
|
|
"1",
|
|
"-lag-in-frames",
|
|
"8",
|
|
"-pix_fmt",
|
|
"yuv420p",
|
|
],
|
|
),
|
|
(
|
|
"av1.mkv",
|
|
vec![
|
|
"-f",
|
|
"lavfi",
|
|
"-i",
|
|
src,
|
|
"-c:v",
|
|
"libaom-av1",
|
|
"-crf",
|
|
"34",
|
|
"-b:v",
|
|
"0",
|
|
"-cpu-used",
|
|
"8",
|
|
"-g",
|
|
"24",
|
|
"-lag-in-frames",
|
|
"8",
|
|
"-pix_fmt",
|
|
"yuv420p",
|
|
],
|
|
),
|
|
];
|
|
filmcraft_platform::register();
|
|
for (name, args) in cases {
|
|
let Some(path) = fixture(&ff, name, &args) else { continue };
|
|
let bytes: std::sync::Arc<[u8]> = std::fs::read(&path).unwrap().into();
|
|
let frames = |hw: bool| {
|
|
filmcraft_codecs::hw::set_hardware_decoding(hw);
|
|
let src = filmcraft_codecs::open_bytes(name, bytes.clone()).unwrap();
|
|
let rate = src.info().frame_rate();
|
|
(0..40).map(|i| src.video_frame(FrameRequest::full(rate.tick_of(i))).unwrap()).collect::<Vec<_>>()
|
|
};
|
|
let off = frames(false);
|
|
let before = filmcraft_codecs::hw::hw_stats();
|
|
let auto = frames(true);
|
|
let after = filmcraft_codecs::hw::hw_stats();
|
|
filmcraft_codecs::hw::set_hardware_decoding(true);
|
|
if after.sessions == before.sessions {
|
|
eprintln!("SKIPPED: no Media Foundation decoder for {name}");
|
|
continue;
|
|
}
|
|
assert!(after.frames > before.frames, "{name}: hardware frames");
|
|
for (i, (a, b)) in auto.iter().zip(&off).enumerate() {
|
|
assert_eq!((a.width, a.height, a.color, a.par), (b.width, b.height, b.color, b.par), "{name} frame {i}");
|
|
let (x, y) = (a.as_ref().clone(), b.as_ref().clone());
|
|
assert_same(
|
|
&format!("{name} frame {i}"),
|
|
&[filmcraft_codecs::DecodedFrame { pts: 0, frame: x, draft: false }],
|
|
&[filmcraft_codecs::DecodedFrame { pts: 0, frame: y, draft: false }],
|
|
);
|
|
}
|
|
}
|
|
}
|