X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-rad%2Fsrc%2Fcodecs%2Fbinkvidenc%2Frc.rs;fp=nihav-rad%2Fsrc%2Fcodecs%2Fbinkvidenc%2Frc.rs;h=01945bfaef2057aa87d979cc985592844b09820d;hp=0000000000000000000000000000000000000000;hb=217de10bc01258d3a479c2784d6c5542c30b0f63;hpb=94cebb38b3bf2252d0863d517838718787bb4529 diff --git a/nihav-rad/src/codecs/binkvidenc/rc.rs b/nihav-rad/src/codecs/binkvidenc/rc.rs new file mode 100644 index 0000000..01945bf --- /dev/null +++ b/nihav-rad/src/codecs/binkvidenc/rc.rs @@ -0,0 +1,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; + } + } + } +}