Files
filmcraft/crates/codecs/tests/pool_bitexact.rs
Brandon ThomasandClaude Opus 5.5 6a9fdce2ef codecs: released decoders are dropped off the cache lock; dirty-pool bit-exact test
trim_decoders dropped the decoders it took from idle caches while the requesting cache's lock was held. Dropping a decoder can block (a VideoToolbox session waits for its frames in flight), so the render path paid that latency under the lock. trim_decoders now returns the released decoders and frame_late keeps them in a local declared before its lock guard, so they drop after the guard on every return path; the cap is enforced exactly as before.

New tests/pool_bitexact.rs: primes the frame pool with 0xAA-filled plane buffers, then decodes an H.264 fixture into them; every frame must match ffmpeg bit for bit, and the pool must report the reuse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:16:09 -04:00

64 lines
2.4 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Decoded pictures are written into recycled plane buffers (`filmcraft_frame::pool`). A recycled
//! buffer still holds an old picture, so the decoder must overwrite every byte: here the pool is
//! primed with buffers full of 0xAA before decoding, and every frame must still match ffmpeg bit
//! for bit. (Its own process: the pool is global.)
mod common;
use std::sync::Arc;
use common::*;
use filmcraft_frame::{Chroma, PixelData, VideoFrame};
use filmcraft_media::FrameRequest;
#[test]
fn dirty_recycled_planes_decode_bit_exact() {
let ff = filmcraft_testkit::require_ffmpeg!();
// 640×480: luma and chroma planes are both large enough to be pooled
let Some(file) = fixture(
&ff,
"pool_h264.mp4",
&[
"-f",
"lavfi",
"-i",
"testsrc2=s=640x480:r=24:d=1,noise=alls=10:allf=t",
"-c:v",
"libx264",
"-preset",
"fast",
"-x264-params",
"bframes=2:keyint=12",
"-pix_fmt",
"yuv420p",
],
) else {
return;
};
let (w, h) = (640usize, 480usize);
let raw = ffmpeg_frames(&ff, &file, "yuv420p");
let fsize = w * h * 3 / 2;
let n = raw.len() / fsize;
assert!(n >= 20, "{n} frames");
let reference = |i: usize| raw_planes(&raw[i * fsize..(i + 1) * fsize], w, h, w / 2, h / 2, 1);
// prime the pool with dirty buffers of exactly the plane sizes the decoder asks for
for _ in 0..8 {
let dirty = |len: usize| Arc::new(vec![0xAAu8; len]);
let f = VideoFrame {
data: PixelData::Yuv8 { planes: [dirty(w * h), dirty(w * h / 4), dirty(w * h / 4)], chroma: Chroma::C420, alpha: None },
..VideoFrame::rgba8(w as u32, h as u32, vec![0; w * h * 4])
};
filmcraft_frame::pool::recycle(Arc::new(f));
}
let reused_before = filmcraft_frame::pool::stats().reused;
let src = filmcraft_codecs::open_bytes("pool_h264.mp4", bytes(&file)).expect("open");
let rate = src.info().frame_rate();
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, "frame {i} decoded into a recycled buffer");
}
assert!(filmcraft_frame::pool::stats().reused > reused_before, "the decoder took the dirty pooled buffers");
}