Files
craft-libs/crates/video/tests/h264_decode/perf.rs
Dylan M. Taylor 0226f1d03e craft-video: H.264 decoder from FilmCraft, with DeckCraft's hardening (h264-decode) (#834)
* craft-video: H.264 decoder from FilmCraft (`h264-decode` feature)

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

Without the threads feature a reference row that was never published read through expect();
it now reads as a gray row. 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: H.264 decoder hardening from DeckCraft's copy

- The picture store (DPB, current picture, pictures in flight or queued for output) is checked
  against a memory budget before a picture is allocated; Decoder::set_memory_budget.
- configure_avcc checks every entry before handling any: SPS NAL units in the first list, PPS in
  the second. A picture slice in the record starts no picture.
- The inverse transforms clamp coefficients to the spec's r range instead of overflowing.
- Temporal direct motion vectors are scaled in i32 and clipped to the spec range.
- slice_qp_delta is added with a checked add.
- A picture job sets up its picture state inside the panic guard.
2026-11-11 15:25:33 -05:00

35 lines
1.3 KiB
Rust

//! Decode speed on a 1080p High-profile stream (ignored by default; run with
//! `cargo test --release -p craft-video --features h264-decode,threads --test h264_decode perf:: -- --ignored --nocapture`).
use crate::common;
use craft_video::h264::decode::Decoder;
use std::time::Instant;
fn bench(data: &[u8], threads: usize, iters: usize) -> (usize, f64) {
let mut best = f64::MAX;
let mut frames = 0;
for _ in 0..iters {
let mut dec = Decoder::with_threads(threads);
let t0 = Instant::now();
frames = dec.decode(data, 0).expect("decode").len() + dec.flush().len();
best = best.min(t0.elapsed().as_secs_f64());
}
(frames, best)
}
#[test]
#[ignore]
fn perf_1080p() {
let f = common::fixture("bench_1080p");
let Some((h264, _)) = common::ensure(f) else { return };
// correctness first
common::check_fixture("bench_1080p").unwrap();
let data = std::fs::read(h264).unwrap();
let cores = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
for threads in [1, cores.min(16)] {
let (frames, secs) = bench(&data, threads, 5);
println!("1080p High CRF20 ({} KB): {threads} thread(s): {frames} frames in {secs:.3}s = {:.1} fps", data.len() / 1024, frames as f64 / secs);
}
}