pito_codecs/vp9/enc/rc/
lf.rs1use 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}