VP6 encoder
[nihav.git] / nihav-duck / src / codecs / vp6enc / ratectl.rs
1 use super::rdo::*;
2
3 pub struct RateControl {
4 pub lambda: f32,
5 tgt_br: u32,
6 budget: isize,
7 cur_time: u32,
8 ts_num: u32,
9 ts_den: u32,
10 mb_w: usize,
11 mb_h: usize,
12 projected: usize,
13 }
14
15 // todo intra/inter decision, better allocation for intra frames
16 impl RateControl {
17 pub fn new() -> Self {
18 Self {
19 lambda: 1.0,
20 tgt_br: 0,
21 budget: 0,
22 cur_time: 0,
23 ts_num: 0,
24 ts_den: 0,
25 mb_w: 0,
26 mb_h: 0,
27 projected: 0,
28 }
29 }
30 pub fn init(&mut self, mb_w: usize, mb_h: usize, bitrate: u32, ts_num: u32, ts_den: u32) {
31 self.mb_w = mb_w;
32 self.mb_h = mb_h;
33 self.lambda = 1.0;
34 self.cur_time = 0;
35 if bitrate == 0 || ts_num == 0 || ts_den == 0 {
36 self.tgt_br = 0;
37 self.budget = 0;
38 } else {
39 self.tgt_br = bitrate;
40 self.budget = bitrate as isize;
41 self.ts_num = ts_num;
42 self.ts_den = ts_den;
43 }
44 }
45 pub fn guess_quant(&mut self, intra: bool, huffman: bool) -> usize {
46 let fsize = self.get_target_frame_size(intra);
47 self.projected = fsize;
48 if fsize > 0 {
49 for q in 0..64 {
50 let est_fsize = estimate_frame_size(intra, huffman, q, self.mb_w, self.mb_h);
51 if fsize < est_fsize - est_fsize / 10 {
52 return q.saturating_sub(1);
53 }
54 if fsize < est_fsize + est_fsize / 10 {
55 return q;
56 }
57 }
58 63
59 } else {
60 42
61 }
62 }
63 pub fn update(&mut self, dsize: usize) {
64 const LAMBDA_STEP: f32 = 1.0 / 32.0;
65
66 if self.tgt_br == 0 {
67 return;
68 }
69 if (self.projected > dsize + dsize / 10) && self.lambda > LAMBDA_STEP {
70 self.lambda -= LAMBDA_STEP;
71 } else if self.projected < dsize - dsize / 10 {
72 self.lambda += LAMBDA_STEP;
73 }
74 self.budget -= dsize as isize;
75 self.cur_time += self.ts_num;
76 while self.cur_time >= self.ts_den {
77 self.cur_time -= self.ts_den;
78 self.budget += self.tgt_br as isize;
79 }
80 }
81 fn get_target_frame_size(&self, intra: bool) -> usize {
82 if self.tgt_br == 0 {
83 0
84 } else {
85 let mut avg_fsize = self.budget / ((self.ts_den - self.cur_time) as isize);
86 if avg_fsize > 0 {
87 // todo better intra/inter selection
88 if intra {
89 avg_fsize *= 3;
90 }
91 avg_fsize as usize
92 } else {
93 (self.tgt_br as usize) * (self.ts_num as usize) / (self.ts_den as usize) / 2
94 }
95 }
96 }
97 }