X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-duck%2Fsrc%2Fcodecs%2Fvp7enc%2Frdo.rs;fp=nihav-duck%2Fsrc%2Fcodecs%2Fvp7enc%2Frdo.rs;h=88f74566bec0a5b9e1058adb1dd1b1e38f3d4d3c;hp=0000000000000000000000000000000000000000;hb=c5d5793c1fd18882a32acabb8141a221b0a97b61;hpb=b922b48d3b003b2f4b84755541fd9dc4be8f22f6 diff --git a/nihav-duck/src/codecs/vp7enc/rdo.rs b/nihav-duck/src/codecs/vp7enc/rdo.rs new file mode 100644 index 0000000..88f7456 --- /dev/null +++ b/nihav-duck/src/codecs/vp7enc/rdo.rs @@ -0,0 +1,207 @@ +use super::blocks::*; +use super::coder::*; + +static mut COUNTER: usize = 0; + +pub const SMALL_DIST: u32 = 256; +pub const MAX_DIST: u32 = std::u32::MAX; + +const INTER_TO_INTRA_RATIO: f32 = 0.85; + +pub struct RateDistMetric { + pub lambda: f32, +} + +impl RateDistMetric { + pub fn new() -> Self { + Self { + lambda: 1.0, + } + } + pub fn calc_metric(&self, dist: u32, nits: u32) -> u32 { + ((dist as f32) + self.lambda * (nits as f32) + 0.5) as u32 + } + pub fn adjust_br(&mut self, cur_size: usize, tgt_size: usize) { + let low_limit = tgt_size - tgt_size / 8; + let up_limit = tgt_size + tgt_size / 8; + if cur_size < low_limit { + self.lambda = (self.lambda - 0.1).max(0.0); + } else if cur_size > up_limit { + self.lambda = (self.lambda + 0.1).min(16.0); + } + } + + pub fn block_dist(&self, src: &[u8; 16], new: &[u8; 16], q: usize, ctype: usize, pctx: u8, probs: &[[[u8; 11]; 3]; 8]) -> (u32, bool) { + let mut diff = [0i16; 16]; + get_block_difference(&mut diff, src, new); + diff.fdct(); + diff.quant(q, ctype); + let has_nz = diff.has_nz(); + let nits = estimate_subblock_nits(&diff, ctype, pctx, probs); + diff.dequant(q, ctype); + diff.idct(); + let dist = get_difference_dist(src, new, &diff); +unsafe {COUNTER += 1;} + (self.calc_metric(dist, nits), has_nz) + } +} + +#[derive(Default)] +pub struct BitRateControl { + tb_num: u32, + tb_den: u32, + key_int: u32, + bitrate: u32, + force_q: Option, + bitpool: u32, + fpos: u32, + kpos: u32, + num_mb: u32, +} + +impl BitRateControl { + pub fn new() -> Self { Self::default() } + fn reset(&mut self) { + self.fpos = 0; + self.kpos = 0; + self.bitpool = self.bitrate; + } + pub fn set_params(&mut self, tb_num: u32, tb_den: u32, bitrate: u32, key_int: u8, num_mb: usize) { + self.tb_num = tb_num; + self.tb_den = tb_den; + self.bitrate = bitrate; + self.key_int = u32::from(key_int); + self.num_mb = num_mb as u32; + self.reset(); + } + pub fn has_bitrate(&self) -> bool { self.bitrate != 0 } + pub fn get_quant(&self) -> Option { self.force_q } + pub fn set_quant(&mut self, q: Option) { + if self.force_q != q { + self.force_q = q; + self.reset(); + } + } + pub fn set_key_interval(&mut self, key_int: u8) { + let key_int = u32::from(key_int); + if self.key_int != key_int { + self.key_int = key_int; + self.reset(); + } + } + pub fn get_target_size(&self, is_intra: bool) -> u32 { + if self.bitrate != 0 && self.force_q.is_none() { + let pool_frames = self.tb_den - self.fpos; + if self.key_int <= 1 { // all intra + if self.bitpool == 0 || pool_frames == 0 { + self.bitrate * self.tb_num / self.tb_den + } else { + self.bitpool / pool_frames + } + } else { + let full_gop_weight = 1.0 + ((self.key_int - 1) as f32) * INTER_TO_INTRA_RATIO; + let i_bits = if self.bitpool == 0 || pool_frames == 0 { + let gop_size = self.bitrate * self.tb_num * self.key_int / self.tb_den; + (gop_size as f32) / full_gop_weight + } else { + let full_gops = pool_frames / self.key_int; + let weight = (full_gops as f32) * full_gop_weight + ((pool_frames % self.key_int) as f32) * INTER_TO_INTRA_RATIO; + (self.bitpool as f32) / weight + }; + if is_intra { + (i_bits + 0.5) as u32 + } else { + (i_bits * INTER_TO_INTRA_RATIO + 0.5) as u32 + } + } + } else { + 0 + } + } + fn pred_nits_per_mb(is_intra: bool, q: usize) -> f32 { + let fq = q as f32; + match (is_intra, q) { + (true, 0..=6) => 3434.0 + fq * fq * 7.5 - fq * 195.0, + (true, _) => 2500.0 - (fq - 6.0).ln() * 500.0, + (false, 0..=10) => 1595.0 + fq * fq * 3.4 - fq * 125.0, + (false, _) => 800.0 - (fq - 8.0).ln() * 155.0, + } + } + #[allow(dead_code)] + // todo use for refining maybe + pub fn predict_size(&self, is_intra: bool, q: usize) -> u32 { + let min_size = if is_intra { 200 * 8 } else { 50 * 8 }; + let nits_per_mb = Self::pred_nits_per_mb(is_intra, q); + ((nits_per_mb * (self.num_mb as f32) / 8.0) as u32).max(min_size) + } + pub fn get_frame_quant(&self, is_intra: bool) -> usize { + if let Some(q) = self.force_q { + q as usize + } else { + let expected_size = self.get_target_size(is_intra); + let nits_per_mb = ((expected_size * 8) as f32) / (self.num_mb as f32); + if is_intra { + if nits_per_mb > 2500.0 { // simple search + if nits_per_mb > Self::pred_nits_per_mb(is_intra, 3) { + if nits_per_mb > Self::pred_nits_per_mb(is_intra, 1) { + 0 + } else if nits_per_mb > Self::pred_nits_per_mb(is_intra, 2) { + 1 + } else { + 2 + } + } else { + if nits_per_mb > Self::pred_nits_per_mb(is_intra, 4) { + 3 + } else if nits_per_mb > Self::pred_nits_per_mb(is_intra, 5) { + 4 + } else { + 5 + } + } + } else { + ((((2500.0 - nits_per_mb) / 500.0).exp() + 6.0) as usize).min(127) + } + } else { + if nits_per_mb > 680.0 { // simple search + let (start, end) = if nits_per_mb > Self::pred_nits_per_mb(is_intra, 5) { + if nits_per_mb > Self::pred_nits_per_mb(is_intra, 3) { + (0, 3) + } else { + (3, 5) + } + } else if nits_per_mb > Self::pred_nits_per_mb(is_intra, 7) { + (5, 7) + } else { + (7, 10) + }; + let mut q = end; + for qq in start..end { + if nits_per_mb > Self::pred_nits_per_mb(is_intra, qq) { + q = qq; + break; + } + } + q + } else { + ((((800.0 - nits_per_mb) / 155.0).exp() + 6.0) as usize).max(10).min(127) + } + } + } + } + pub fn update(&mut self, size: usize) { + self.kpos += 1; + if self.kpos == self.key_int { + self.kpos = 0; + } + if self.bitrate == 0 || self.force_q.is_some() { + return; + } + 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((size * 8) as u32); + } +}