RealVideo 4 encoder
[nihav.git] / nihav-realmedia / src / codecs / rv40enc / ratectl.rs
diff --git a/nihav-realmedia/src/codecs/rv40enc/ratectl.rs b/nihav-realmedia/src/codecs/rv40enc/ratectl.rs
new file mode 100644 (file)
index 0000000..aa881ed
--- /dev/null
@@ -0,0 +1,244 @@
+use nihav_core::frame::FrameType;
+
+pub struct RateDistMetric {
+    pub lambda:         f32,
+    pub good_enough:    u32,
+    pub p_split_thr:    u32,
+}
+
+impl RateDistMetric {
+    pub fn new() -> Self {
+        Self {
+            lambda:         1.0,
+            good_enough:    256,
+            p_split_thr:   8192,
+        }
+    }
+    pub fn get_metric(&self, bits: u32, dist: u32) -> u32 {
+        ((bits as f32) + (dist as f32) * 0.1 * self.lambda).ceil() as u32
+    }
+}
+
+#[derive(Clone,Copy)]
+struct BitrateCounter {
+    factors:    [f32; 32],
+    last_q:     usize,
+    proj_size:  usize,
+    intra:      bool,
+}
+
+impl BitrateCounter {
+    fn new(intra: bool) -> Self {
+        let mut obj = Self {
+            factors:    [0.0; 32],
+            last_q:     0,
+            proj_size:  0,
+            intra
+        };
+        obj.reset();
+        obj
+    }
+    fn reset(&mut self) {
+        if self.intra {
+            self.last_q = 8;
+            for (q, dst) in self.factors.iter_mut().enumerate() {
+                let q = q as f32;
+                *dst = (-0.1 * q + 2.95) / 100.0;
+            }
+        } else {
+            self.last_q = 10;
+            for (q, dst) in self.factors.iter_mut().enumerate() {
+                let q = q as f32;
+                *dst = 100.0 / (8.2 * q * q + 51.0 * q + 3411.0);
+            }
+        }
+    }
+    fn init_metric(&self, metric: &mut RateDistMetric, q_add: usize) {
+        let q = (self.last_q + q_add).min(31);
+        const THRESHOLDS: [(u32, u32); 4] = [
+            (256, 8192), (128, 8192), (64, 4196), (32, 2048)
+        ];
+        let (ge_thr, ps_thr) = THRESHOLDS[q / 8];
+        metric.good_enough = ge_thr;
+        metric.p_split_thr = ps_thr;
+        metric.lambda = 1.0;
+    }
+    fn update_stats(&mut self, fsize: usize) {
+        if fsize < self.proj_size - self.proj_size / 8 {
+            let mut inv_fac = 1.0 / self.factors[self.last_q];
+            if inv_fac > 1.0 {
+                inv_fac -= 0.5;
+            }
+            self.factors[self.last_q] = 1.0 / inv_fac;
+        } else if fsize > self.proj_size + self.proj_size / 8 {
+            let mut inv_fac = 1.0 / self.factors[self.last_q];
+            if inv_fac < 200.0 {
+                inv_fac += 0.5;
+            }
+            self.factors[self.last_q] = 1.0 / inv_fac;
+        }
+    }
+    fn get_est_size(&self, complexity: u32, q: usize) -> usize {
+        ((complexity as f32) * self.factors[q]).ceil() as usize
+    }
+    fn get_quant(&mut self, target: usize, complexity: u32) -> usize {
+        let tgt_31 = self.get_est_size(complexity, 31);
+        let tgt_0  = self.get_est_size(complexity, 0);
+        if target < tgt_31 {
+            self.last_q = 31;
+            self.proj_size = tgt_31;
+        } else if target > tgt_0 {
+            self.last_q = 0;
+            self.proj_size = tgt_0;
+        } else { //xxx: do binary search?
+            for q in (0..31).rev() {
+                let expected_size = self.get_est_size(complexity, q);
+                if target >= (expected_size - expected_size / 8) &&
+                   target <= (expected_size + expected_size / 8) {
+                    self.proj_size = expected_size;
+                    self.last_q = q;
+                }
+            }
+        }
+        self.last_q
+    }
+    fn get_last_quant(&self) -> usize { self.last_q }
+}
+
+const TIMEBASE: u32 = 1000;
+
+pub struct BitRateControl {
+    force_quant:    Option<usize>,
+    force_quality:  Option<u8>,
+    br_counter:     [BitrateCounter; 2],
+
+    bitrate:        u32,
+    tpos:           u32,
+    bitpool:        usize,
+
+    duration:       u32,
+    dcount:         u32,
+
+    pub b_offset:   usize,
+}
+
+impl BitRateControl {
+    pub fn new() -> Self {
+        Self {
+            force_quant:    None,
+            force_quality:  None,
+            br_counter:     [BitrateCounter::new(true), BitrateCounter::new(false)],
+
+            bitrate:        0,
+            tpos:           0,
+            bitpool:        0,
+
+            duration:       0,
+            dcount:         0,
+
+            b_offset:       4,
+        }
+    }
+    pub fn rate_ctl_in_use(&self) -> bool {
+        self.force_quant.is_none() && self.force_quality.is_none() && self.bitrate != 0
+    }
+    pub fn set_bitrate(&mut self, bitrate: u32) {
+        self.bitrate = bitrate;
+        for br in self.br_counter.iter_mut() {
+            br.reset();
+        }
+
+        self.bitpool = (self.bitrate as usize) * 2;
+        self.tpos = 0;
+    }
+    pub fn set_force_quant(&mut self, force_q: Option<usize>) { self.force_quant = force_q; }
+    pub fn get_force_quant(&self) -> i8 {
+        if let Some(q) = self.force_quant {
+            q as i8
+        } else {
+            -1
+        }
+    }
+    pub fn set_force_quality(&mut self, force_q: Option<u8>) { self.force_quality = force_q; }
+    pub fn get_force_quality(&self) -> i8 {
+        if let Some(q) = self.force_quality {
+            q as i8
+        } else {
+            -1
+        }
+    }
+    pub fn get_quant(&mut self, ftype: FrameType, complexity: u32) -> usize {
+        if let Some(q) = self.force_quant {
+            q
+        } else if self.force_quality.is_some() {
+            4
+        } else if ftype != FrameType::B {
+            let tgt = self.get_target_size(ftype);
+            self.br_counter[if ftype == FrameType::I { 0 } else { 1 }].get_quant(tgt, complexity)
+        } else {
+            (self.br_counter[1].get_last_quant() + self.b_offset).min(31)
+        }
+    }
+    pub fn get_last_quant(&self, ftype: FrameType) -> usize {
+        match ftype {
+            FrameType::I => self.br_counter[0].get_last_quant(),
+            FrameType::P => self.br_counter[1].get_last_quant(),
+            _ => (self.br_counter[1].get_last_quant() + self.b_offset).min(31),
+        }
+    }
+    pub fn init_metric(&self, ftype: FrameType, metric: &mut RateDistMetric) {
+        if let Some(q) = self.force_quality {
+            metric.lambda = (q as f32) / 50.0;
+        } else {
+            match ftype {
+                FrameType::I => {
+                    self.br_counter[0].init_metric(metric, 0);
+                },
+                FrameType::P => {
+                    self.br_counter[1].init_metric(metric, 0);
+                },
+                _ => {
+                    self.br_counter[1].init_metric(metric, self.b_offset);
+                },
+            };
+        }
+    }
+    pub fn update_stats(&mut self, ftype: FrameType, fsize: usize, ts_diff: u32) {
+        if self.bitrate > 0 {
+            if ts_diff > 0 && self.duration < std::u32::MAX / 2 {
+                self.duration += ts_diff;
+                self.dcount += 1;
+            }
+            self.tpos += ts_diff;
+            while self.tpos >= TIMEBASE {
+                self.tpos -= TIMEBASE;
+                self.bitpool += self.bitrate as usize;
+            }
+            self.bitpool = self.bitpool.saturating_sub(fsize * 8).max(1024);
+        }
+        match ftype {
+            FrameType::I => self.br_counter[0].update_stats(fsize),
+            FrameType::P => self.br_counter[1].update_stats(fsize),
+            _ => {},
+        };
+    }
+    pub fn get_target_size(&self, ftype: FrameType) -> usize {
+        if self.bitrate == 0 || self.bitpool == 0 {
+            return 0;
+        }
+        let bitpool_limit = (self.bitrate + self.bitrate / 8) as usize;
+        let bitpool_avail = self.bitpool.min(bitpool_limit);
+        let target_size = if self.dcount > 0 {
+                let avg_len = ((self.duration + self.dcount / 2) / self.dcount).max(1);
+                bitpool_avail * (avg_len as usize) / ((TIMEBASE - self.tpos) as usize)
+            } else {
+                bitpool_avail / 10
+            };
+        let tgt_bits = match ftype {
+                FrameType::I => target_size * 3,
+                FrameType::B => target_size * 3 / 4,
+                _ => target_size,
+            };
+        (tgt_bits + 7) / 8
+    }
+}