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