Files
filmcraft/crates/codecs/tests/h264_draft.rs
Brandon Thomas 1f4546d549 M4.9: draft decoding for reduced-resolution playback (opt-in preference)
Settings > Playback > Draft decoding (off by default): the Program monitor
asks for draft frames only while playing at 1/2 resolution or lower
(FrameKey::draft, filmcraft_media::cancel::with_draft). The GOP cache runs
the decoder in draft mode for those requests and serves draft frames to draft
requests only, so paused frames, renders and exports always get exact
pictures (codecs test: exports after draft playback match ffmpeg). Draft
plans hand the GPU box-decimated YUV planes at the drawn size. perf.stats
reports draft frames, H.264 threads and the preference; the bench gains
2160p quarter and draft playback cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:33:03 -04:00

69 lines
2.8 KiB
Rust

//! H.264 draft decoding (opt-in, reduced-resolution playback) through the media stack: it is off
//! by default, its approximate frames only ever reach draft requests, and decoding for an export
//! after draft playback (same source, same GOP cache) still matches ffmpeg bit for bit.
mod common;
use common::*;
use filmcraft_media::FrameRequest;
use filmcraft_media::cancel::with_draft;
#[test]
fn draft_playback_never_reaches_exports() {
let ff = filmcraft_testkit::require_ffmpeg!();
// B-pyramid: references and non-reference B pictures (the ones draft mode leaves unfiltered)
let Some(file) = fixture(
&ff,
"draft_h264.mp4",
&[
"-f",
"lavfi",
"-i",
"testsrc2=s=320x240:r=24:d=2,noise=alls=10:allf=t",
"-c:v",
"libx264",
"-preset",
"fast",
"-x264-params",
"bframes=3:b-pyramid=normal:keyint=48",
"-pix_fmt",
"yuv420p",
],
) else {
return;
};
let raw = ffmpeg_frames(&ff, &file, "yuv420p");
let (w, h) = (320usize, 240usize);
let fsize = w * h * 3 / 2;
let n = raw.len() / fsize;
assert!(n >= 40, "{n} frames");
let reference = |i: usize| raw_planes(&raw[i * fsize..(i + 1) * fsize], w, h, w / 2, h / 2, 1);
let src = filmcraft_codecs::open_bytes("draft_h264.mp4", bytes(&file)).expect("open");
let rate = src.info().frame_rate();
// Default: nothing is a draft, every frame exact.
let g0 = filmcraft_codecs::gop_stats();
for i in 0..n {
let f = src.video_frame(FrameRequest { time: rate.tick_of(i as i64), scale: 0.5 }).expect("frame");
assert_eq!(max_diff(&planes(&f), &reference(i)), 0, "frame {i} without draft mode");
}
assert_eq!(filmcraft_codecs::gop_stats().draft, g0.draft, "draft decoding is off by default");
// Draft playback at 1/2 resolution on a fresh source: non-reference pictures are drafts.
let src = filmcraft_codecs::open_bytes("draft_h264.mp4", bytes(&file)).expect("open");
let g1 = filmcraft_codecs::gop_stats();
let mut approximate = 0;
for i in 0..n {
let f = with_draft(true, || src.video_frame(FrameRequest { time: rate.tick_of(i as i64), scale: 0.5 })).expect("frame");
approximate += (max_diff(&planes(&f), &reference(i)) > 0) as usize;
}
let drafts = filmcraft_codecs::gop_stats().draft - g1.draft;
assert!(drafts > 0 && approximate > 0 && approximate as u64 <= drafts, "{drafts} draft frames, {approximate} differ");
// Then an export from the same source (no draft hint): every frame is exact again.
for i in 0..n {
let f = src.video_frame(FrameRequest::full(rate.tick_of(i as i64))).expect("frame");
assert_eq!(max_diff(&planes(&f), &reference(i)), 0, "export frame {i} after draft playback");
}
}