Skip to main content

pito_codecs/vp9/enc/rc/
lf.rs

1use super::FrameKind;
2use super::q::qstep;
3
4pub const MAX_LEVEL: u8 = 63;
5
6pub fn guess_filter_level(base_q_idx: u8, kind: FrameKind) -> u8 {
7    if base_q_idx == 0 {
8        return 0;
9    }
10    let s = qstep(base_q_idx).sqrt();
11    let g = match kind {
12        FrameKind::Key => 1.45 * s - 3.5,
13        FrameKind::Golden | FrameKind::AltRef => 1.5 * s - 3.0,
14        FrameKind::Inter => 1.55 * s - 3.0,
15    };
16    g.round().clamp(0.0, MAX_LEVEL as f64) as u8
17}
18
19pub fn pick_filter_level(
20    base_q_idx: u8,
21    kind: FrameKind,
22    sse_at: Option<&mut dyn FnMut(u8) -> u64>,
23) -> u8 {
24    let guess = guess_filter_level(base_q_idx, kind);
25    let Some(sse_at) = sse_at else {
26        return guess;
27    };
28    if base_q_idx == 0 {
29        return 0;
30    }
31    let mut cache = [u64::MAX; MAX_LEVEL as usize + 1];
32    let mut probe = |l: u8, f: &mut dyn FnMut(u8) -> u64| {
33        if cache[l as usize] == u64::MAX {
34            cache[l as usize] = f(l);
35        }
36        cache[l as usize]
37    };
38    let mut best = guess;
39    let mut best_sse = probe(best, sse_at);
40    let mut step = (guess / 4).max(2);
41    while step > 0 {
42        let mut moved = false;
43        for l in [best.saturating_sub(step), (best + step).min(MAX_LEVEL)] {
44            if l == best {
45                continue;
46            }
47            let s = probe(l, sse_at);
48            if s < best_sse || (s == best_sse && l < best) {
49                best_sse = s;
50                best = l;
51                moved = true;
52            }
53        }
54        if !moved {
55            step /= 2;
56        }
57    }
58    best
59}