From 71b1297a65d79bc598f56b8ea20149711fdb8aba Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Tue, 24 Feb 2026 19:05:06 +0100 Subject: [PATCH] indeo3enc: improve rate control a bit --- nihav-indeo/src/codecs/indeo3enc/cell.rs | 4 ++-- nihav-indeo/src/codecs/indeo3enc/mod.rs | 18 ++++++++++++------ nihav-indeo/src/codecs/indeo3enc/ratectl.rs | 21 +++++++++++++++++---- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/nihav-indeo/src/codecs/indeo3enc/cell.rs b/nihav-indeo/src/codecs/indeo3enc/cell.rs index b4e30f9..ae00845 100644 --- a/nihav-indeo/src/codecs/indeo3enc/cell.rs +++ b/nihav-indeo/src/codecs/indeo3enc/cell.rs @@ -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], diff --git a/nihav-indeo/src/codecs/indeo3enc/mod.rs b/nihav-indeo/src/codecs/indeo3enc/mod.rs index f2269a2..cf4ff14 100644 --- a/nihav-indeo/src/codecs/indeo3enc/mod.rs +++ b/nihav-indeo/src/codecs/indeo3enc/mod.rs @@ -107,6 +107,8 @@ struct Indeo3Encoder { buf_sel: bool, width: usize, height: usize, + flat_thr_i: Option, + flat_thr_p: Option, 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() { diff --git a/nihav-indeo/src/codecs/indeo3enc/ratectl.rs b/nihav-indeo/src/codecs/indeo3enc/ratectl.rs index 1142ff7..e9c5f63 100644 --- a/nihav-indeo/src/codecs/indeo3enc/ratectl.rs +++ b/nihav-indeo/src/codecs/indeo3enc/ratectl.rs @@ -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) { + pub fn get_quant(&self, frameno: u32) -> (bool, Option, 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); } } -- 2.39.5