Files
craft-libs/crates/video/tests/hevc_decode/perf.rs
Dylan M. Taylor 1175abec2e craft-video: HEVC decoder from FilmCraft, with DeckCraft's hardening (hevc-decode) (#828)
* craft-video: HEVC decoder from FilmCraft (`hevc-decode` feature)

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

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: HEVC decoder hardening from DeckCraft's copy

- The picture store (DPB, current picture, pictures in flight) is checked against a memory
  budget before a picture is allocated; Decoder::set_memory_budget.
- configure_hvcc checks every array before handling any NAL: only VPS, SPS, PPS and SEI arrays,
  each NAL header agreeing with its array type. A picture slice in the record starts no picture.
- init_qp_minus26, slice_qp_delta and delta_chroma_log2_weight_denom are range checked before
  the additions that overflowed on Exp-Golomb values near the i32 limits.
- A picture job sets up its picture state inside the panic guard.
2026-11-11 14:24:30 -05:00

44 lines
1.3 KiB
Rust

//! Decode speed (ignored by default; run with
//! `cargo test --release -p craft-video --features hevc-decode,threads --test hevc_decode perf:: -- --ignored --nocapture`).
use crate::common;
use craft_video::hevc::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)
}
fn run(name: &str, label: &str) {
let f = common::fixture(name);
let Some((hevc, _)) = common::ensure(f) else { return };
common::check_fixture(name).unwrap();
let data = std::fs::read(hevc).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, 3);
println!("{label} ({} KB): {threads} thread(s): {frames} frames in {secs:.3}s = {:.1} fps", data.len() / 1024, frames as f64 / secs);
}
}
#[test]
#[ignore]
fn perf_1080p() {
run("bench_1080p", "1080p Main CRF24 medium");
}
#[test]
#[ignore]
fn perf_4k() {
run("bench_4k", "2160p Main10 CRF26 fast");
}