Skip to main content

me_snap_ab/
me_snap_ab.rs

1//! Interleaved A/B of the integer-pel diamond-centre snap (`tune_me_snap`).
2//!
3//! Both arms in ONE process, alternating pass by pass, best-of-N — this box drifts
4//! ~20% run to run, far more than the effect.
5//!
6//! ```text
7//! cargo run --release -p rusty_h264-encoder --example me_snap_ab -- <clip.yuv> <w>x<h>
8//! ```
9use rusty_h264_common::YuvFrame;
10use rusty_h264_encoder::{Encoder, EncoderConfig, Preset};
11
12fn load(path: &str, w: usize, h: usize, n: usize) -> Vec<YuvFrame> {
13    let raw = std::fs::read(path).expect("clip");
14    let fsz = w * h * 3 / 2;
15    raw.chunks_exact(fsz).take(n).map(|c| {
16        let mut fr = YuvFrame::black(w, h);
17        fr.y.copy_from_slice(&c[..w * h]);
18        fr.u.copy_from_slice(&c[w * h..w * h + w * h / 4]);
19        fr.v.copy_from_slice(&c[w * h + w * h / 4..]);
20        fr
21    }).collect()
22}
23
24fn main() {
25    let a: Vec<String> = std::env::args().skip(1).collect();
26    let (w, h) = a[1].split_once('x').unwrap();
27    let (w, h): (usize, usize) = (w.parse().unwrap(), h.parse().unwrap());
28    let frames = load(&a[0], w, h, std::env::var("RS_FRAMES").ok().and_then(|v| v.parse().ok()).unwrap_or(60));
29    let preset = match std::env::var("RS_PRESET").unwrap_or_default().as_str() {
30        "fast" => Preset::Fast, "quality" => Preset::Quality, _ => Preset::Balanced,
31    };
32    let arm_off: u32 = std::env::var("RS_ARM_OFF").ok().and_then(|v| v.parse().ok()).unwrap_or(0);
33    let arm_on: u32 = std::env::var("RS_ARM_ON").ok().and_then(|v| v.parse().ok()).unwrap_or(3);
34    let run = |on: bool| {
35        let m = if on { arm_on } else { arm_off };
36        let mut cfg = EncoderConfig::new(w, h);
37        cfg.qp = std::env::var("RS_QP").ok().and_then(|v| v.parse().ok()).unwrap_or(27);
38        cfg.gop_size = 30;
39        cfg.preset = preset;
40        cfg.tune_me_snap = m & 1 != 0;
41        cfg.tune_me_subpel_iter = m & 2 != 0;
42        let mut enc = Encoder::new(cfg).expect("enc");
43        let t = std::time::Instant::now();
44        let mut b = 0usize;
45        for f in &frames { b += enc.encode(f).len(); }
46        (t.elapsed().as_secs_f64(), b)
47    };
48    let mut best = [f64::MAX; 2];
49    let mut bytes = [0usize; 2];
50    for pass in 0..10 {
51        let arm = pass % 2;
52        let (t, b) = run(arm == 1);
53        if t < best[arm] { best[arm] = t; }
54        bytes[arm] = b;
55    }
56    let px = (w * h * frames.len()) as f64;
57    println!("arm {arm_off} -> {arm_on} — {} {w}x{h} {} frames {preset:?}", a[0], frames.len());
58    println!("  off : {:>7.1} ms  {:>6.2} Mpx/s  {:>9} bytes", best[0]*1e3, px/best[0]/1e6, bytes[0]);
59    println!("  on  : {:>7.1} ms  {:>6.2} Mpx/s  {:>9} bytes", best[1]*1e3, px/best[1]/1e6, bytes[1]);
60    println!("  speed {:>6.3}x   size {:>+6.2}%", best[0]/best[1],
61             100.0*(bytes[1] as f64/bytes[0] as f64 - 1.0));
62}