Files
craft-libs/crates/video/examples/vp9_vp9dec.rs
Dylan M. Taylor 6ac2a6a34a craft-video: VP9 decoder from FilmCraft, with DeckCraft's memory budget (vp9-decode) (#821)
* craft-video: VP9 decoder from FilmCraft (`vp9-decode` feature)

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

The decoder's tests find ffmpeg and the fixture directory through craft-media-testkit; a fixture
that cannot be generated fails under CRAFT_MEDIA_REQUIRE_ORACLES=1. craft-video is built at
opt-level 3 in the dev profile, as FilmCraft built its codecs.

* craft-video: VP9 decoder checks its picture store against a memory budget

DeckCraft's copy of the decoder refuses a frame size whose pictures (eight reference slots, the
new frame and those in flight) would not fit a byte budget. The budget type comes with it as
craft_video::budget; FilmCraft's 2^26 luma sample cap stays.
2026-11-11 13:47:53 -05:00

46 lines
2.1 KiB
Rust

//! Decode an IVF file: `cargo run --release -p craft-video --features vp9-decode,threads --example vp9_vp9dec -- in.ivf [out.yuv]`.
//! Writes raw planar output (8-bit or 16-bit little endian) and prints decoder statistics.
use std::io::Write;
fn main() {
let args: Vec<String> = std::env::args().collect();
let data = std::fs::read(&args[1]).expect("read input");
let mut out = args.get(2).map(|p| std::io::BufWriter::new(std::fs::File::create(p).expect("create output")));
let threads = std::env::var("VP9_THREADS").ok().and_then(|s| s.parse().ok()).unwrap_or(0);
let mut dec = if threads > 0 { craft_video::vp9::decode::Decoder::with_threads(threads) } else { craft_video::vp9::decode::Decoder::new() };
// VP9_DRAFT=1: draft mode (non-reference frames skip the loop filter).
dec.set_draft(std::env::var_os("VP9_DRAFT").is_some());
// VP9_LOOPS=n decodes the file n times (for profiling; the stream starts with a key frame).
let loops = std::env::var("VP9_LOOPS").ok().and_then(|s| s.parse().ok()).unwrap_or(1usize);
let hl = u16::from_le_bytes([data[6], data[7]]) as usize;
let mut i = 0i64;
let t0 = std::time::Instant::now();
let mut n = 0;
for _ in 0..loops {
let mut p = hl;
while p + 12 <= data.len() {
let sz = u32::from_le_bytes(data[p..p + 4].try_into().unwrap()) as usize;
p += 12;
let Some(frame) = data.get(p..p + sz) else { break };
p += sz;
match dec.decode(frame, i) {
Ok(pics) => {
for pic in pics.into_iter().chain(if p >= data.len() { dec.flush() } else { Vec::new() }) {
n += 1;
if let Some(o) = out.as_mut() {
for pl in [&pic.y, &pic.u, &pic.v] {
o.write_all(&pl.to_le_bytes()).unwrap();
}
}
}
}
Err(e) => eprintln!("chunk {i}: {e}"),
}
i += 1;
}
}
eprintln!("{n} pictures in {:.3}s", t0.elapsed().as_secs_f64());
eprintln!("{:#?}", dec.stats());
}