mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-29 12:38:06 -06:00
New backend in crates/platform (media_foundation/): a Direct3D-aware decoder MFT is driven at the level of single access units (Annex B), given the process's D3D11 video device through an IMFDXGIDeviceManager so it decodes with DXVA, and its NV12 / P010 textures are read back through a staging texture into planar Yuv8 / Yuv16. H.264 Baseline/Main/High and HEVC Main / Main 10, 4:2:0. - Hardware is verified: non-D3D-aware or async MFTs, streams the GPU's DXVA decoder does not list, and system-memory output (Microsoft's MFT software fallback) are declined or fail the stream so HybridDecoder continues with our decoder. - mfplat.dll is loaded at run time so Windows N editions still start. - register() / registered() / hardware_decoder_for() cover Windows; perf.stats reports the backend. - Tests (Windows): bit-exact parity with the software decoders at 640x360, 1080p and 2160p (resets, seeks, flush), forced mid-stream failure, damaged input, concurrency, repeated init. - Bench: runs on Windows (ffmpeg on PATH, process/thread CPU clocks), HEVC Main 10 2160p row, hardware counters per row. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMorBHwct72udS2LiC3ZHp
51 lines
2.5 KiB
Rust
51 lines
2.5 KiB
Rust
//! Settings ▸ Playback ▸ Hardware decoding through the decoder registry and the media stack:
|
|
//! Off gives the software decoder (and no hardware frames), Auto the hardware one where the
|
|
//! system has it. Its own test binary: the switch is process-wide.
|
|
|
|
mod common;
|
|
|
|
use common::*;
|
|
use filmcraft_media::FrameRequest;
|
|
|
|
#[test]
|
|
fn off_uses_the_software_decoder() {
|
|
let ff = filmcraft_testkit::require_ffmpeg!();
|
|
let Some(path) = named(&ff, "hevc_main.mp4") else { return };
|
|
let s = read_stream(&path);
|
|
let available = filmcraft_platform::register();
|
|
assert_eq!(filmcraft_platform::register(), available, "registering twice is harmless");
|
|
|
|
filmcraft_codecs::hw::set_hardware_decoding(false);
|
|
let d = filmcraft_codecs::make_video_decoder(&s.entry).unwrap();
|
|
assert_eq!(d.name(), "FilmCraft HEVC");
|
|
let bytes: std::sync::Arc<[u8]> = std::fs::read(&path).unwrap().into();
|
|
let (g0, h0) = (filmcraft_codecs::gop_stats(), filmcraft_codecs::hw::hw_stats());
|
|
let src = filmcraft_codecs::open_bytes("hevc_main.mp4", bytes.clone()).unwrap();
|
|
let rate = src.info().frame_rate();
|
|
for i in 0..30 {
|
|
src.video_frame(FrameRequest::full(rate.tick_of(i))).unwrap();
|
|
}
|
|
assert!(filmcraft_codecs::gop_stats().frames > g0.frames, "frames counted");
|
|
assert_eq!(filmcraft_codecs::hw::hw_stats().frames, h0.frames, "Off: no hardware frames");
|
|
|
|
filmcraft_codecs::hw::set_hardware_decoding(true);
|
|
let d = filmcraft_codecs::make_video_decoder(&s.entry).unwrap();
|
|
let hw = filmcraft_platform::hardware_decoder_for(&s.entry);
|
|
if !hw {
|
|
eprintln!("SKIPPED (Auto half): no hardware decoder ({available:?})");
|
|
assert_eq!(d.name(), "FilmCraft HEVC");
|
|
return;
|
|
}
|
|
let (backend, hw_name) = if cfg!(target_os = "macos") { ("VideoToolbox", "VideoToolbox HEVC") } else { ("Media Foundation", "Media Foundation HEVC") };
|
|
assert_eq!(d.name(), hw_name);
|
|
assert_eq!(filmcraft_codecs::hw::hw_backend(), Some(backend), "perf.stats reports the backend");
|
|
assert!(filmcraft_platform::registered(), "register() put the factory in the registry");
|
|
let h1 = filmcraft_codecs::hw::hw_stats();
|
|
let src = filmcraft_codecs::open_bytes("hevc_main.mp4", bytes).unwrap();
|
|
for i in 0..30 {
|
|
src.video_frame(FrameRequest::full(rate.tick_of(i))).unwrap();
|
|
}
|
|
let h2 = filmcraft_codecs::hw::hw_stats();
|
|
assert!(h2.frames > h1.frames && h2.sessions > h1.sessions, "Auto: hardware frames {h1:?} → {h2:?}");
|
|
}
|