Skip to main content

oximedia_optimize/
lib.rs

1//! Codec optimization and tuning suite for `OxiMedia`.
2//!
3//! `oximedia-optimize` provides advanced optimization techniques for video encoders:
4//!
5//! - **Rate-Distortion Optimization (RDO)** - Advanced mode decision based on rate-distortion curves
6//! - **Psychovisual Optimization** - Perceptual quality tuning using visual masking models
7//! - **Motion Search Tuning** - Advanced algorithms (`TZSearch`, EPZS, UMH) for motion estimation
8//! - **Intra Prediction Optimization** - RDO-based mode selection for intra frames
9//! - **Transform Optimization** - Adaptive transform selection (DCT/ADST) and quantization
10//! - **Loop Filter Tuning** - Deblocking and Sample Adaptive Offset (SAO) optimization
11//! - **Partition Selection** - Complexity-based block size selection
12//! - **Reference Frame Management** - Optimal reference frame selection and DPB management
13//! - **Adaptive Quantization** - Variance and psychovisual-based AQ modes
14//! - **Entropy Coding Optimization** - Context modeling for CABAC/CAVLC
15//! - **ROI Encoding** - Region-of-interest based quality allocation via [`roi_encode`]
16//! - **Temporal AQ** - Frame-level QP adaptation based on temporal complexity via [`temporal_aq`]
17//! - **VMAF Prediction** - Lightweight VMAF score estimation from pixel features via [`vmaf_predict`]
18//! - **Content-Adaptive GOP** - GOP structure selection based on content type via [`gop_optimizer`]
19//! - **Scene-Aware QP** - Lookahead-based scene-cut-aware QP adjustment via [`scene_encode`]
20//!
21//! # Architecture
22//!
23//! The optimization suite is organized into several modules:
24//!
25//! - [`rdo`] - Rate-distortion optimization engine and cost functions
26//! - [`psycho`] - Psychovisual optimization and masking models
27//! - [`motion`] - Advanced motion search algorithms
28//! - [`intra`] - Intra mode selection and directional prediction
29//! - [`transform`] - Transform type selection and quantization
30//! - [`filter`] - Loop filter strength tuning
31//! - [`partition`] - Partition decision trees
32//! - [`mod@reference`] - Reference frame management
33//! - [`aq`] - Adaptive quantization strategies
34//! - [`entropy`] - Entropy coding context optimization
35//! - [`roi_encode`] - Region of interest encoding with per-CTU QP maps
36//! - [`temporal_aq`] - Temporal adaptive quantization with AQ bridge
37//! - [`vmaf_predict`] - VMAF score prediction from spatial/temporal features
38//! - [`gop_optimizer`] - Content-adaptive GOP structure selection
39//! - [`scene_encode`] - Lookahead-based scene-aware QP adjustment
40//!
41//! # Optimization Levels
42//!
43//! Different preset levels balance encoding speed vs. quality:
44//!
45//! - **Fast**: Simple SAD-based decisions, limited search patterns
46//! - **Medium**: SATD-based with moderate RDO
47//! - **Slow**: Full RDO with extended search patterns
48//! - **Placebo**: Exhaustive search for maximum quality
49//!
50//! # Example
51//!
52//! ```ignore
53//! use oximedia_optimize::{OptimizerConfig, OptimizationLevel, Optimizer};
54//!
55//! let config = OptimizerConfig {
56//!     level: OptimizationLevel::Slow,
57//!     enable_psychovisual: true,
58//!     enable_aq: true,
59//!     lookahead_frames: 40,
60//!     ..Default::default()
61//! };
62//!
63//! let optimizer = Optimizer::new(config)?;
64//! let decision = optimizer.optimize_block(&frame_data, block_info)?;
65//! ```
66
67#![deny(unsafe_code)]
68#![warn(missing_docs)]
69#![allow(clippy::too_many_arguments)]
70#![allow(clippy::cast_precision_loss)]
71#![allow(clippy::cast_possible_truncation)]
72#![allow(clippy::cast_sign_loss)]
73#![allow(clippy::module_name_repetitions)]
74
75pub mod adaptive_ladder;
76pub mod aq;
77pub mod benchmark;
78pub mod bitrate_controller;
79pub mod bitrate_optimizer;
80pub mod cache_opt;
81pub mod cache_optimizer;
82pub mod cache_strategy;
83pub mod complexity_analysis;
84pub mod crf_sweep;
85pub mod decision;
86pub mod encode_preset;
87pub mod encode_stats;
88pub mod entropy;
89pub mod examples;
90pub mod filter;
91pub mod frame_budget;
92pub mod gop_optimizer;
93pub mod intra;
94pub mod lookahead;
95pub mod media_optimize;
96pub mod motion;
97pub mod parallel_strategy;
98pub mod partition;
99pub mod perceptual_optimization;
100pub mod prefetch;
101pub mod presets;
102pub mod psycho;
103pub mod quality_ladder;
104pub mod quality_metric;
105pub mod quantizer_curve;
106pub mod rdo;
107pub mod reference;
108pub mod roi_encode;
109pub mod scene_encode;
110pub mod strategies;
111pub mod temporal_aq;
112pub mod transcode_optimizer;
113pub mod transform;
114pub mod two_pass;
115pub mod utils;
116pub mod vmaf_predict;
117
118/// SIMD-accelerated block difference metrics (SAD, SATD).
119#[allow(unsafe_code)]
120pub mod simd_metrics;
121
122use oximedia_core::OxiResult;
123
124/// Optimization level presets.
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
126pub enum OptimizationLevel {
127    /// Fast encoding with simple SAD-based decisions.
128    Fast,
129    /// Medium quality with SATD and moderate RDO.
130    #[default]
131    Medium,
132    /// Slow encoding with full RDO.
133    Slow,
134    /// Exhaustive search for maximum quality.
135    Placebo,
136}
137
138/// Content type hints for adaptive optimization.
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
140pub enum ContentType {
141    /// Animation content (sharp edges, flat areas).
142    Animation,
143    /// Film/camera content (grain, natural textures).
144    Film,
145    /// Screen content (text, graphics).
146    Screen,
147    /// Generic mixed content.
148    #[default]
149    Generic,
150}
151
152/// Main optimizer configuration.
153#[derive(Debug, Clone)]
154pub struct OptimizerConfig {
155    /// Optimization level preset.
156    pub level: OptimizationLevel,
157    /// Enable psychovisual optimizations.
158    pub enable_psychovisual: bool,
159    /// Enable adaptive quantization.
160    pub enable_aq: bool,
161    /// Number of lookahead frames for temporal optimization.
162    pub lookahead_frames: usize,
163    /// Content type hint.
164    pub content_type: ContentType,
165    /// Enable parallel RDO evaluation.
166    pub parallel_rdo: bool,
167    /// Lambda multiplier for rate-distortion tradeoff.
168    pub lambda_multiplier: f64,
169    /// Enable ROI-based encoding optimization.
170    pub enable_roi: bool,
171    /// Enable temporal AQ (frame-level QP adaptation).
172    pub enable_temporal_aq: bool,
173}
174
175impl Default for OptimizerConfig {
176    fn default() -> Self {
177        Self {
178            level: OptimizationLevel::default(),
179            enable_psychovisual: true,
180            enable_aq: true,
181            lookahead_frames: 20,
182            content_type: ContentType::default(),
183            parallel_rdo: true,
184            lambda_multiplier: 1.0,
185            enable_roi: false,
186            enable_temporal_aq: false,
187        }
188    }
189}
190
191/// Main optimization engine.
192///
193/// Integrates RDO, psychovisual analysis, motion optimization, adaptive quantization,
194/// ROI encoding, and temporal AQ into a unified pipeline.
195pub struct Optimizer {
196    config: OptimizerConfig,
197    rdo_engine: rdo::RdoEngine,
198    psycho_analyzer: psycho::PsychoAnalyzer,
199    motion_optimizer: motion::MotionOptimizer,
200    aq_engine: aq::AqEngine,
201    roi_encoder: Option<roi_encode::RoiEncoder>,
202    temporal_aq_bridge: Option<temporal_aq::TemporalAqBridge>,
203}
204
205impl Optimizer {
206    /// Creates a new optimizer with the given configuration.
207    pub fn new(config: OptimizerConfig) -> OxiResult<Self> {
208        let rdo_engine = rdo::RdoEngine::new(&config)?;
209        let psycho_analyzer = psycho::PsychoAnalyzer::new(&config)?;
210        let motion_optimizer = motion::MotionOptimizer::new(&config)?;
211        let aq_engine = aq::AqEngine::new(&config)?;
212
213        let roi_encoder = if config.enable_roi {
214            Some(roi_encode::RoiEncoder::new(
215                roi_encode::RoiEncoderConfig::default(),
216            ))
217        } else {
218            None
219        };
220
221        let temporal_aq_bridge = if config.enable_temporal_aq {
222            Some(temporal_aq::TemporalAqBridge::with_defaults())
223        } else {
224            None
225        };
226
227        Ok(Self {
228            config,
229            rdo_engine,
230            psycho_analyzer,
231            motion_optimizer,
232            aq_engine,
233            roi_encoder,
234            temporal_aq_bridge,
235        })
236    }
237
238    /// Gets the optimizer configuration.
239    #[must_use]
240    pub fn config(&self) -> &OptimizerConfig {
241        &self.config
242    }
243
244    /// Gets the RDO engine.
245    #[must_use]
246    pub fn rdo_engine(&self) -> &rdo::RdoEngine {
247        &self.rdo_engine
248    }
249
250    /// Gets the psychovisual analyzer.
251    #[must_use]
252    pub fn psycho_analyzer(&self) -> &psycho::PsychoAnalyzer {
253        &self.psycho_analyzer
254    }
255
256    /// Gets the motion optimizer.
257    #[must_use]
258    pub fn motion_optimizer(&self) -> &motion::MotionOptimizer {
259        &self.motion_optimizer
260    }
261
262    /// Gets the adaptive quantization engine.
263    #[must_use]
264    pub fn aq_engine(&self) -> &aq::AqEngine {
265        &self.aq_engine
266    }
267
268    /// Gets a mutable reference to the adaptive quantization engine.
269    pub fn aq_engine_mut(&mut self) -> &mut aq::AqEngine {
270        &mut self.aq_engine
271    }
272
273    /// Gets the ROI encoder, if enabled.
274    #[must_use]
275    pub fn roi_encoder(&self) -> Option<&roi_encode::RoiEncoder> {
276        self.roi_encoder.as_ref()
277    }
278
279    /// Gets a mutable reference to the ROI encoder, if enabled.
280    pub fn roi_encoder_mut(&mut self) -> Option<&mut roi_encode::RoiEncoder> {
281        self.roi_encoder.as_mut()
282    }
283
284    /// Gets the temporal AQ bridge, if enabled.
285    #[must_use]
286    pub fn temporal_aq_bridge(&self) -> Option<&temporal_aq::TemporalAqBridge> {
287        self.temporal_aq_bridge.as_ref()
288    }
289
290    /// Gets a mutable reference to the temporal AQ bridge, if enabled.
291    pub fn temporal_aq_bridge_mut(&mut self) -> Option<&mut temporal_aq::TemporalAqBridge> {
292        self.temporal_aq_bridge.as_mut()
293    }
294
295    /// Processes a frame's temporal activity through the temporal AQ bridge
296    /// and returns the combined AQ result for a given spatial QP offset.
297    ///
298    /// This is the main integration point between temporal and spatial AQ.
299    pub fn process_temporal_aq(
300        &mut self,
301        activity: temporal_aq::TemporalActivity,
302        spatial_qp_offset: i8,
303    ) -> Option<temporal_aq::CombinedAqResult> {
304        if let Some(ref mut bridge) = self.temporal_aq_bridge {
305            bridge.update_temporal(activity);
306            Some(bridge.combine_with_spatial(spatial_qp_offset))
307        } else {
308            None
309        }
310    }
311
312    /// Generates the ROI QP delta map for the current frame.
313    ///
314    /// Returns `None` if ROI encoding is not enabled or no regions are set.
315    pub fn generate_roi_map(&self) -> Option<roi_encode::RoiOptimizeResult> {
316        self.roi_encoder.as_ref().map(|enc| enc.optimize_frame())
317    }
318
319    /// Adds an ROI region to the encoder (if enabled).
320    ///
321    /// Returns `true` if the region was added, `false` if ROI is not enabled.
322    pub fn add_roi_region(&mut self, region: roi_encode::RoiRegion) -> bool {
323        if let Some(ref mut enc) = self.roi_encoder {
324            enc.add_region(region);
325            true
326        } else {
327            false
328        }
329    }
330
331    /// Clears all ROI regions (if ROI is enabled).
332    pub fn clear_roi_regions(&mut self) {
333        if let Some(ref mut enc) = self.roi_encoder {
334            enc.clear_regions();
335        }
336    }
337}
338
339// Re-export commonly used types
340pub use aq::{AqEngine, AqMode, AqResult};
341pub use benchmark::{BenchmarkConfig, BenchmarkResult, BenchmarkRunner, Profiler};
342pub use decision::{
343    DecisionContext, DecisionStrategy, ModeDecision, ReferenceDecision, SplitDecision,
344};
345pub use entropy::{ContextModel, ContextOptimizer, EntropyStats};
346pub use filter::{DeblockOptimizer, FilterDecision, SaoOptimizer};
347pub use gop_optimizer::{
348    ContentAdaptiveGop, ContentGopDecision, GopOptimizer, GopPattern, GopPlan,
349};
350pub use intra::{AngleOptimizer, IntraModeDecision, ModeOptimizer};
351pub use lookahead::{GopStructure, LookaheadAnalyzer, LookaheadFrame};
352pub use motion::{
353    BidirectionalOptimizer, MotionOptimizer, MotionSearchResult, MotionVector, MvPredictor,
354    SubpelOptimizer,
355};
356pub use partition::{ComplexityAnalyzer, PartitionDecision, SplitOptimizer};
357pub use presets::{OptimizationPresets, TunePresets};
358pub use psycho::{ContrastSensitivity, PsychoAnalyzer, VisualMasking};
359pub use rdo::{CostEstimate, LambdaCalculator, RdoEngine, RdoResult, RdoqOptimizer};
360pub use reference::{DpbOptimizer, ReferenceSelection};
361pub use roi_encode::{QpDeltaMap, RoiEncoder, RoiEncoderConfig, RoiOptimizeResult, RoiRegion};
362pub use scene_encode::{LookaheadSceneQp, SceneEncodeParams, SceneEncoder, SceneMetrics};
363pub use strategies::{
364    BitrateAllocator, ContentAdaptiveOptimizer, OptimizationStrategy, StrategySelector,
365    TemporalOptimizer,
366};
367pub use temporal_aq::{CombinedAqResult, TemporalActivity, TemporalAqBridge, TemporalAqEngine};
368pub use transform::{QuantizationOptimizer, TransformSelection};
369pub use utils::{BlockMetrics, FrameMetrics, OptimizationStats};
370pub use vmaf_predict::{
371    BatchVmafPredictor, VmafFeatures, VmafPrediction, VmafPredictor, VmafPredictorConfig,
372};
373
374#[cfg(test)]
375mod integration_tests {
376    use super::*;
377
378    #[test]
379    fn test_optimizer_with_roi() {
380        let config = OptimizerConfig {
381            enable_roi: true,
382            ..Default::default()
383        };
384        let mut optimizer = Optimizer::new(config).expect("Optimizer creation should succeed");
385
386        assert!(optimizer.roi_encoder().is_some());
387        assert!(
388            optimizer.add_roi_region(roi_encode::RoiRegion::with_priority(0, 0, 128, 128, 2.0),)
389        );
390
391        let map = optimizer.generate_roi_map();
392        assert!(map.is_some());
393        let result = map.expect("ROI map should exist");
394        assert!(result.has_active_regions);
395    }
396
397    #[test]
398    fn test_optimizer_without_roi() {
399        let config = OptimizerConfig::default();
400        let mut optimizer = Optimizer::new(config).expect("Optimizer creation should succeed");
401
402        assert!(optimizer.roi_encoder().is_none());
403        assert!(!optimizer.add_roi_region(roi_encode::RoiRegion::new(0, 0, 64, 64),));
404        assert!(optimizer.generate_roi_map().is_none());
405    }
406
407    #[test]
408    fn test_optimizer_with_temporal_aq() {
409        let config = OptimizerConfig {
410            enable_temporal_aq: true,
411            ..Default::default()
412        };
413        let mut optimizer = Optimizer::new(config).expect("Optimizer creation should succeed");
414
415        assert!(optimizer.temporal_aq_bridge().is_some());
416
417        let mut activity = temporal_aq::TemporalActivity::new(0);
418        activity.avg_motion_magnitude = 30.0;
419        activity.motion_coverage = 0.6;
420
421        let result = optimizer.process_temporal_aq(activity, -2);
422        assert!(result.is_some());
423        let combined = result.expect("Temporal AQ result should exist");
424        assert_ne!(combined.final_qp_delta, 0);
425    }
426
427    #[test]
428    fn test_optimizer_without_temporal_aq() {
429        let config = OptimizerConfig::default();
430        let mut optimizer = Optimizer::new(config).expect("Optimizer creation should succeed");
431
432        assert!(optimizer.temporal_aq_bridge().is_none());
433        let result = optimizer.process_temporal_aq(temporal_aq::TemporalActivity::new(0), 0);
434        assert!(result.is_none());
435    }
436
437    #[test]
438    fn test_optimizer_full_pipeline() {
439        let config = OptimizerConfig {
440            enable_roi: true,
441            enable_temporal_aq: true,
442            enable_aq: true,
443            enable_psychovisual: true,
444            content_type: ContentType::Film,
445            ..Default::default()
446        };
447        let mut optimizer = Optimizer::new(config).expect("Optimizer creation should succeed");
448
449        // Add ROI region
450        optimizer.add_roi_region(roi_encode::RoiRegion::with_priority(
451            100, 100, 200, 200, 1.5,
452        ));
453
454        // Process temporal AQ
455        let mut activity = temporal_aq::TemporalActivity::new(0);
456        activity.avg_motion_magnitude = 15.0;
457        activity.motion_coverage = 0.4;
458        let temporal_result = optimizer.process_temporal_aq(activity, -1);
459        assert!(temporal_result.is_some());
460
461        // Generate ROI map
462        let roi_result = optimizer.generate_roi_map();
463        assert!(roi_result.is_some());
464
465        // Verify AQ engine is accessible
466        assert_eq!(optimizer.aq_engine().mode(), aq::AqMode::Combined);
467    }
468
469    #[test]
470    fn test_clear_roi_regions() {
471        let config = OptimizerConfig {
472            enable_roi: true,
473            ..Default::default()
474        };
475        let mut optimizer = Optimizer::new(config).expect("Optimizer creation should succeed");
476
477        optimizer.add_roi_region(roi_encode::RoiRegion::new(0, 0, 64, 64));
478        optimizer.clear_roi_regions();
479        let result = optimizer
480            .generate_roi_map()
481            .expect("ROI map should exist even without regions");
482        assert!(!result.has_active_regions);
483    }
484}