mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 08:52:32 +00:00
* 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.
58 lines
2.3 KiB
Rust
58 lines
2.3 KiB
Rust
//! Decode an Annex-B H.264 file to raw yuv420p: `h264dec in.h264 [out.yuv]`.
|
|
//! Prints timing information to stderr. `H264_BENCH_ITERS=n` (with `H264_THREADS=t`, and
|
|
//! `H264_DRAFT=1` for draft mode) turns it into a benchmark.
|
|
|
|
use craft_video::h264::decode::Decoder;
|
|
use std::io::Write;
|
|
|
|
fn main() {
|
|
let args: Vec<String> = std::env::args().collect();
|
|
if args.len() < 2 {
|
|
eprintln!("usage: h264dec in.h264 [out.yuv]");
|
|
std::process::exit(2);
|
|
}
|
|
let data = std::fs::read(&args[1]).expect("read input");
|
|
// H264_BENCH_ITERS=n: decode n times without output and report the best run
|
|
if let Some(n) = std::env::var("H264_BENCH_ITERS").ok().and_then(|v| v.parse::<usize>().ok()) {
|
|
let mut best = f64::MAX;
|
|
let mut frames = 0;
|
|
let threads = std::env::var("H264_THREADS").ok().and_then(|v| v.parse::<usize>().ok());
|
|
for _ in 0..n {
|
|
let mut dec = threads.map(Decoder::with_threads).unwrap_or_default();
|
|
dec.set_draft(std::env::var_os("H264_DRAFT").is_some());
|
|
let t0 = std::time::Instant::now();
|
|
frames = dec.decode(&data, 0).expect("decode").len() + dec.flush().len();
|
|
best = best.min(t0.elapsed().as_secs_f64());
|
|
}
|
|
eprintln!("best of {n}: {frames} frames in {best:.3}s ({:.1} fps)", frames as f64 / best);
|
|
return;
|
|
}
|
|
let mut out = args.get(2).map(|p| std::io::BufWriter::new(std::fs::File::create(p).expect("create output")));
|
|
let mut dec = Decoder::new();
|
|
let t0 = std::time::Instant::now();
|
|
let mut n = 0usize;
|
|
let mut write = |pics: Vec<craft_video::h264::decode::Picture>| {
|
|
for p in pics {
|
|
n += 1;
|
|
if let Some(o) = out.as_mut() {
|
|
o.write_all(&p.y).unwrap();
|
|
o.write_all(&p.u).unwrap();
|
|
o.write_all(&p.v).unwrap();
|
|
}
|
|
}
|
|
};
|
|
match dec.decode(&data, 0) {
|
|
Ok(p) => write(p),
|
|
Err(e) => eprintln!("decode error: {e}"),
|
|
}
|
|
write(dec.flush());
|
|
let dt = t0.elapsed().as_secs_f64();
|
|
if let Some(e) = dec.take_error() {
|
|
eprintln!("decode error: {e}");
|
|
}
|
|
if std::env::var("H264_STATS").is_ok() {
|
|
eprintln!("{:#?}", dec.stats());
|
|
}
|
|
eprintln!("{n} frames in {dt:.3}s ({:.1} fps)", n as f64 / dt);
|
|
}
|