| 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 | } |