Skip to main content

oximedia_codec/rate_control/
abr.rs

1//! Average Bitrate (ABR) rate control with multi-pass encoding.
2//!
3//! ABR mode targets an average bitrate over the entire encode, allowing
4//! more flexibility than CBR while ensuring predictable file sizes.
5//!
6//! # Features
7//!
8//! - Multi-pass encoding (1-pass and 2-pass modes)
9//! - Sophisticated lookahead analysis for optimal bit distribution
10//! - Rate control models (linear, quadratic, power)
11//! - Curve compression for bitrate management
12//! - Adaptive reference frame selection
13//! - Target file size achievement
14//!
15//! # Two-Pass Encoding Workflow
16//!
17//! ```text
18//! Pass 1: Analysis
19//!   ┌─────────────┐
20//!   │ Read Frames │
21//!   └──────┬──────┘
22//!          │
23//!   ┌──────▼──────────┐
24//!   │ Complexity      │
25//!   │ Analysis        │
26//!   └──────┬──────────┘
27//!          │
28//!   ┌──────▼──────────┐
29//!   │ Statistics      │
30//!   │ Collection      │
31//!   └──────┬──────────┘
32//!          │
33//!   ┌──────▼──────────┐
34//!   │ Write Stats     │
35//!   │ File            │
36//!   └─────────────────┘
37//!
38//! Pass 2: Encoding
39//!   ┌─────────────┐
40//!   │ Read Stats  │
41//!   │ File        │
42//!   └──────┬──────┘
43//!          │
44//!   ┌──────▼──────────┐
45//!   │ Bit Allocation  │
46//!   │ Planning        │
47//!   └──────┬──────────┘
48//!          │
49//!   ┌──────▼──────────┐
50//!   │ Optimal         │
51//!   │ Encoding        │
52//!   └─────────────────┘
53//! ```
54
55#![allow(clippy::cast_lossless)]
56#![allow(clippy::cast_precision_loss)]
57#![allow(clippy::cast_possible_truncation)]
58#![allow(clippy::cast_sign_loss)]
59#![allow(clippy::cast_possible_wrap)]
60#![allow(clippy::too_many_arguments)]
61#![allow(clippy::struct_excessive_bools)]
62#![allow(dead_code)]
63#![forbid(unsafe_code)]
64
65use crate::frame::FrameType;
66
67use super::types::{FrameStats, GopStats, RcConfig, RcOutput};
68
69/// ABR (Average Bitrate) rate controller.
70#[derive(Clone, Debug)]
71pub struct AbrController {
72    /// Target bitrate in bits per second.
73    target_bitrate: u64,
74    /// Target file size in bytes (if specified).
75    target_file_size: Option<u64>,
76    /// Total frames in the sequence (if known).
77    total_frames: Option<u64>,
78    /// Current encoding pass (1 or 2).
79    pass: u8,
80    /// Frame rate.
81    framerate: f64,
82    /// Current QP.
83    current_qp: f32,
84    /// Min/max QP bounds.
85    min_qp: u8,
86    max_qp: u8,
87    /// Frame type QP offsets.
88    i_qp_offset: i8,
89    b_qp_offset: i8,
90    /// Frame counter.
91    frame_count: u64,
92    /// Total bits encoded so far.
93    total_bits: u64,
94    /// First pass statistics.
95    first_pass_stats: Option<FirstPassStats>,
96    /// Multi-pass allocator.
97    allocator: MultiPassAllocator,
98    /// Lookahead analyzer.
99    lookahead: LookaheadAnalyzer,
100    /// Rate control model.
101    rc_model: RateControlModel,
102    /// Curve compression state.
103    curve_state: CurveCompressionState,
104    /// GOP tracking.
105    current_gop: GopStats,
106    gop_history: Vec<GopStats>,
107}
108
109/// First pass statistics collection.
110#[derive(Clone, Debug, Default)]
111pub struct FirstPassStats {
112    /// Per-frame statistics.
113    pub frames: Vec<FramePassStats>,
114    /// Total complexity.
115    pub total_complexity: f64,
116    /// Total frames analyzed.
117    pub frame_count: u64,
118    /// Per-GOP complexity.
119    pub gop_complexities: Vec<f64>,
120    /// Scene change indices.
121    pub scene_changes: Vec<u64>,
122    /// Average frame complexity.
123    pub avg_complexity: f64,
124    /// Complexity variance.
125    pub complexity_variance: f64,
126}
127
128/// Per-frame statistics from first pass.
129#[derive(Clone, Debug, Default)]
130pub struct FramePassStats {
131    /// Frame index.
132    pub frame_index: u64,
133    /// Frame type.
134    pub frame_type: FrameType,
135    /// Spatial complexity.
136    pub spatial_complexity: f32,
137    /// Temporal complexity.
138    pub temporal_complexity: f32,
139    /// Combined complexity.
140    pub combined_complexity: f32,
141    /// Is scene change.
142    pub is_scene_change: bool,
143    /// Number of intra-coded blocks.
144    pub intra_blocks: u32,
145    /// Number of inter-coded blocks.
146    pub inter_blocks: u32,
147    /// Average motion vector magnitude.
148    pub avg_motion: f32,
149    /// Predicted bits needed.
150    pub predicted_bits: u64,
151    /// Allocated bits (filled in second pass).
152    pub allocated_bits: u64,
153}
154
155impl FirstPassStats {
156    /// Add frame statistics.
157    pub fn add_frame(&mut self, stats: FramePassStats) {
158        self.total_complexity += stats.combined_complexity as f64;
159        self.frame_count += 1;
160        if stats.is_scene_change {
161            self.scene_changes.push(stats.frame_index);
162        }
163        self.frames.push(stats);
164    }
165
166    /// Finalize statistics after first pass.
167    pub fn finalize(&mut self) {
168        if self.frame_count == 0 {
169            return;
170        }
171
172        self.avg_complexity = self.total_complexity / self.frame_count as f64;
173
174        // Calculate variance
175        let mut variance_sum = 0.0;
176        for frame in &self.frames {
177            let diff = frame.combined_complexity as f64 - self.avg_complexity;
178            variance_sum += diff * diff;
179        }
180        self.complexity_variance = variance_sum / self.frame_count as f64;
181    }
182
183    /// Get frame statistics by index.
184    pub fn get_frame(&self, index: u64) -> Option<&FramePassStats> {
185        self.frames.get(index as usize)
186    }
187}
188
189/// Multi-pass bit allocator.
190#[derive(Clone, Debug)]
191struct MultiPassAllocator {
192    /// Total bits available for entire encode.
193    total_bits_budget: u64,
194    /// Bits used so far.
195    bits_used: u64,
196    /// Frames remaining.
197    frames_remaining: u64,
198    /// Complexity-based allocation enabled.
199    complexity_based: bool,
200    /// Frame-type allocation ratios.
201    i_frame_ratio: f64,
202    p_frame_ratio: f64,
203    b_frame_ratio: f64,
204    /// Adaptive allocation strength.
205    adaptation_strength: f32,
206}
207
208impl Default for MultiPassAllocator {
209    fn default() -> Self {
210        Self {
211            total_bits_budget: 0,
212            bits_used: 0,
213            frames_remaining: 0,
214            complexity_based: true,
215            i_frame_ratio: 3.0,
216            p_frame_ratio: 1.0,
217            b_frame_ratio: 0.5,
218            adaptation_strength: 1.0,
219        }
220    }
221}
222
223impl MultiPassAllocator {
224    /// Initialize allocator with total budget.
225    fn initialize(&mut self, total_bits: u64, total_frames: u64) {
226        self.total_bits_budget = total_bits;
227        self.frames_remaining = total_frames;
228        self.bits_used = 0;
229    }
230
231    /// Allocate bits for a frame based on complexity and frame type.
232    fn allocate_frame_bits(
233        &mut self,
234        frame_type: FrameType,
235        complexity: f32,
236        avg_complexity: f32,
237        first_pass: &FirstPassStats,
238    ) -> u64 {
239        if self.frames_remaining == 0 {
240            return 0;
241        }
242
243        let bits_remaining = self.total_bits_budget.saturating_sub(self.bits_used);
244        let base_allocation = bits_remaining / self.frames_remaining;
245
246        // Frame type multiplier
247        let type_mult = match frame_type {
248            FrameType::Key => self.i_frame_ratio,
249            FrameType::Inter => self.p_frame_ratio,
250            FrameType::BiDir => self.b_frame_ratio,
251            FrameType::Switch => 1.5,
252        };
253
254        // Complexity-based adjustment
255        let complexity_mult = if self.complexity_based && avg_complexity > 0.0 {
256            (complexity / avg_complexity).clamp(0.5, 2.0)
257        } else {
258            1.0
259        };
260
261        // Calculate allocation with lookahead consideration
262        let lookahead_mult = self.calculate_lookahead_multiplier(first_pass);
263
264        let allocated =
265            (base_allocation as f64 * type_mult * complexity_mult as f64 * lookahead_mult)
266                .max(base_allocation as f64 * 0.25) as u64;
267
268        self.bits_used += allocated;
269        self.frames_remaining = self.frames_remaining.saturating_sub(1);
270
271        allocated
272    }
273
274    /// Calculate multiplier based on future complexity.
275    fn calculate_lookahead_multiplier(&self, first_pass: &FirstPassStats) -> f64 {
276        // Simplified lookahead - check remaining complexity distribution
277        let skip_count =
278            (first_pass.frame_count as usize).saturating_sub(self.frames_remaining as usize);
279        let remaining_complexity: f64 = first_pass
280            .frames
281            .iter()
282            .skip(skip_count)
283            .map(|f| f.combined_complexity as f64)
284            .sum();
285
286        if remaining_complexity > 0.0 && self.frames_remaining > 0 {
287            let avg_remaining = remaining_complexity / self.frames_remaining as f64;
288            let ratio = avg_remaining / first_pass.avg_complexity;
289
290            // Adjust based on future complexity
291            if ratio > 1.2 {
292                0.95 // Save bits for complex future
293            } else if ratio < 0.8 {
294                1.05 // Spend more now
295            } else {
296                1.0
297            }
298        } else {
299            1.0
300        }
301    }
302
303    /// Update after frame encoding.
304    fn update(&mut self, actual_bits: u64) {
305        // In practice, might adjust budget based on actual vs allocated
306        let _ = actual_bits;
307    }
308}
309
310/// Lookahead analyzer for optimal encoding decisions.
311#[derive(Clone, Debug)]
312struct LookaheadAnalyzer {
313    /// Lookahead depth in frames.
314    depth: usize,
315    /// Buffered frame complexities.
316    complexity_buffer: Vec<f32>,
317    /// Enable motion estimation lookahead.
318    enable_me_lookahead: bool,
319    /// Enable scene cut lookahead.
320    enable_scene_lookahead: bool,
321    /// Scene cut threshold.
322    scene_threshold: f32,
323}
324
325impl Default for LookaheadAnalyzer {
326    fn default() -> Self {
327        Self {
328            depth: 40,
329            complexity_buffer: Vec::new(),
330            enable_me_lookahead: true,
331            enable_scene_lookahead: true,
332            scene_threshold: 0.4,
333        }
334    }
335}
336
337impl LookaheadAnalyzer {
338    /// Push frame complexity to lookahead buffer.
339    fn push_complexity(&mut self, complexity: f32) {
340        self.complexity_buffer.push(complexity);
341        if self.complexity_buffer.len() > self.depth {
342            self.complexity_buffer.remove(0);
343        }
344    }
345
346    /// Analyze lookahead window for bit allocation hints.
347    fn analyze(&self) -> LookaheadAnalysis {
348        if self.complexity_buffer.is_empty() {
349            return LookaheadAnalysis::default();
350        }
351
352        let avg_complexity: f32 =
353            self.complexity_buffer.iter().sum::<f32>() / self.complexity_buffer.len() as f32;
354
355        let current_complexity = self.complexity_buffer[0];
356
357        // Detect scene cuts
358        let has_scene_cut = if self.enable_scene_lookahead && self.complexity_buffer.len() > 1 {
359            self.detect_scene_cut()
360        } else {
361            false
362        };
363
364        // Calculate complexity trend
365        let trend = if self.complexity_buffer.len() >= 10 {
366            let first_half: f32 = self.complexity_buffer[..5].iter().sum::<f32>() / 5.0;
367            let second_half: f32 = self.complexity_buffer[5..10].iter().sum::<f32>() / 5.0;
368            (second_half - first_half) / first_half
369        } else {
370            0.0
371        };
372
373        LookaheadAnalysis {
374            avg_complexity,
375            current_complexity,
376            has_scene_cut,
377            complexity_trend: trend,
378            recommend_bits_multiplier: self.calculate_multiplier(
379                current_complexity,
380                avg_complexity,
381                trend,
382            ),
383        }
384    }
385
386    /// Detect scene cut in lookahead window.
387    fn detect_scene_cut(&self) -> bool {
388        if self.complexity_buffer.len() < 2 {
389            return false;
390        }
391
392        let window = self.complexity_buffer.len().min(self.depth);
393        for i in 1..window {
394            let prev = self.complexity_buffer[i - 1];
395            let curr = self.complexity_buffer[i];
396            if prev > 0.0 && (curr / prev) > (1.0 + self.scene_threshold) {
397                return true;
398            }
399        }
400        false
401    }
402
403    /// Calculate bit allocation multiplier based on lookahead.
404    fn calculate_multiplier(&self, current: f32, avg: f32, trend: f32) -> f64 {
405        let base_mult = if avg > 0.0 {
406            (current / avg).clamp(0.7, 1.5)
407        } else {
408            1.0
409        };
410
411        // Adjust for trend
412        let trend_adj = if trend > 0.2 {
413            0.95 // Complexity increasing, save bits
414        } else if trend < -0.2 {
415            1.05 // Complexity decreasing, spend more
416        } else {
417            1.0
418        };
419
420        base_mult as f64 * trend_adj
421    }
422}
423
424/// Lookahead analysis result.
425#[derive(Clone, Debug, Default)]
426struct LookaheadAnalysis {
427    /// Average complexity in lookahead window.
428    avg_complexity: f32,
429    /// Current frame complexity.
430    current_complexity: f32,
431    /// Scene cut detected ahead.
432    has_scene_cut: bool,
433    /// Complexity trend (-1.0 to 1.0).
434    complexity_trend: f32,
435    /// Recommended bits multiplier.
436    recommend_bits_multiplier: f64,
437}
438
439/// Rate control model for bit prediction.
440#[derive(Clone, Debug)]
441struct RateControlModel {
442    /// Model type (0=linear, 1=quadratic, 2=power).
443    model_type: u8,
444    /// Linear model parameters.
445    linear_params: LinearModel,
446    /// Quadratic model parameters.
447    quad_params: QuadraticModel,
448    /// Power model parameters.
449    power_params: PowerModel,
450    /// Model selection based on error.
451    auto_select: bool,
452}
453
454impl Default for RateControlModel {
455    fn default() -> Self {
456        Self {
457            model_type: 0,
458            linear_params: LinearModel::default(),
459            quad_params: QuadraticModel::default(),
460            power_params: PowerModel::default(),
461            auto_select: true,
462        }
463    }
464}
465
466impl RateControlModel {
467    /// Predict bits for given complexity and QP.
468    fn predict(&self, complexity: f32, qp: f32) -> u64 {
469        let prediction = match self.model_type {
470            0 => self.linear_params.predict(complexity, qp),
471            1 => self.quad_params.predict(complexity, qp),
472            2 => self.power_params.predict(complexity, qp),
473            _ => self.linear_params.predict(complexity, qp),
474        };
475
476        prediction.max(100.0) as u64
477    }
478
479    /// Fit models using first pass data.
480    fn fit(&mut self, first_pass: &FirstPassStats) {
481        if first_pass.frames.len() < 10 {
482            return;
483        }
484
485        self.linear_params.fit(first_pass);
486        self.quad_params.fit(first_pass);
487        self.power_params.fit(first_pass);
488
489        if self.auto_select {
490            self.select_best_model(first_pass);
491        }
492    }
493
494    /// Select best model based on prediction error.
495    fn select_best_model(&mut self, first_pass: &FirstPassStats) {
496        let mut errors = [0.0_f64; 3];
497
498        for frame in &first_pass.frames {
499            if frame.predicted_bits == 0 {
500                continue;
501            }
502
503            let actual = frame.predicted_bits as f64;
504            let qp = 28.0; // Assume default QP for model selection
505
506            errors[0] += (actual - self.linear_params.predict(frame.combined_complexity, qp)).abs();
507            errors[1] += (actual - self.quad_params.predict(frame.combined_complexity, qp)).abs();
508            errors[2] += (actual - self.power_params.predict(frame.combined_complexity, qp)).abs();
509        }
510
511        // Select model with minimum error
512        self.model_type = if errors[0] <= errors[1] && errors[0] <= errors[2] {
513            0
514        } else if errors[1] <= errors[2] {
515            1
516        } else {
517            2
518        };
519    }
520}
521
522/// Linear rate control model: bits = a * complexity + b * qp + c
523#[derive(Clone, Debug)]
524struct LinearModel {
525    a: f64,
526    b: f64,
527    c: f64,
528}
529
530impl Default for LinearModel {
531    fn default() -> Self {
532        Self {
533            a: 100_000.0,
534            b: -5_000.0,
535            c: 50_000.0,
536        }
537    }
538}
539
540impl LinearModel {
541    fn predict(&self, complexity: f32, qp: f32) -> f64 {
542        self.a * complexity as f64 + self.b * qp as f64 + self.c
543    }
544
545    fn fit(&mut self, first_pass: &FirstPassStats) {
546        // Simplified least squares fitting
547        let n = first_pass.frames.len() as f64;
548        if n < 3.0 {
549            return;
550        }
551
552        let mut sum_c = 0.0;
553        let mut sum_bits = 0.0;
554
555        for frame in &first_pass.frames {
556            if frame.predicted_bits > 0 {
557                sum_c += frame.combined_complexity as f64;
558                sum_bits += frame.predicted_bits as f64;
559            }
560        }
561
562        let avg_c = sum_c / n;
563        let avg_bits = sum_bits / n;
564
565        // Simple linear fit
566        if avg_c > 0.0 {
567            self.a = avg_bits / avg_c;
568            self.c = avg_bits * 0.2;
569        }
570    }
571}
572
573/// Quadratic model: bits = a * complexity^2 + b * complexity + c * qp + d
574#[derive(Clone, Debug)]
575struct QuadraticModel {
576    a: f64,
577    b: f64,
578    c: f64,
579    d: f64,
580}
581
582impl Default for QuadraticModel {
583    fn default() -> Self {
584        Self {
585            a: 10_000.0,
586            b: 80_000.0,
587            c: -5_000.0,
588            d: 50_000.0,
589        }
590    }
591}
592
593impl QuadraticModel {
594    fn predict(&self, complexity: f32, qp: f32) -> f64 {
595        let c = complexity as f64;
596        self.a * c * c + self.b * c + self.c * qp as f64 + self.d
597    }
598
599    fn fit(&mut self, first_pass: &FirstPassStats) {
600        // Simplified quadratic fitting
601        let _ = first_pass;
602        // In production, would use proper polynomial regression
603    }
604}
605
606/// Power model: bits = a * complexity^b / qp^c
607#[derive(Clone, Debug)]
608struct PowerModel {
609    a: f64,
610    b: f64,
611    c: f64,
612}
613
614impl Default for PowerModel {
615    fn default() -> Self {
616        Self {
617            a: 150_000.0,
618            b: 1.1,
619            c: 0.8,
620        }
621    }
622}
623
624impl PowerModel {
625    fn predict(&self, complexity: f32, qp: f32) -> f64 {
626        if complexity <= 0.0 || qp <= 0.0 {
627            return 50_000.0;
628        }
629        self.a * (complexity as f64).powf(self.b) / (qp as f64).powf(self.c)
630    }
631
632    fn fit(&mut self, first_pass: &FirstPassStats) {
633        // Simplified power model fitting using log-space regression
634        let _ = first_pass;
635        // In production, would use proper log-linear regression
636    }
637}
638
639/// Curve compression for rate smoothing.
640#[derive(Clone, Debug)]
641struct CurveCompressionState {
642    /// Enable curve compression.
643    enabled: bool,
644    /// Compression ratio (0.0-1.0).
645    ratio: f32,
646    /// Complexity adjustment factor.
647    adjustment_factor: f32,
648}
649
650impl Default for CurveCompressionState {
651    fn default() -> Self {
652        Self {
653            enabled: true,
654            ratio: 0.6,
655            adjustment_factor: 1.0,
656        }
657    }
658}
659
660impl CurveCompressionState {
661    /// Apply curve compression to complexity.
662    fn compress(&self, complexity: f32, avg_complexity: f32) -> f32 {
663        if !self.enabled || avg_complexity <= 0.0 {
664            return complexity;
665        }
666
667        let ratio = complexity / avg_complexity;
668        let compressed_ratio = if ratio > 1.0 {
669            // Compress high complexity
670            1.0 + (ratio - 1.0) * self.ratio
671        } else {
672            // Expand low complexity
673            1.0 - (1.0 - ratio) * self.ratio
674        };
675
676        avg_complexity * compressed_ratio * self.adjustment_factor
677    }
678
679    /// Update adjustment factor based on encoding results.
680    fn update(&mut self, target_bitrate: f64, actual_bitrate: f64) {
681        if target_bitrate <= 0.0 {
682            return;
683        }
684
685        let error = (actual_bitrate - target_bitrate) / target_bitrate;
686
687        // Gradually adjust
688        let adjustment = 1.0 + (error * 0.05) as f32;
689        self.adjustment_factor = (self.adjustment_factor * adjustment).clamp(0.5, 2.0);
690    }
691}
692
693impl AbrController {
694    /// Create new ABR controller from configuration.
695    #[must_use]
696    pub fn new(config: &RcConfig) -> Self {
697        Self {
698            target_bitrate: config.target_bitrate,
699            target_file_size: None,
700            total_frames: None,
701            pass: 1,
702            framerate: config.framerate(),
703            current_qp: config.initial_qp as f32,
704            min_qp: config.min_qp,
705            max_qp: config.max_qp,
706            i_qp_offset: config.i_qp_offset,
707            b_qp_offset: config.b_qp_offset,
708            frame_count: 0,
709            total_bits: 0,
710            first_pass_stats: None,
711            allocator: MultiPassAllocator::default(),
712            lookahead: LookaheadAnalyzer::default(),
713            rc_model: RateControlModel::default(),
714            curve_state: CurveCompressionState::default(),
715            current_gop: GopStats::new(0, 0),
716            gop_history: Vec::new(),
717        }
718    }
719
720    /// Set target file size in bytes.
721    pub fn set_target_file_size(&mut self, size_bytes: u64, duration_seconds: f64) {
722        self.target_file_size = Some(size_bytes);
723        if duration_seconds > 0.0 {
724            // Calculate target bitrate from file size
725            self.target_bitrate = ((size_bytes * 8) as f64 / duration_seconds) as u64;
726            self.total_frames = Some((duration_seconds * self.framerate) as u64);
727        }
728    }
729
730    /// Set encoding pass (1 or 2).
731    pub fn set_pass(&mut self, pass: u8) {
732        self.pass = pass.clamp(1, 2);
733        if pass == 1 {
734            self.first_pass_stats = Some(FirstPassStats::default());
735        }
736    }
737
738    /// Set total frames for the encode.
739    pub fn set_total_frames(&mut self, count: u64) {
740        self.total_frames = Some(count);
741    }
742
743    /// Import first pass statistics for second pass.
744    pub fn set_first_pass_stats(&mut self, stats: FirstPassStats) {
745        // Fit rate control models
746        self.rc_model.fit(&stats);
747
748        // Initialize allocator
749        if let Some(total_frames) = self.total_frames {
750            let duration = total_frames as f64 / self.framerate;
751            let total_bits = (self.target_bitrate as f64 * duration) as u64;
752            self.allocator.initialize(total_bits, total_frames);
753        }
754
755        self.first_pass_stats = Some(stats);
756    }
757
758    /// Add frame to lookahead (for real-time first pass).
759    pub fn push_lookahead(&mut self, complexity: f32) {
760        self.lookahead.push_complexity(complexity);
761    }
762
763    /// Get rate control output for current frame.
764    #[must_use]
765    pub fn get_rc(&mut self, frame_type: FrameType, complexity: f32) -> RcOutput {
766        let target_bits = if self.pass == 2 && self.first_pass_stats.is_some() {
767            // Second pass: use allocated bits from first pass
768            self.calculate_second_pass_bits(frame_type, complexity)
769        } else {
770            // First pass: estimate bits
771            self.calculate_first_pass_bits(frame_type, complexity)
772        };
773
774        // Calculate QP
775        let qp = self.calculate_qp(frame_type, complexity, target_bits);
776
777        // Calculate lambda
778        let lambda = self.calculate_lambda(qp, frame_type);
779        let lambda_me = lambda.sqrt();
780
781        RcOutput {
782            qp,
783            qp_f: qp as f32,
784            target_bits,
785            min_bits: target_bits / 4,
786            max_bits: target_bits * 4,
787            lambda,
788            lambda_me,
789            ..Default::default()
790        }
791    }
792
793    /// Calculate bits for first pass.
794    fn calculate_first_pass_bits(&self, frame_type: FrameType, complexity: f32) -> u64 {
795        let base_bits = if self.framerate > 0.0 {
796            (self.target_bitrate as f64 / self.framerate) as u64
797        } else {
798            100_000
799        };
800
801        let type_mult = match frame_type {
802            FrameType::Key => 3.0,
803            FrameType::Inter => 1.0,
804            FrameType::BiDir => 0.5,
805            FrameType::Switch => 1.5,
806        };
807
808        let complexity_mult = if complexity > 0.5 {
809            complexity.clamp(0.5, 2.0)
810        } else {
811            1.0
812        };
813
814        (base_bits as f64 * type_mult * complexity_mult as f64) as u64
815    }
816
817    /// Calculate bits for second pass using first pass statistics.
818    fn calculate_second_pass_bits(&mut self, frame_type: FrameType, complexity: f32) -> u64 {
819        if let Some(ref stats) = self.first_pass_stats {
820            // Get frame statistics
821            if let Some(frame_stats) = stats.get_frame(self.frame_count) {
822                // Use pre-allocated bits
823                if frame_stats.allocated_bits > 0 {
824                    return frame_stats.allocated_bits;
825                }
826            }
827
828            // Allocate bits based on complexity
829            let avg_complexity = stats.avg_complexity as f32;
830            let compressed_complexity = self.curve_state.compress(complexity, avg_complexity);
831
832            self.allocator.allocate_frame_bits(
833                frame_type,
834                compressed_complexity,
835                avg_complexity,
836                stats,
837            )
838        } else {
839            self.calculate_first_pass_bits(frame_type, complexity)
840        }
841    }
842
843    /// Calculate QP based on target bits and complexity.
844    fn calculate_qp(&self, frame_type: FrameType, complexity: f32, target_bits: u64) -> u8 {
845        // Use rate model to estimate QP
846        let base_qp = if target_bits > 0 {
847            // Inverse model: estimate QP from bits and complexity
848            let model_prediction = self.rc_model.predict(complexity, 28.0_f32) as f64;
849            let ratio = target_bits as f64 / model_prediction;
850
851            // Adjust QP based on ratio
852            if ratio > 1.0 {
853                // More bits available, decrease QP
854                28.0_f64 - (ratio.ln() * 3.0)
855            } else {
856                // Fewer bits available, increase QP
857                28.0_f64 + ((1.0_f64 / ratio).ln() * 3.0)
858            }
859        } else {
860            28.0_f64
861        };
862
863        // Apply frame type offset
864        let offset = match frame_type {
865            FrameType::Key => self.i_qp_offset,
866            FrameType::BiDir => self.b_qp_offset,
867            _ => 0,
868        };
869
870        let final_qp = (base_qp + offset as f64).clamp(self.min_qp as f64, self.max_qp as f64);
871        final_qp as u8
872    }
873
874    /// Calculate lambda for RDO.
875    fn calculate_lambda(&self, qp: u8, frame_type: FrameType) -> f64 {
876        let base = 0.85 * 2.0_f64.powf((f64::from(qp) - 12.0) / 3.0);
877
878        let multiplier = match frame_type {
879            FrameType::Key => 0.6,
880            FrameType::BiDir => 1.4,
881            _ => 1.0,
882        };
883
884        base * multiplier
885    }
886
887    /// Update controller with frame encoding results.
888    pub fn update(&mut self, stats: &FrameStats) {
889        self.frame_count += 1;
890        self.total_bits += stats.bits;
891
892        // Update allocator
893        if self.pass == 2 {
894            self.allocator.update(stats.bits);
895        }
896
897        // Update curve compression
898        if self.frame_count > 10 {
899            let elapsed = self.frame_count as f64 / self.framerate;
900            let actual_bitrate = self.total_bits as f64 / elapsed;
901            self.curve_state
902                .update(self.target_bitrate as f64, actual_bitrate);
903        }
904
905        // Update GOP tracking
906        self.current_gop.add_frame(stats.clone());
907        if stats.frame_type == FrameType::Key && self.current_gop.frame_count > 1 {
908            self.gop_history.push(self.current_gop.clone());
909            let next_idx = self.current_gop.gop_index + 1;
910            self.current_gop = GopStats::new(next_idx, self.frame_count);
911        }
912
913        // Collect first pass stats
914        if self.pass == 1 {
915            if let Some(ref mut pass_stats) = self.first_pass_stats {
916                let frame_stats = FramePassStats {
917                    frame_index: stats.frame_num,
918                    frame_type: stats.frame_type,
919                    spatial_complexity: stats.spatial_complexity,
920                    temporal_complexity: stats.temporal_complexity,
921                    combined_complexity: stats.complexity,
922                    is_scene_change: stats.scene_cut,
923                    intra_blocks: 0,
924                    inter_blocks: 0,
925                    avg_motion: 0.0,
926                    predicted_bits: stats.bits,
927                    allocated_bits: 0,
928                };
929                pass_stats.add_frame(frame_stats);
930            }
931        }
932    }
933
934    /// Finalize first pass and return statistics.
935    #[must_use]
936    pub fn finalize_first_pass(&mut self) -> Option<FirstPassStats> {
937        if self.pass != 1 {
938            return None;
939        }
940
941        if let Some(ref mut stats) = self.first_pass_stats {
942            stats.finalize();
943
944            // Calculate bit allocation for second pass
945            if let Some(total_frames) = self.total_frames {
946                let duration = total_frames as f64 / self.framerate;
947                let total_bits = (self.target_bitrate as f64 * duration) as u64;
948
949                for frame in &mut stats.frames {
950                    // Simple allocation based on complexity
951                    let complexity_ratio = if stats.total_complexity > 0.0 {
952                        frame.combined_complexity as f64 / stats.avg_complexity
953                    } else {
954                        1.0
955                    };
956
957                    frame.allocated_bits =
958                        (total_bits as f64 / total_frames as f64 * complexity_ratio) as u64;
959                }
960            }
961        }
962
963        self.first_pass_stats.take()
964    }
965
966    /// Get current average bitrate.
967    #[must_use]
968    pub fn current_bitrate(&self) -> f64 {
969        if self.frame_count == 0 || self.framerate <= 0.0 {
970            return 0.0;
971        }
972        let elapsed = self.frame_count as f64 / self.framerate;
973        self.total_bits as f64 / elapsed
974    }
975
976    /// Get target bitrate.
977    #[must_use]
978    pub fn target_bitrate(&self) -> u64 {
979        self.target_bitrate
980    }
981
982    /// Get frame count.
983    #[must_use]
984    pub fn frame_count(&self) -> u64 {
985        self.frame_count
986    }
987
988    /// Get total bits encoded.
989    #[must_use]
990    pub fn total_bits(&self) -> u64 {
991        self.total_bits
992    }
993
994    /// Get current QP.
995    #[must_use]
996    pub fn current_qp(&self) -> f32 {
997        self.current_qp
998    }
999
1000    /// Calculate projected file size based on current progress.
1001    #[must_use]
1002    pub fn projected_file_size(&self) -> Option<u64> {
1003        if let Some(total_frames) = self.total_frames {
1004            if self.frame_count > 0 && self.frame_count < total_frames {
1005                let bits_per_frame = self.total_bits as f64 / self.frame_count as f64;
1006                let projected_bits = bits_per_frame * total_frames as f64;
1007                Some((projected_bits / 8.0) as u64)
1008            } else {
1009                Some(self.total_bits / 8)
1010            }
1011        } else {
1012            None
1013        }
1014    }
1015
1016    /// Get bitrate deviation from target.
1017    #[must_use]
1018    pub fn bitrate_deviation(&self) -> f64 {
1019        let current = self.current_bitrate();
1020        if self.target_bitrate > 0 {
1021            (current - self.target_bitrate as f64) / self.target_bitrate as f64
1022        } else {
1023            0.0
1024        }
1025    }
1026
1027    /// Reset controller state.
1028    pub fn reset(&mut self) {
1029        self.frame_count = 0;
1030        self.total_bits = 0;
1031        self.current_gop = GopStats::new(0, 0);
1032        self.gop_history.clear();
1033        self.lookahead.complexity_buffer.clear();
1034    }
1035}
1036
1037impl Default for AbrController {
1038    fn default() -> Self {
1039        Self {
1040            target_bitrate: 5_000_000,
1041            target_file_size: None,
1042            total_frames: None,
1043            pass: 1,
1044            framerate: 30.0,
1045            current_qp: 28.0,
1046            min_qp: 1,
1047            max_qp: 63,
1048            i_qp_offset: -2,
1049            b_qp_offset: 2,
1050            frame_count: 0,
1051            total_bits: 0,
1052            first_pass_stats: None,
1053            allocator: MultiPassAllocator::default(),
1054            lookahead: LookaheadAnalyzer::default(),
1055            rc_model: RateControlModel::default(),
1056            curve_state: CurveCompressionState::default(),
1057            current_gop: GopStats::new(0, 0),
1058            gop_history: Vec::new(),
1059        }
1060    }
1061}
1062
1063#[cfg(test)]
1064mod tests {
1065    use super::*;
1066
1067    #[test]
1068    fn test_abr_creation() {
1069        let config = RcConfig::default();
1070        let controller = AbrController::new(&config);
1071        assert_eq!(controller.target_bitrate(), 5_000_000);
1072        assert_eq!(controller.frame_count(), 0);
1073    }
1074
1075    #[test]
1076    fn test_first_pass_collection() {
1077        let config = RcConfig::default();
1078        let mut controller = AbrController::new(&config);
1079        controller.set_pass(1);
1080
1081        for i in 0..30 {
1082            let frame_type = if i % 10 == 0 {
1083                FrameType::Key
1084            } else {
1085                FrameType::Inter
1086            };
1087
1088            let output = controller.get_rc(frame_type, 1.0);
1089
1090            let mut stats = FrameStats::new(i, frame_type);
1091            stats.bits = output.target_bits;
1092            stats.complexity = 1.0;
1093            controller.update(&stats);
1094        }
1095
1096        let first_pass = controller.finalize_first_pass().expect("should succeed");
1097        assert_eq!(first_pass.frame_count, 30);
1098    }
1099
1100    #[test]
1101    fn test_two_pass_encoding() {
1102        let config = RcConfig::default();
1103        let mut controller = AbrController::new(&config);
1104        controller.set_total_frames(60);
1105        controller.set_pass(1);
1106
1107        // First pass
1108        for i in 0..30 {
1109            let frame_type = if i % 10 == 0 {
1110                FrameType::Key
1111            } else {
1112                FrameType::Inter
1113            };
1114            let _output = controller.get_rc(frame_type, 1.0 + (i as f32 % 3.0) * 0.1);
1115
1116            let mut stats = FrameStats::new(i, frame_type);
1117            stats.bits = 100_000;
1118            stats.complexity = 1.0;
1119            controller.update(&stats);
1120        }
1121
1122        let first_pass_stats = controller.finalize_first_pass().expect("should succeed");
1123
1124        // Second pass
1125        let mut controller2 = AbrController::new(&config);
1126        controller2.set_total_frames(60);
1127        controller2.set_pass(2);
1128        controller2.set_first_pass_stats(first_pass_stats);
1129
1130        let output = controller2.get_rc(FrameType::Key, 1.0);
1131        assert!(output.target_bits > 0);
1132    }
1133
1134    #[test]
1135    fn test_target_file_size() {
1136        let config = RcConfig::default();
1137        let mut controller = AbrController::new(&config);
1138
1139        let target_size = 10_000_000; // 10 MB
1140        let duration = 60.0; // 60 seconds
1141        controller.set_target_file_size(target_size, duration);
1142
1143        // Target bitrate should be calculated from file size
1144        assert!(controller.target_bitrate() > 0);
1145    }
1146
1147    #[test]
1148    fn test_bit_allocation() {
1149        let mut allocator = MultiPassAllocator::default();
1150        allocator.initialize(10_000_000, 100);
1151
1152        let mut stats = FirstPassStats::default();
1153        for i in 0..10 {
1154            let frame_stats = FramePassStats {
1155                frame_index: i,
1156                combined_complexity: 1.0,
1157                ..Default::default()
1158            };
1159            stats.add_frame(frame_stats);
1160        }
1161        stats.finalize();
1162
1163        let bits = allocator.allocate_frame_bits(FrameType::Key, 1.5, 1.0, &stats);
1164        assert!(bits > 0);
1165    }
1166
1167    #[test]
1168    fn test_lookahead_analysis() {
1169        let mut lookahead = LookaheadAnalyzer::default();
1170
1171        for i in 0..20 {
1172            lookahead.push_complexity(1.0 + (i as f32 % 5.0) * 0.1);
1173        }
1174
1175        let analysis = lookahead.analyze();
1176        assert!(analysis.avg_complexity > 0.0);
1177        assert!(analysis.recommend_bits_multiplier > 0.0);
1178    }
1179
1180    #[test]
1181    fn test_scene_cut_detection() {
1182        let mut lookahead = LookaheadAnalyzer::default();
1183
1184        // Normal complexity
1185        for _ in 0..5 {
1186            lookahead.push_complexity(1.0);
1187        }
1188
1189        // Sudden spike (scene cut)
1190        lookahead.push_complexity(3.0);
1191
1192        let analysis = lookahead.analyze();
1193        assert!(analysis.has_scene_cut);
1194    }
1195
1196    #[test]
1197    fn test_curve_compression() {
1198        let curve = CurveCompressionState::default();
1199
1200        let high_complexity = 2.0;
1201        let low_complexity = 0.5;
1202        let avg = 1.0;
1203
1204        let compressed_high = curve.compress(high_complexity, avg);
1205        let compressed_low = curve.compress(low_complexity, avg);
1206
1207        // High complexity should be compressed (closer to avg)
1208        assert!(compressed_high < high_complexity * avg);
1209        // Low complexity should be expanded (closer to avg)
1210        assert!(compressed_low > low_complexity * avg);
1211    }
1212
1213    #[test]
1214    fn test_rate_model_prediction() {
1215        let model = RateControlModel::default();
1216
1217        let bits = model.predict(1.5, 28.0);
1218        assert!(bits > 0);
1219
1220        // Higher complexity should predict more bits
1221        let bits_high = model.predict(2.0, 28.0);
1222        assert!(bits_high > bits);
1223    }
1224
1225    #[test]
1226    fn test_linear_model() {
1227        let model = LinearModel::default();
1228
1229        let bits1 = model.predict(1.0, 28.0);
1230        let bits2 = model.predict(2.0, 28.0);
1231
1232        // Higher complexity should predict more bits
1233        assert!(bits2 > bits1);
1234    }
1235
1236    #[test]
1237    fn test_quadratic_model() {
1238        let model = QuadraticModel::default();
1239
1240        let bits1 = model.predict(1.0, 28.0);
1241        let bits2 = model.predict(2.0, 28.0);
1242
1243        assert!(bits2 > bits1);
1244    }
1245
1246    #[test]
1247    fn test_power_model() {
1248        let model = PowerModel::default();
1249
1250        let bits1 = model.predict(1.0, 28.0);
1251        let bits2 = model.predict(2.0, 28.0);
1252
1253        assert!(bits2 > bits1);
1254    }
1255
1256    #[test]
1257    fn test_projected_file_size() {
1258        let config = RcConfig::default();
1259        let mut controller = AbrController::new(&config);
1260        controller.set_total_frames(100);
1261
1262        // Encode some frames
1263        for i in 0..25 {
1264            let mut stats = FrameStats::new(i, FrameType::Inter);
1265            stats.bits = 100_000;
1266            controller.update(&stats);
1267        }
1268
1269        let projected = controller.projected_file_size();
1270        assert!(projected.is_some());
1271        assert!(projected.expect("should succeed") > 0);
1272    }
1273
1274    #[test]
1275    fn test_bitrate_deviation() {
1276        let config = RcConfig::default();
1277        let mut controller = AbrController::new(&config);
1278
1279        for i in 0..30 {
1280            let mut stats = FrameStats::new(i, FrameType::Inter);
1281            stats.bits = 200_000; // Higher than target
1282            controller.update(&stats);
1283        }
1284
1285        let deviation = controller.bitrate_deviation();
1286        // Should be positive (over target)
1287        assert!(deviation > 0.0);
1288    }
1289
1290    #[test]
1291    fn test_reset() {
1292        let config = RcConfig::default();
1293        let mut controller = AbrController::new(&config);
1294
1295        for i in 0..10 {
1296            let mut stats = FrameStats::new(i, FrameType::Inter);
1297            stats.bits = 100_000;
1298            controller.update(&stats);
1299        }
1300
1301        controller.reset();
1302        assert_eq!(controller.frame_count(), 0);
1303        assert_eq!(controller.total_bits(), 0);
1304    }
1305}