]> git.nihav.org Git - nihav.git/commitdiff
indeo3enc: improve rate control a bit
authorKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 24 Feb 2026 18:05:06 +0000 (19:05 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 24 Feb 2026 18:05:06 +0000 (19:05 +0100)
nihav-indeo/src/codecs/indeo3enc/cell.rs
nihav-indeo/src/codecs/indeo3enc/mod.rs
nihav-indeo/src/codecs/indeo3enc/ratectl.rs

index b4e30f912080442baff9b44a169bef2b23648226..ae00845ed967d11c2a7e1373f538b9c03074e323 100644 (file)
@@ -5,8 +5,8 @@ use super::{Indeo3Cell, Plane};
 
 pub const MAX_CELL_SIZE: usize = 2400;
 const DEFAULT_PIXEL: u8 = 0x40;
-const INTRA_FLAT_THRESHOLD: u32 = 8;
-const INTER_FLAT_THRESHOLD: u32 = 8;
+pub const INTRA_FLAT_THRESHOLD: u32 = 8;
+pub const INTER_FLAT_THRESHOLD: u32 = 8;
 
 struct IndexWriter<'a> {
     dst:    &'a mut [u8],
index f2269a24745fb6b2c18c63cdc571563accbc6252..cf4ff142687b2ade41fe9fad6ba635c2ca3aec08 100644 (file)
@@ -107,6 +107,8 @@ struct Indeo3Encoder {
     buf_sel:    bool,
     width:      usize,
     height:     usize,
+    flat_thr_i: Option<u32>,
+    flat_thr_p: Option<u32>,
 
     debug_tree: bool,
     debug_frm:  bool,
@@ -127,6 +129,8 @@ impl Indeo3Encoder {
             buf_sel:    false,
             width:      0,
             height:     0,
+            flat_thr_i: None,
+            flat_thr_p: None,
 
             debug_tree: false,
             debug_frm:  false,
@@ -298,8 +302,10 @@ impl NAEncoder for Indeo3Encoder {
                 self.rc.reset();
             }
 
-            let (is_intra, quant) = self.rc.get_quant(self.frameno);
+            let (is_intra, quant, ft_i, ft_p) = self.rc.get_quant(self.frameno);
             self.cenc.quant = quant;
+            self.cenc.flat_thr_i = self.flat_thr_i.unwrap_or(ft_i);
+            self.cenc.flat_thr_p = self.flat_thr_i.unwrap_or(ft_p);
 
             if is_intra {
                 self.buf_sel = false;
@@ -496,12 +502,12 @@ impl NAOptionHandler for Indeo3Encoder {
                         },
                         CELL_I_THRESHOLD_OPTION => {
                             if let NAValue::Int(val) = option.value {
-                                self.cenc.flat_thr_i = val as u32;
+                                self.flat_thr_i = Some(val as u32);
                             }
                         },
                         CELL_P_THRESHOLD_OPTION => {
                             if let NAValue::Int(val) = option.value {
-                                self.cenc.flat_thr_p = val as u32;
+                                self.flat_thr_p = Some(val as u32);
                             }
                         },
                         DO_RLE_OPTION => {
@@ -528,8 +534,8 @@ impl NAOptionHandler for Indeo3Encoder {
             MV_RANGE_OPTION => Some(NAValue::Int(i64::from(self.mv_est.mv_range))),
             MV_FLAT_OPTION => Some(NAValue::Int(i64::from(self.mv_est.flat_thr))),
             MV_THRESHOLD_OPTION => Some(NAValue::Int(i64::from(self.mv_est.mv_thr))),
-            CELL_I_THRESHOLD_OPTION => Some(NAValue::Int(i64::from(self.cenc.flat_thr_i))),
-            CELL_P_THRESHOLD_OPTION => Some(NAValue::Int(i64::from(self.cenc.flat_thr_p))),
+            CELL_I_THRESHOLD_OPTION => Some(NAValue::Int(i64::from(self.flat_thr_i.unwrap_or(INTRA_FLAT_THRESHOLD)))),
+            CELL_P_THRESHOLD_OPTION => Some(NAValue::Int(i64::from(self.flat_thr_p.unwrap_or(INTER_FLAT_THRESHOLD)))),
             DO_RLE_OPTION => Some(NAValue::Bool(self.cenc.do_rle)),
             TRY_AGAIN_OPTION => Some(NAValue::Bool(self.try_again)),
             _ => None,
@@ -597,7 +603,7 @@ mod test {
         let enc_options = &[
                 NAOption { name: super::TRY_AGAIN_OPTION, value: NAValue::Bool(true) },
             ];
-        encode_test("indeo3.avi", enc_options, Some(4), &[0xb7a78851, 0x194f8842, 0x3d05071d, 0x3029ab72]);
+        encode_test("indeo3.avi", enc_options, Some(4), &[0xc5453ec1, 0xe84f0f76, 0x550ecf66, 0x31e24dec]);
     }
     /*#[test]
     fn test_indeo3_roundtrip() {
index 1142ff7359649297a1b0ce0e01be7a765c3097c7..e9c5f631ac02600d5d88e562c3c5316c491222c9 100644 (file)
@@ -1,3 +1,5 @@
+use super::cell::{INTRA_FLAT_THRESHOLD, INTER_FLAT_THRESHOLD};
+
 #[derive(Default)]
 pub struct RateControl {
     bitrate:    u32,
@@ -14,7 +16,7 @@ impl RateControl {
     pub fn new() -> Self { Self{ key_int: 10, ..Default::default() } }
     pub fn set_quality(&mut self, quality: u8) {
         if quality > 0 {
-            self.quality = Some((quality - 1).min(15));
+            self.quality = Some((quality - 1).min(63));
         } else {
             self.quality = None;
         }
@@ -42,9 +44,20 @@ impl RateControl {
         self.fracs   = self.tb_den;
     }
     pub fn get_key_int(&self) -> u32 { self.key_int }
-    pub fn get_quant(&self, frameno: u32) -> (bool, Option<u8>) {
+    pub fn get_quant(&self, frameno: u32) -> (bool, Option<u8>, u32, u32) {
         let is_intra = self.key_int == 0 || (frameno % self.key_int) == 0;
-        (is_intra, self.quality)
+        if let Some(q) = self.quality {
+            // quality format mapping: LSB is altquant, then two bits of flat threshold, then three bits of quantiser
+            let quant = ((q >> 3) & 7) + (q & 1) * 8;
+            let qmul = (q >> 1) & 3;
+            const I_MULT: [u32; 4] = [ 8, 12, 16, 24 ];
+            const P_MULT: [u32; 4] = [ 8, 12, 16, 24 ];
+            let ft_i = INTRA_FLAT_THRESHOLD * I_MULT[usize::from(qmul)] / 8;
+            let ft_p = INTER_FLAT_THRESHOLD * P_MULT[usize::from(qmul)] / 8;
+            (is_intra, Some(quant), ft_i, ft_p)
+        } else {
+            (is_intra, None, INTRA_FLAT_THRESHOLD, INTER_FLAT_THRESHOLD)
+        }
     }
     pub fn advance(&mut self, size: u32, skipped: bool) {
         if self.bitrate != 0 {
@@ -71,7 +84,7 @@ impl RateControl {
     }
     pub fn increase_quant(&mut self) {
         if let Some(q) = self.quality {
-            if q < 8 {
+            if q < 63 {
                 self.quality = Some(q + 1);
             }
         }