aboutsummaryrefslogtreecommitdiffstats
path: root/nihav-rad/src/codecs/binkvidenc/rc.rs
blob: 01945bfaef2057aa87d979cc985592844b09820d (plain) (blame)
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use super::BlockMode;

#[derive(Default)]
pub struct RateControl {
    bitrate:    u32,
    bitpool:    u32,
    tb_num:     u32,
    tb_den:     u32,
    fpos:       u32,
    quality:    u8,
    lambda:     f32,
    first:      bool,
}

impl RateControl {
    pub fn new() -> Self {
        Self {
            lambda: 1.0,
            ..Default::default()
        }
    }
    pub fn init(&mut self, tb_num: u32, tb_den: u32, bitrate: u32, quality: u8) {
        self.tb_num  = tb_num;
        self.tb_den  = tb_den;
        self.bitrate = bitrate;
        self.quality = quality;

        self.bitpool = self.bitrate;
        self.fpos = 0;
        self.first = true;
    }
    pub fn metric(&self, diff: u32, bits: usize) -> u32 {
        diff.saturating_add((self.get_weight() * (bits as f32)) as u32)
    }
    fn get_weight(&self) -> f32 {
        if (0..=100).contains(&self.quality) {
            self.lambda * ((100 - self.quality) as f32)
        } else {
            self.lambda
        }
    }
    pub fn expected_size(&self) -> u32 {
        if self.bitrate != 0 {
            (if !self.first {
                let ticks = self.tb_den - self.fpos;
                u64::from(self.bitpool) * u64::from(self.tb_num) / u64::from(ticks)
            } else {
                u64::from(self.bitrate) * 4 * u64::from(self.tb_num) / u64::from(self.tb_den)
            }) as u32
        } else {
            0
        }
    }
    pub fn update_size(&mut self, real_size: usize) {
        if self.bitrate != 0 {
            let bits = (real_size * 8) as u32;
            let tgt_size = self.expected_size();

            self.fpos += self.tb_num;
            while self.fpos >= self.tb_den {
                self.fpos -= self.tb_den;
                self.bitpool += self.bitrate;
            }
            self.bitpool = self.bitpool.saturating_sub(bits);

            if bits > tgt_size + tgt_size / 8 {
                self.lambda += 0.1;
            }
            if bits < tgt_size - tgt_size / 8 {
                self.lambda -= 0.1;
                if self.lambda < 0.0 {
                    self.lambda = 0.0;
                }
            }
            self.first = false;
        }
    }
    pub fn pattern_run_threshold(&self) -> u8 {
        match self.quality {
            1..=39 => 4,
            40..=59 => 3,
            60..=79 => 2,
            80..=89 => 1,
            _ => 0,
        }
    }
    pub fn get_quant_ranges(&self) -> [u8; 4] {
        match self.quality {
            98..=100 => [  0,  0,  0,  2 ],
            92..=97  => [  2, 16,  4, 16 ],
            85..=91  => [  5, 16,  7, 16 ],
            75..=84  => [  8, 16, 10, 16 ],
            55..=74  => [ 11, 16, 12, 16 ],
             1..=54  => [ 12, 16, 13, 16 ],
            _        => [  0, 16,  0, 16 ],
        }
    }
    pub fn modify_forbidden_btypes(&self, forbidden: &mut [bool; 12]) {
        if self.quality > 98 {
            forbidden[usize::from(BlockMode::Intra)] = true;
        }
        if self.quality > 0 {
            if self.quality < 80 {
                forbidden[usize::from(BlockMode::Run)] = true;
                forbidden[usize::from(BlockMode::Residue)] = true;
            }
            if self.quality < 90 {
                forbidden[usize::from(BlockMode::Raw)] = true;
            }
        }
    }
}