Skip to main content

oximedia_codec/rate_control/
vbr.rs

1//! Variable Bitrate rate control with closed-loop feedback.
2//!
3//! VBR (Variable Bitrate) allows the bitrate to fluctuate within defined
4//! bounds while targeting an average bitrate. This implementation provides:
5//!
6//! - Closed-loop feedback control for accurate bitrate targeting
7//! - VBV (Video Buffering Verifier) and HRD compliance
8//! - Advanced frame complexity analysis
9//! - Lookahead buffer for optimal bit allocation
10//! - Adaptive quantization integration
11//! - Multi-pass encoding support
12//!
13//! # Architecture
14//!
15//! The VBR controller operates in a closed-loop fashion:
16//!
17//! ```text
18//! Frame → Complexity → Bit → QP → Encode → Update → Next
19//!         Analysis     Alloc   Calc          Stats    Frame
20//!            ↓           ↓       ↓              ↓       ↓
21//!         Lookahead   Model   Lambda        Feedback  Loop
22//! ```
23
24#![allow(clippy::cast_lossless)]
25#![allow(clippy::cast_precision_loss)]
26#![allow(clippy::cast_possible_truncation)]
27#![allow(clippy::cast_sign_loss)]
28#![allow(clippy::cast_possible_wrap)]
29#![allow(clippy::too_many_arguments)]
30#![allow(clippy::struct_excessive_bools)]
31#![forbid(unsafe_code)]
32
33use crate::frame::FrameType;
34
35use super::buffer::BufferModel;
36use super::types::{FrameStats, GopStats, RcConfig, RcOutput};
37
38/// Variable Bitrate rate controller with closed-loop feedback.
39#[derive(Clone, Debug)]
40pub struct VbrController {
41    /// Target bitrate in bits per second.
42    target_bitrate: u64,
43    /// Maximum bitrate in bits per second.
44    max_bitrate: u64,
45    /// Minimum bitrate in bits per second.
46    min_bitrate: u64,
47    /// Current QP (floating point for smoother adjustments).
48    current_qp: f32,
49    /// Minimum QP.
50    min_qp: u8,
51    /// Maximum QP.
52    max_qp: u8,
53    /// I-frame QP offset.
54    i_qp_offset: i8,
55    /// B-frame QP offset.
56    b_qp_offset: i8,
57    /// Frame rate.
58    framerate: f64,
59    /// GOP length.
60    gop_length: u32,
61    /// Frame counter.
62    frame_count: u64,
63    /// Total bits encoded.
64    total_bits: u64,
65    /// Current GOP statistics.
66    current_gop: GopStats,
67    /// Historical GOP statistics for analysis.
68    gop_history: Vec<GopStats>,
69    /// Maximum GOP history size.
70    max_gop_history: usize,
71    /// Encoding pass (0 = single pass, 1 = first pass, 2 = second pass).
72    pass: u8,
73    /// First pass complexity data.
74    first_pass_data: Option<FirstPassData>,
75    /// Quality vs bitrate stability factor (0.0-1.0).
76    quality_stability: f32,
77    /// Bit reservoir (accumulated bits for averaging).
78    bit_reservoir: i64,
79    /// Maximum reservoir size.
80    max_reservoir: i64,
81    /// VBV buffer model for HRD compliance.
82    vbv_buffer: Option<BufferModel>,
83    /// Enable VBV/HRD compliance.
84    enable_vbv: bool,
85    /// Lookahead buffer size.
86    lookahead_size: usize,
87    /// Lookahead frame data.
88    lookahead_frames: Vec<LookaheadFrameData>,
89    /// Rate prediction model.
90    rate_model: RatePredictionModel,
91    /// Adaptive GOP sizing enabled.
92    adaptive_gop: bool,
93    /// Scene change detection threshold.
94    scene_change_threshold: f32,
95    /// Enable adaptive quantization.
96    enable_aq: bool,
97    /// AQ strength.
98    #[allow(dead_code)]
99    aq_strength: f32,
100    /// PID controller state.
101    pid_state: PidControllerState,
102    /// Rate-distortion optimization parameters.
103    rdo_params: RdoParameters,
104    /// Frame type decision state.
105    frame_type_state: FrameTypeDecisionState,
106}
107
108/// First pass analysis data.
109#[derive(Clone, Debug, Default)]
110pub struct FirstPassData {
111    /// Per-frame complexity values.
112    pub frame_complexity: Vec<f32>,
113    /// Per-frame spatial complexity.
114    pub spatial_complexity: Vec<f32>,
115    /// Per-frame temporal complexity.
116    pub temporal_complexity: Vec<f32>,
117    /// Per-GOP total complexity.
118    pub gop_complexity: Vec<f32>,
119    /// Total complexity for the entire sequence.
120    pub total_complexity: f32,
121    /// Frame count.
122    pub frame_count: u64,
123    /// Suggested bits per frame based on complexity.
124    pub suggested_bits: Vec<u64>,
125    /// Scene change frame indices.
126    pub scene_changes: Vec<u64>,
127    /// Optimal GOP boundaries.
128    pub gop_boundaries: Vec<u64>,
129    /// Per-frame QP recommendations.
130    pub recommended_qp: Vec<f32>,
131}
132
133impl FirstPassData {
134    /// Add a frame's complexity data.
135    pub fn add_frame(&mut self, spatial: f32, temporal: f32, combined: f32) {
136        self.frame_complexity.push(combined);
137        self.spatial_complexity.push(spatial);
138        self.temporal_complexity.push(temporal);
139        self.total_complexity += combined;
140        self.frame_count += 1;
141    }
142
143    /// Mark a frame as scene change.
144    pub fn mark_scene_change(&mut self, frame_num: u64) {
145        self.scene_changes.push(frame_num);
146    }
147
148    /// Add GOP boundary.
149    pub fn add_gop_boundary(&mut self, frame_num: u64) {
150        self.gop_boundaries.push(frame_num);
151    }
152
153    /// Finalize a GOP's complexity.
154    pub fn finalize_gop(&mut self) {
155        let gop_start = self
156            .gop_complexity
157            .last()
158            .map(|_| self.gop_boundaries.last().copied().unwrap_or(0))
159            .unwrap_or(0) as usize;
160
161        let gop_sum: f32 = self
162            .frame_complexity
163            .get(gop_start..)
164            .map(|slice| slice.iter().sum())
165            .unwrap_or(0.0);
166
167        self.gop_complexity.push(gop_sum);
168    }
169
170    /// Calculate suggested bit allocation for second pass.
171    pub fn calculate_bit_allocation(&mut self, total_bits: u64) {
172        if self.total_complexity <= 0.0 || self.frame_complexity.is_empty() {
173            return;
174        }
175
176        let bits_per_complexity = total_bits as f64 / self.total_complexity as f64;
177
178        self.suggested_bits = self
179            .frame_complexity
180            .iter()
181            .map(|c| ((*c as f64) * bits_per_complexity) as u64)
182            .collect();
183
184        // Calculate recommended QP based on complexity
185        let avg_complexity = self.total_complexity / self.frame_count as f32;
186
187        for complexity in &self.frame_complexity {
188            let complexity_ratio = complexity / avg_complexity;
189            // Higher complexity → higher QP to save bits
190            let qp_adjustment = (complexity_ratio - 1.0) * 4.0;
191            let base_qp = 28.0;
192            let recommended = (base_qp + qp_adjustment).clamp(18.0, 51.0);
193            self.recommended_qp.push(recommended);
194        }
195    }
196
197    /// Get suggested bits for a frame.
198    #[must_use]
199    pub fn get_suggested_bits(&self, frame_num: u64) -> Option<u64> {
200        self.suggested_bits.get(frame_num as usize).copied()
201    }
202
203    /// Get recommended QP for a frame.
204    #[must_use]
205    pub fn get_recommended_qp(&self, frame_num: u64) -> Option<f32> {
206        self.recommended_qp.get(frame_num as usize).copied()
207    }
208
209    /// Check if frame is a detected scene change.
210    #[must_use]
211    pub fn is_scene_change(&self, frame_num: u64) -> bool {
212        self.scene_changes.contains(&frame_num)
213    }
214}
215
216/// Lookahead frame data for rate control.
217#[derive(Clone, Debug, Default)]
218struct LookaheadFrameData {
219    /// Frame index.
220    #[allow(dead_code)]
221    frame_index: u64,
222    /// Spatial complexity.
223    #[allow(dead_code)]
224    spatial_complexity: f32,
225    /// Temporal complexity.
226    #[allow(dead_code)]
227    temporal_complexity: f32,
228    /// Combined complexity.
229    combined_complexity: f32,
230    /// Is scene change.
231    is_scene_change: bool,
232    /// Predicted frame type.
233    #[allow(dead_code)]
234    predicted_type: FrameType,
235    /// Predicted bits needed.
236    #[allow(dead_code)]
237    predicted_bits: u64,
238}
239
240/// Rate prediction model using multiple regression approaches.
241#[derive(Clone, Debug)]
242struct RatePredictionModel {
243    /// Linear model coefficients: bits = a * complexity + b
244    linear_a: f64,
245    linear_b: f64,
246    /// Quadratic model coefficients: bits = a * complexity^2 + b * complexity + c
247    quad_a: f64,
248    quad_b: f64,
249    quad_c: f64,
250    /// Power model coefficients: bits = a * complexity^b
251    power_a: f64,
252    power_b: f64,
253    /// Model selection (0 = linear, 1 = quadratic, 2 = power)
254    active_model: u8,
255    /// Historical data for model fitting.
256    history_complexity: Vec<f32>,
257    history_bits: Vec<u64>,
258    /// Maximum history size.
259    max_history: usize,
260    /// Model update counter.
261    update_count: u32,
262}
263
264impl Default for RatePredictionModel {
265    fn default() -> Self {
266        Self {
267            linear_a: 100_000.0,
268            linear_b: 50_000.0,
269            quad_a: 10_000.0,
270            quad_b: 50_000.0,
271            quad_c: 20_000.0,
272            power_a: 100_000.0,
273            power_b: 1.2,
274            active_model: 0,
275            history_complexity: Vec::new(),
276            history_bits: Vec::new(),
277            max_history: 100,
278            update_count: 0,
279        }
280    }
281}
282
283impl RatePredictionModel {
284    /// Predict bits needed for given complexity.
285    fn predict(&self, complexity: f32) -> u64 {
286        if complexity <= 0.0 {
287            return 50_000;
288        }
289
290        let prediction = match self.active_model {
291            0 => self.predict_linear(complexity),
292            1 => self.predict_quadratic(complexity),
293            2 => self.predict_power(complexity),
294            _ => self.predict_linear(complexity),
295        };
296
297        prediction.max(1000.0) as u64
298    }
299
300    /// Linear prediction.
301    fn predict_linear(&self, complexity: f32) -> f64 {
302        self.linear_a * complexity as f64 + self.linear_b
303    }
304
305    /// Quadratic prediction.
306    fn predict_quadratic(&self, complexity: f32) -> f64 {
307        let c = complexity as f64;
308        self.quad_a * c * c + self.quad_b * c + self.quad_c
309    }
310
311    /// Power prediction.
312    fn predict_power(&self, complexity: f32) -> f64 {
313        self.power_a * (complexity as f64).powf(self.power_b)
314    }
315
316    /// Update model with new observation.
317    fn update(&mut self, complexity: f32, bits: u64) {
318        self.history_complexity.push(complexity);
319        self.history_bits.push(bits);
320
321        if self.history_complexity.len() > self.max_history {
322            self.history_complexity.remove(0);
323            self.history_bits.remove(0);
324        }
325
326        // Update models periodically
327        self.update_count += 1;
328        if self.update_count >= 10 {
329            self.fit_models();
330            self.update_count = 0;
331        }
332    }
333
334    /// Fit all models using historical data.
335    fn fit_models(&mut self) {
336        if self.history_complexity.len() < 5 {
337            return;
338        }
339
340        self.fit_linear_model();
341        self.fit_quadratic_model();
342        self.fit_power_model();
343        self.select_best_model();
344    }
345
346    /// Fit linear model using least squares.
347    fn fit_linear_model(&mut self) {
348        let n = self.history_complexity.len();
349        let mut sum_x = 0.0;
350        let mut sum_y = 0.0;
351        let mut sum_xy = 0.0;
352        let mut sum_xx = 0.0;
353
354        for i in 0..n {
355            let x = self.history_complexity[i] as f64;
356            let y = self.history_bits[i] as f64;
357            sum_x += x;
358            sum_y += y;
359            sum_xy += x * y;
360            sum_xx += x * x;
361        }
362
363        let n_f = n as f64;
364        let denominator = n_f * sum_xx - sum_x * sum_x;
365
366        if denominator.abs() > 1e-6 {
367            self.linear_a = (n_f * sum_xy - sum_x * sum_y) / denominator;
368            self.linear_b = (sum_y - self.linear_a * sum_x) / n_f;
369        }
370    }
371
372    /// Fit quadratic model (simplified).
373    fn fit_quadratic_model(&mut self) {
374        // Simplified quadratic fitting
375        // In practice, would use proper polynomial regression
376        let n = self.history_complexity.len();
377        if n < 3 {
378            return;
379        }
380
381        let mut sum_x = 0.0;
382        let mut sum_y = 0.0;
383        let mut sum_x2 = 0.0;
384        let mut sum_xy = 0.0;
385
386        for i in 0..n {
387            let x = self.history_complexity[i] as f64;
388            let y = self.history_bits[i] as f64;
389            sum_x += x;
390            sum_y += y;
391            sum_x2 += x * x;
392            sum_xy += x * y;
393        }
394
395        let n_f = n as f64;
396        let avg_x = sum_x / n_f;
397        let avg_y = sum_y / n_f;
398
399        // Simplified quadratic coefficients
400        self.quad_b = self.linear_a;
401        self.quad_a = (sum_xy - n_f * avg_x * avg_y) / (sum_x2 - n_f * avg_x * avg_x) * 0.1;
402        self.quad_c = avg_y - self.quad_b * avg_x - self.quad_a * avg_x * avg_x;
403    }
404
405    /// Fit power model using log transformation.
406    fn fit_power_model(&mut self) {
407        let n = self.history_complexity.len();
408        let mut sum_log_x = 0.0;
409        let mut sum_log_y = 0.0;
410        let mut sum_log_xy = 0.0;
411        let mut sum_log_xx = 0.0;
412        let mut count = 0;
413
414        for i in 0..n {
415            let x = self.history_complexity[i] as f64;
416            let y = self.history_bits[i] as f64;
417
418            if x > 0.0 && y > 0.0 {
419                let log_x = x.ln();
420                let log_y = y.ln();
421                sum_log_x += log_x;
422                sum_log_y += log_y;
423                sum_log_xy += log_x * log_y;
424                sum_log_xx += log_x * log_x;
425                count += 1;
426            }
427        }
428
429        if count < 3 {
430            return;
431        }
432
433        let n_f = count as f64;
434        let denominator = n_f * sum_log_xx - sum_log_x * sum_log_x;
435
436        if denominator.abs() > 1e-6 {
437            self.power_b = (n_f * sum_log_xy - sum_log_x * sum_log_y) / denominator;
438            let log_a = (sum_log_y - self.power_b * sum_log_x) / n_f;
439            self.power_a = log_a.exp();
440        }
441    }
442
443    /// Select the best model based on prediction error.
444    fn select_best_model(&mut self) {
445        if self.history_complexity.len() < 5 {
446            return;
447        }
448
449        let mut error_linear = 0.0;
450        let mut error_quadratic = 0.0;
451        let mut error_power = 0.0;
452
453        for i in 0..self.history_complexity.len() {
454            let complexity = self.history_complexity[i];
455            let actual_bits = self.history_bits[i] as f64;
456
457            let pred_linear = self.predict_linear(complexity);
458            let pred_quadratic = self.predict_quadratic(complexity);
459            let pred_power = self.predict_power(complexity);
460
461            error_linear += (actual_bits - pred_linear).abs();
462            error_quadratic += (actual_bits - pred_quadratic).abs();
463            error_power += (actual_bits - pred_power).abs();
464        }
465
466        // Select model with lowest error
467        if error_linear <= error_quadratic && error_linear <= error_power {
468            self.active_model = 0;
469        } else if error_quadratic <= error_power {
470            self.active_model = 1;
471        } else {
472            self.active_model = 2;
473        }
474    }
475}
476
477/// PID controller state for closed-loop rate control.
478#[derive(Clone, Debug, Default)]
479struct PidControllerState {
480    /// Proportional gain.
481    kp: f32,
482    /// Integral gain.
483    ki: f32,
484    /// Derivative gain.
485    kd: f32,
486    /// Previous error.
487    prev_error: f32,
488    /// Accumulated error (integral term).
489    integral: f32,
490    /// Maximum integral windup.
491    max_integral: f32,
492}
493
494impl PidControllerState {
495    /// Create new PID controller with default gains.
496    fn new() -> Self {
497        Self {
498            kp: 0.5,
499            ki: 0.1,
500            kd: 0.05,
501            prev_error: 0.0,
502            integral: 0.0,
503            max_integral: 10.0,
504        }
505    }
506
507    /// Calculate PID output.
508    fn calculate(&mut self, error: f32) -> f32 {
509        // Proportional term
510        let p_term = self.kp * error;
511
512        // Integral term with anti-windup
513        self.integral += error;
514        self.integral = self.integral.clamp(-self.max_integral, self.max_integral);
515        let i_term = self.ki * self.integral;
516
517        // Derivative term
518        let d_term = self.kd * (error - self.prev_error);
519        self.prev_error = error;
520
521        p_term + i_term + d_term
522    }
523
524    /// Reset controller state.
525    fn reset(&mut self) {
526        self.prev_error = 0.0;
527        self.integral = 0.0;
528    }
529}
530
531/// Rate-distortion optimization parameters.
532#[derive(Clone, Debug)]
533struct RdoParameters {
534    /// Base lambda value.
535    base_lambda: f64,
536    /// Lambda multiplier for I-frames.
537    i_lambda_mult: f64,
538    /// Lambda multiplier for B-frames.
539    b_lambda_mult: f64,
540    /// Enable psychovisual RDO.
541    #[allow(dead_code)]
542    psy_rd: bool,
543    /// Psychovisual strength.
544    #[allow(dead_code)]
545    psy_strength: f64,
546}
547
548impl Default for RdoParameters {
549    fn default() -> Self {
550        Self {
551            base_lambda: 1.0,
552            i_lambda_mult: 0.6,
553            b_lambda_mult: 1.4,
554            psy_rd: true,
555            psy_strength: 1.0,
556        }
557    }
558}
559
560impl RdoParameters {
561    /// Calculate lambda for given QP and frame type.
562    fn calculate_lambda(&self, qp: f32, frame_type: FrameType) -> f64 {
563        let base = 0.85 * 2.0_f64.powf((f64::from(qp) - 12.0) / 3.0);
564
565        let multiplier = match frame_type {
566            FrameType::Key => self.i_lambda_mult,
567            FrameType::BiDir => self.b_lambda_mult,
568            _ => 1.0,
569        };
570
571        base * multiplier * self.base_lambda
572    }
573
574    /// Calculate motion estimation lambda.
575    fn calculate_lambda_me(&self, lambda: f64) -> f64 {
576        lambda.sqrt()
577    }
578}
579
580/// Frame type decision state.
581#[derive(Clone, Debug, Default)]
582struct FrameTypeDecisionState {
583    /// Frames since last keyframe.
584    frames_since_keyframe: u32,
585    /// Consecutive B-frames count.
586    consecutive_b_frames: u32,
587    /// Maximum consecutive B-frames.
588    max_b_frames: u32,
589    /// Force next keyframe.
590    force_keyframe: bool,
591}
592
593impl FrameTypeDecisionState {
594    /// Create new frame type decision state.
595    fn new(max_b_frames: u32) -> Self {
596        Self {
597            frames_since_keyframe: 0,
598            consecutive_b_frames: 0,
599            max_b_frames,
600            force_keyframe: false,
601        }
602    }
603
604    /// Decide frame type based on GOP structure and scene changes.
605    fn decide_type(
606        &mut self,
607        gop_length: u32,
608        is_scene_change: bool,
609        adaptive_gop: bool,
610    ) -> FrameType {
611        // Check for forced keyframe or GOP boundary
612        if self.force_keyframe
613            || self.frames_since_keyframe == 0
614            || (!adaptive_gop && self.frames_since_keyframe >= gop_length)
615            || is_scene_change
616        {
617            self.frames_since_keyframe = 1;
618            self.consecutive_b_frames = 0;
619            self.force_keyframe = false;
620            return FrameType::Key;
621        }
622
623        self.frames_since_keyframe += 1;
624
625        // Adaptive B-frame decision
626        if self.max_b_frames > 0 && self.consecutive_b_frames < self.max_b_frames {
627            // Use B-frames for middle frames in mini-GOP
628            let mini_gop_pos = self.frames_since_keyframe % (self.max_b_frames + 1);
629            if mini_gop_pos > 0 && mini_gop_pos <= self.max_b_frames {
630                self.consecutive_b_frames += 1;
631                return FrameType::BiDir;
632            }
633        }
634
635        self.consecutive_b_frames = 0;
636        FrameType::Inter
637    }
638
639    /// Force next frame to be keyframe.
640    fn force_next_keyframe(&mut self) {
641        self.force_keyframe = true;
642    }
643}
644
645impl VbrController {
646    /// Create a new VBR controller from configuration.
647    #[must_use]
648    pub fn new(config: &RcConfig) -> Self {
649        let max_bitrate = config.max_bitrate.unwrap_or(config.target_bitrate * 2);
650        let min_bitrate = config.min_bitrate.unwrap_or(config.target_bitrate / 4);
651        let framerate = config.framerate();
652
653        let vbv_buffer = if config.buffer_size > 0 {
654            Some(BufferModel::new(
655                config.buffer_size,
656                config.target_bitrate,
657                framerate,
658                config.initial_buffer_fullness as f64,
659            ))
660        } else {
661            None
662        };
663
664        Self {
665            target_bitrate: config.target_bitrate,
666            max_bitrate,
667            min_bitrate,
668            current_qp: config.initial_qp as f32,
669            min_qp: config.min_qp,
670            max_qp: config.max_qp,
671            i_qp_offset: config.i_qp_offset,
672            b_qp_offset: config.b_qp_offset,
673            framerate,
674            gop_length: config.gop_length,
675            frame_count: 0,
676            total_bits: 0,
677            current_gop: GopStats::new(0, 0),
678            gop_history: Vec::new(),
679            max_gop_history: 10,
680            pass: 0,
681            first_pass_data: None,
682            quality_stability: 0.5,
683            bit_reservoir: 0,
684            max_reservoir: (config.target_bitrate as i64) * 2,
685            vbv_buffer,
686            enable_vbv: config.buffer_size > 0,
687            lookahead_size: config.lookahead_depth.min(250),
688            lookahead_frames: Vec::new(),
689            rate_model: RatePredictionModel::default(),
690            adaptive_gop: true,
691            scene_change_threshold: config.scene_cut_threshold,
692            enable_aq: config.enable_aq,
693            aq_strength: config.aq_strength,
694            pid_state: PidControllerState::new(),
695            rdo_params: RdoParameters::default(),
696            frame_type_state: FrameTypeDecisionState::new(3),
697        }
698    }
699
700    /// Set the encoding pass (0 = single, 1 = first, 2 = second).
701    pub fn set_pass(&mut self, pass: u8) {
702        self.pass = pass.min(2);
703        if pass == 1 {
704            self.first_pass_data = Some(FirstPassData::default());
705        }
706    }
707
708    /// Set quality stability factor (0.0-1.0).
709    pub fn set_quality_stability(&mut self, stability: f32) {
710        self.quality_stability = stability.clamp(0.0, 1.0);
711    }
712
713    /// Enable or disable VBV/HRD compliance.
714    pub fn set_vbv_enabled(&mut self, enabled: bool) {
715        self.enable_vbv = enabled;
716    }
717
718    /// Set lookahead buffer size (10-250 frames).
719    pub fn set_lookahead_size(&mut self, size: usize) {
720        self.lookahead_size = size.clamp(10, 250);
721        self.lookahead_frames.reserve(self.lookahead_size);
722    }
723
724    /// Enable or disable adaptive GOP sizing.
725    pub fn set_adaptive_gop(&mut self, enabled: bool) {
726        self.adaptive_gop = enabled;
727    }
728
729    /// Set scene change detection threshold.
730    pub fn set_scene_change_threshold(&mut self, threshold: f32) {
731        self.scene_change_threshold = threshold.clamp(0.0, 1.0);
732    }
733
734    /// Import first pass data for second pass encoding.
735    pub fn set_first_pass_data(&mut self, data: FirstPassData) {
736        self.first_pass_data = Some(data);
737    }
738
739    /// Add frame to lookahead buffer.
740    pub fn push_lookahead_frame(
741        &mut self,
742        spatial: f32,
743        temporal: f32,
744        combined: f32,
745        is_scene_change: bool,
746    ) {
747        let frame_data = LookaheadFrameData {
748            frame_index: self.frame_count + self.lookahead_frames.len() as u64,
749            spatial_complexity: spatial,
750            temporal_complexity: temporal,
751            combined_complexity: combined,
752            is_scene_change,
753            predicted_type: FrameType::Inter,
754            predicted_bits: self.rate_model.predict(combined),
755        };
756
757        self.lookahead_frames.push(frame_data);
758
759        // Trim to size
760        if self.lookahead_frames.len() > self.lookahead_size {
761            self.lookahead_frames.remove(0);
762        }
763    }
764
765    /// Get rate control output for a frame with closed-loop feedback.
766    #[must_use]
767    pub fn get_rc(&mut self, frame_type: FrameType, complexity: f32) -> RcOutput {
768        // Determine frame type using lookahead and adaptive GOP
769        let is_scene_change = self.detect_scene_change_from_lookahead();
770        let actual_frame_type =
771            self.frame_type_state
772                .decide_type(self.gop_length, is_scene_change, self.adaptive_gop);
773
774        // Use provided frame_type if it's more restrictive (Key)
775        let final_frame_type = if frame_type == FrameType::Key {
776            FrameType::Key
777        } else {
778            actual_frame_type
779        };
780
781        // Calculate target bits using multiple strategies
782        let target_bits = self.calculate_target_bits(final_frame_type, complexity);
783
784        // Closed-loop QP adjustment using PID controller
785        let qp_adjustment = self.calculate_closed_loop_qp_adjustment();
786        let adjusted_qp = self.current_qp + qp_adjustment;
787
788        // Apply frame type offset
789        let offset = match final_frame_type {
790            FrameType::Key => self.i_qp_offset,
791            FrameType::BiDir => self.b_qp_offset,
792            FrameType::Inter | FrameType::Switch => 0,
793        };
794
795        // Apply complexity-based adjustment
796        let complexity_adjustment = self.calculate_complexity_adjustment(complexity);
797
798        // Apply VBV buffer constraint if enabled
799        let vbv_adjustment = if self.enable_vbv {
800            self.calculate_vbv_qp_adjustment()
801        } else {
802            0.0
803        };
804
805        let final_qp = (adjusted_qp + offset as f32 + complexity_adjustment + vbv_adjustment)
806            .clamp(self.min_qp as f32, self.max_qp as f32);
807        let qp = final_qp.round() as u8;
808
809        // Calculate bit limits with VBV constraints
810        let (min_bits, max_bits) = self.calculate_bit_limits(final_frame_type, target_bits);
811
812        // Calculate lambda for RDO
813        let lambda = self.rdo_params.calculate_lambda(final_qp, final_frame_type);
814        let lambda_me = self.rdo_params.calculate_lambda_me(lambda);
815
816        let mut output = RcOutput {
817            qp,
818            qp_f: final_qp,
819            target_bits,
820            min_bits,
821            max_bits,
822            lambda,
823            lambda_me,
824            force_keyframe: final_frame_type == FrameType::Key && is_scene_change,
825            ..Default::default()
826        };
827
828        // Apply adaptive quantization if enabled
829        if self.enable_aq {
830            output.qp_offsets = Some(self.calculate_aq_offsets(final_qp));
831        }
832
833        output
834    }
835
836    /// Detect scene change from lookahead buffer.
837    fn detect_scene_change_from_lookahead(&self) -> bool {
838        if self.lookahead_frames.is_empty() {
839            return false;
840        }
841
842        // Check current frame in lookahead
843        self.lookahead_frames
844            .first()
845            .map(|f| f.is_scene_change)
846            .unwrap_or(false)
847    }
848
849    /// Calculate target bits for a frame with multiple strategies.
850    fn calculate_target_bits(&self, frame_type: FrameType, complexity: f32) -> u64 {
851        let base_target = self.bits_per_frame_at_bitrate(self.target_bitrate);
852
853        // Check if we have second pass data
854        if self.pass == 2 {
855            if let Some(ref data) = self.first_pass_data {
856                if let Some(suggested) = data.get_suggested_bits(self.frame_count) {
857                    return suggested;
858                }
859            }
860        }
861
862        // Use rate prediction model
863        let model_prediction = self.rate_model.predict(complexity);
864
865        // Frame type multiplier
866        let type_multiplier = match frame_type {
867            FrameType::Key => 3.0,
868            FrameType::Inter => 1.0,
869            FrameType::BiDir => 0.5,
870            FrameType::Switch => 1.5,
871        };
872
873        // Lookahead-based adjustment
874        let lookahead_mult = self.calculate_lookahead_multiplier();
875
876        // Complexity-based adjustment
877        let complexity_multiplier: f64 = if complexity > 0.0 {
878            (f64::from(complexity) / f64::from(self.average_complexity())).clamp(0.5, 2.0)
879        } else {
880            1.0
881        };
882
883        // Reservoir adjustment
884        let reservoir_adjustment = self.calculate_reservoir_adjustment();
885
886        // Combine predictions
887        let target =
888            (base_target as f64 * type_multiplier * complexity_multiplier * lookahead_mult)
889                .max(model_prediction as f64)
890                + reservoir_adjustment;
891
892        let adjusted_target = target.max(base_target as f64 / 4.0);
893
894        adjusted_target as u64
895    }
896
897    /// Calculate lookahead-based bitrate multiplier.
898    fn calculate_lookahead_multiplier(&self) -> f64 {
899        if self.lookahead_frames.is_empty() {
900            return 1.0;
901        }
902
903        // Calculate average complexity in lookahead window
904        let avg_future_complexity: f32 = self
905            .lookahead_frames
906            .iter()
907            .map(|f| f.combined_complexity)
908            .sum::<f32>()
909            / self.lookahead_frames.len() as f32;
910
911        let current_complexity = self
912            .lookahead_frames
913            .first()
914            .map(|f| f.combined_complexity)
915            .unwrap_or(1.0);
916
917        // Adjust based on future complexity trend
918        if avg_future_complexity > current_complexity * 1.3 {
919            // Future is more complex, save bits now
920            0.9
921        } else if avg_future_complexity < current_complexity * 0.7 {
922            // Future is simpler, spend more bits now
923            1.1
924        } else {
925            1.0
926        }
927    }
928
929    /// Calculate closed-loop QP adjustment using PID controller.
930    fn calculate_closed_loop_qp_adjustment(&mut self) -> f32 {
931        if self.frame_count == 0 {
932            return 0.0;
933        }
934
935        let elapsed_time = self.frame_count as f64 / self.framerate;
936        if elapsed_time <= 0.0 {
937            return 0.0;
938        }
939
940        let actual_bitrate = self.total_bits as f64 / elapsed_time;
941        let error = (actual_bitrate - self.target_bitrate as f64) / self.target_bitrate as f64;
942
943        // Use PID controller for smooth adjustment
944        let pid_output = self.pid_state.calculate(error as f32);
945
946        // Scale by quality stability
947        pid_output * (1.0 - self.quality_stability)
948    }
949
950    /// Calculate QP adjustment based on frame complexity.
951    fn calculate_complexity_adjustment(&self, complexity: f32) -> f32 {
952        let avg = self.average_complexity();
953        if avg <= 0.0 {
954            return 0.0;
955        }
956
957        let ratio = complexity / avg;
958
959        // Higher complexity frames get slightly higher QP to save bits
960        // Lower complexity frames get slightly lower QP for better quality
961        let adjustment = (ratio - 1.0) * 2.0 * self.quality_stability;
962        adjustment.clamp(-2.0, 2.0)
963    }
964
965    /// Calculate VBV buffer-based QP adjustment.
966    fn calculate_vbv_qp_adjustment(&self) -> f32 {
967        if let Some(ref buffer) = self.vbv_buffer {
968            let fullness = buffer.fullness();
969
970            // If buffer is too full, increase QP to reduce bitrate
971            // If buffer is too empty, decrease QP to increase bitrate
972            let target_fullness = 0.5;
973            let error = fullness - target_fullness;
974
975            // Aggressive adjustment when near overflow/underflow
976            if error > 0.3 {
977                // Buffer nearly full, strongly increase QP
978                (error - 0.3) * 20.0
979            } else if error < -0.3 {
980                // Buffer nearly empty, strongly decrease QP
981                (error + 0.3) * 20.0
982            } else {
983                // Normal range, gentle adjustment
984                error * 5.0
985            }
986        } else {
987            0.0
988        }
989    }
990
991    /// Calculate bit limits with VBV constraints.
992    fn calculate_bit_limits(&self, frame_type: FrameType, target_bits: u64) -> (u64, u64) {
993        let base_min = self.bits_per_frame_at_bitrate(self.min_bitrate) / 4;
994        let base_max = self.bits_per_frame_at_bitrate(self.max_bitrate) * 4;
995
996        let mut min_bits = base_min;
997        let mut max_bits = base_max;
998
999        // Apply VBV constraints
1000        if self.enable_vbv {
1001            if let Some(ref buffer) = self.vbv_buffer {
1002                let available = buffer.max_frame_bits();
1003                max_bits = max_bits.min(available);
1004
1005                // Ensure we don't underflow
1006                let min_to_prevent_underflow = target_bits / 2;
1007                min_bits = min_bits.max(min_to_prevent_underflow);
1008            }
1009        }
1010
1011        // Frame type specific limits
1012        match frame_type {
1013            FrameType::Key => {
1014                // I-frames can be much larger
1015                max_bits = max_bits.max(target_bits * 5);
1016            }
1017            FrameType::BiDir => {
1018                // B-frames should be smaller
1019                max_bits = max_bits.min(target_bits * 2);
1020            }
1021            _ => {}
1022        }
1023
1024        (min_bits, max_bits)
1025    }
1026
1027    /// Calculate AQ offsets (simplified placeholder).
1028    fn calculate_aq_offsets(&self, _base_qp: f32) -> Vec<f32> {
1029        // In a full implementation, this would analyze the frame
1030        // and return per-block QP offsets
1031        Vec::new()
1032    }
1033
1034    /// Calculate reservoir adjustment (bits to borrow or save).
1035    fn calculate_reservoir_adjustment(&self) -> f64 {
1036        let target_per_frame = self.bits_per_frame_at_bitrate(self.target_bitrate);
1037
1038        // Slowly use or build reservoir
1039        let reservoir_factor = self.bit_reservoir as f64 / self.max_reservoir as f64;
1040        reservoir_factor * (target_per_frame as f64 * 0.1)
1041    }
1042
1043    /// Calculate target bits per frame at a given bitrate.
1044    fn bits_per_frame_at_bitrate(&self, bitrate: u64) -> u64 {
1045        if self.framerate <= 0.0 {
1046            return 0;
1047        }
1048        (bitrate as f64 / self.framerate) as u64
1049    }
1050
1051    /// Get average complexity from history.
1052    fn average_complexity(&self) -> f32 {
1053        if self.gop_history.is_empty() {
1054            return 1.0;
1055        }
1056
1057        let total: f32 = self.gop_history.iter().map(|g| g.average_complexity).sum();
1058        (total / self.gop_history.len() as f32).max(0.01)
1059    }
1060
1061    /// Update controller with frame statistics (closed-loop feedback).
1062    pub fn update(&mut self, stats: &FrameStats) {
1063        self.frame_count += 1;
1064        self.total_bits += stats.bits;
1065
1066        // Update bit reservoir
1067        let target = self.bits_per_frame_at_bitrate(self.target_bitrate);
1068        self.bit_reservoir += target as i64 - stats.bits as i64;
1069        self.bit_reservoir = self
1070            .bit_reservoir
1071            .clamp(-self.max_reservoir, self.max_reservoir);
1072
1073        // Update VBV buffer
1074        if self.enable_vbv {
1075            if let Some(ref mut buffer) = self.vbv_buffer {
1076                buffer.fill_for_frame();
1077                buffer.remove_frame_bits(stats.bits);
1078            }
1079        }
1080
1081        // Update rate prediction model
1082        self.rate_model.update(stats.complexity, stats.bits);
1083
1084        // Update current GOP
1085        self.current_gop.add_frame(stats.clone());
1086
1087        // Check for GOP boundary
1088        if stats.frame_type == FrameType::Key && self.current_gop.frame_count > 1 {
1089            self.finalize_gop();
1090        }
1091
1092        // First pass data collection
1093        if self.pass == 1 {
1094            if let Some(ref mut data) = self.first_pass_data {
1095                data.add_frame(
1096                    stats.spatial_complexity,
1097                    stats.temporal_complexity,
1098                    stats.complexity,
1099                );
1100
1101                if stats.scene_cut {
1102                    data.mark_scene_change(stats.frame_num);
1103                }
1104
1105                if stats.frame_type == FrameType::Key && data.frame_count > 1 {
1106                    data.finalize_gop();
1107                    data.add_gop_boundary(stats.frame_num);
1108                }
1109            }
1110        }
1111
1112        // Adjust base QP based on results
1113        self.adjust_base_qp(stats);
1114
1115        // Remove oldest lookahead frame as it's now encoded
1116        if !self.lookahead_frames.is_empty() {
1117            self.lookahead_frames.remove(0);
1118        }
1119    }
1120
1121    /// Finalize current GOP and start a new one.
1122    fn finalize_gop(&mut self) {
1123        self.gop_history.push(self.current_gop.clone());
1124        if self.gop_history.len() > self.max_gop_history {
1125            self.gop_history.remove(0);
1126        }
1127
1128        let next_gop_index = self.current_gop.gop_index + 1;
1129        self.current_gop = GopStats::new(next_gop_index, self.frame_count);
1130    }
1131
1132    /// Adjust base QP based on encoding results.
1133    fn adjust_base_qp(&mut self, stats: &FrameStats) {
1134        if stats.target_bits == 0 {
1135            return;
1136        }
1137
1138        let accuracy = stats.bits as f32 / stats.target_bits as f32;
1139
1140        // Gradual QP adjustment
1141        let adjustment = if accuracy > 1.3 {
1142            0.15
1143        } else if accuracy > 1.1 {
1144            0.05
1145        } else if accuracy < 0.7 {
1146            -0.15
1147        } else if accuracy < 0.9 {
1148            -0.05
1149        } else {
1150            0.0
1151        };
1152
1153        self.current_qp =
1154            (self.current_qp + adjustment).clamp(self.min_qp as f32, self.max_qp as f32);
1155    }
1156
1157    /// Finalize first pass and get data.
1158    #[must_use]
1159    pub fn finalize_first_pass(&mut self) -> Option<FirstPassData> {
1160        if self.pass != 1 {
1161            return None;
1162        }
1163
1164        if let Some(ref mut data) = self.first_pass_data {
1165            data.finalize_gop();
1166
1167            // Calculate total duration
1168            let duration = self.frame_count as f64 / self.framerate;
1169            let total_bits = (self.target_bitrate as f64 * duration) as u64;
1170            data.calculate_bit_allocation(total_bits);
1171        }
1172
1173        self.first_pass_data.take()
1174    }
1175
1176    /// Get target bitrate.
1177    #[must_use]
1178    pub fn target_bitrate(&self) -> u64 {
1179        self.target_bitrate
1180    }
1181
1182    /// Get maximum bitrate.
1183    #[must_use]
1184    pub fn max_bitrate(&self) -> u64 {
1185        self.max_bitrate
1186    }
1187
1188    /// Get minimum bitrate.
1189    #[must_use]
1190    pub fn min_bitrate(&self) -> u64 {
1191        self.min_bitrate
1192    }
1193
1194    /// Get current average bitrate.
1195    #[must_use]
1196    pub fn current_bitrate(&self) -> f64 {
1197        if self.frame_count == 0 || self.framerate <= 0.0 {
1198            return 0.0;
1199        }
1200        let elapsed = self.frame_count as f64 / self.framerate;
1201        self.total_bits as f64 / elapsed
1202    }
1203
1204    /// Get current QP.
1205    #[must_use]
1206    pub fn current_qp(&self) -> f32 {
1207        self.current_qp
1208    }
1209
1210    /// Get frame count.
1211    #[must_use]
1212    pub fn frame_count(&self) -> u64 {
1213        self.frame_count
1214    }
1215
1216    /// Get bit reservoir level.
1217    #[must_use]
1218    pub fn bit_reservoir(&self) -> i64 {
1219        self.bit_reservoir
1220    }
1221
1222    /// Get VBV buffer fullness (0.0-1.0).
1223    #[must_use]
1224    pub fn vbv_fullness(&self) -> f32 {
1225        self.vbv_buffer
1226            .as_ref()
1227            .map(|b| b.fullness())
1228            .unwrap_or(0.5)
1229    }
1230
1231    /// Force next frame to be a keyframe.
1232    pub fn force_keyframe(&mut self) {
1233        self.frame_type_state.force_next_keyframe();
1234    }
1235
1236    /// Reset the controller state.
1237    pub fn reset(&mut self) {
1238        self.frame_count = 0;
1239        self.total_bits = 0;
1240        self.bit_reservoir = 0;
1241        self.current_gop = GopStats::new(0, 0);
1242        self.gop_history.clear();
1243        self.lookahead_frames.clear();
1244        self.pid_state.reset();
1245        self.frame_type_state = FrameTypeDecisionState::new(self.frame_type_state.max_b_frames);
1246
1247        if let Some(ref mut buffer) = self.vbv_buffer {
1248            buffer.reset();
1249        }
1250    }
1251}
1252
1253impl Default for VbrController {
1254    fn default() -> Self {
1255        Self {
1256            target_bitrate: 5_000_000,
1257            max_bitrate: 10_000_000,
1258            min_bitrate: 1_000_000,
1259            current_qp: 28.0,
1260            min_qp: 1,
1261            max_qp: 63,
1262            i_qp_offset: -2,
1263            b_qp_offset: 2,
1264            framerate: 30.0,
1265            gop_length: 250,
1266            frame_count: 0,
1267            total_bits: 0,
1268            current_gop: GopStats::new(0, 0),
1269            gop_history: Vec::new(),
1270            max_gop_history: 10,
1271            pass: 0,
1272            first_pass_data: None,
1273            quality_stability: 0.5,
1274            bit_reservoir: 0,
1275            max_reservoir: 10_000_000,
1276            vbv_buffer: None,
1277            enable_vbv: false,
1278            lookahead_size: 40,
1279            lookahead_frames: Vec::new(),
1280            rate_model: RatePredictionModel::default(),
1281            adaptive_gop: true,
1282            scene_change_threshold: 0.4,
1283            enable_aq: true,
1284            aq_strength: 1.0,
1285            pid_state: PidControllerState::new(),
1286            rdo_params: RdoParameters::default(),
1287            frame_type_state: FrameTypeDecisionState::new(3),
1288        }
1289    }
1290}
1291
1292#[cfg(test)]
1293mod tests {
1294    use super::*;
1295
1296    fn create_test_controller() -> VbrController {
1297        let config = RcConfig::vbr(5_000_000, 10_000_000);
1298        VbrController::new(&config)
1299    }
1300
1301    #[test]
1302    fn test_vbr_creation() {
1303        let controller = create_test_controller();
1304        assert_eq!(controller.target_bitrate(), 5_000_000);
1305        assert_eq!(controller.max_bitrate(), 10_000_000);
1306    }
1307
1308    #[test]
1309    fn test_get_rc() {
1310        let mut controller = create_test_controller();
1311        let output = controller.get_rc(FrameType::Key, 1.0);
1312
1313        assert!(!output.drop_frame);
1314        assert!(output.target_bits > 0);
1315        assert!(output.qp > 0);
1316        assert!(output.lambda > 0.0);
1317    }
1318
1319    #[test]
1320    fn test_closed_loop_feedback() {
1321        let mut controller = create_test_controller();
1322
1323        // Simulate encoding frames
1324        for i in 0..30 {
1325            let frame_type = if i % 10 == 0 {
1326                FrameType::Key
1327            } else {
1328                FrameType::Inter
1329            };
1330
1331            let output = controller.get_rc(frame_type, 1.0);
1332
1333            let mut stats = FrameStats::new(i, frame_type);
1334            stats.bits = output.target_bits;
1335            stats.target_bits = output.target_bits;
1336            stats.qp = output.qp;
1337            stats.qp_f = output.qp_f;
1338            stats.complexity = 1.0;
1339
1340            controller.update(&stats);
1341        }
1342
1343        // Check that controller adapted
1344        assert_eq!(controller.frame_count(), 30);
1345        assert!(controller.current_bitrate() > 0.0);
1346    }
1347
1348    #[test]
1349    fn test_lookahead_buffer() {
1350        let mut controller = create_test_controller();
1351        controller.set_lookahead_size(20);
1352
1353        // Fill lookahead buffer
1354        for _ in 0..25 {
1355            controller.push_lookahead_frame(1.0, 1.0, 1.0, false);
1356        }
1357
1358        assert!(controller.lookahead_frames.len() <= 20);
1359    }
1360
1361    #[test]
1362    fn test_vbv_compliance() {
1363        let mut config = RcConfig::vbr(5_000_000, 10_000_000);
1364        config.buffer_size = 5_000_000;
1365        let mut controller = VbrController::new(&config);
1366        controller.set_vbv_enabled(true);
1367
1368        let output = controller.get_rc(FrameType::Key, 1.0);
1369        assert!(output.max_bits > 0);
1370
1371        let mut stats = FrameStats::new(0, FrameType::Key);
1372        stats.bits = output.target_bits;
1373        stats.target_bits = output.target_bits;
1374        controller.update(&stats);
1375
1376        let fullness = controller.vbv_fullness();
1377        assert!(fullness >= 0.0 && fullness <= 1.0);
1378    }
1379
1380    #[test]
1381    fn test_rate_prediction_model() {
1382        let mut model = RatePredictionModel::default();
1383
1384        // Add some observations
1385        for i in 1..20 {
1386            let complexity = i as f32 * 0.1;
1387            let bits = 50_000 + i * 5_000;
1388            model.update(complexity, bits);
1389        }
1390
1391        let prediction = model.predict(1.5);
1392        assert!(prediction > 0);
1393    }
1394
1395    #[test]
1396    fn test_pid_controller() {
1397        let mut pid = PidControllerState::new();
1398
1399        // Simulate error correction
1400        let mut error = 1.0;
1401        for _ in 0..10 {
1402            let output = pid.calculate(error);
1403            error -= output * 0.1; // Simulate system response
1404        }
1405
1406        // Error should decrease
1407        assert!(error.abs() < 1.0);
1408    }
1409
1410    #[test]
1411    fn test_adaptive_gop() {
1412        let mut controller = create_test_controller();
1413        controller.set_adaptive_gop(true);
1414
1415        // Simulate scene change
1416        controller.push_lookahead_frame(1.0, 1.0, 1.0, false);
1417        controller.push_lookahead_frame(5.0, 5.0, 5.0, true); // Scene change
1418
1419        let output = controller.get_rc(FrameType::Inter, 1.0);
1420        // Controller should handle scene changes
1421        assert!(output.qp > 0);
1422    }
1423
1424    #[test]
1425    fn test_two_pass_encoding() {
1426        let mut controller = create_test_controller();
1427        controller.set_pass(1);
1428
1429        // First pass
1430        for i in 0..30 {
1431            let frame_type = if i % 10 == 0 {
1432                FrameType::Key
1433            } else {
1434                FrameType::Inter
1435            };
1436
1437            let _output = controller.get_rc(frame_type, 1.0 + (i as f32 % 3.0) * 0.2);
1438
1439            let mut stats = FrameStats::new(i, frame_type);
1440            stats.bits = 100_000;
1441            stats.target_bits = 100_000;
1442            stats.spatial_complexity = 1.0;
1443            stats.temporal_complexity = 1.0;
1444            stats.complexity = 1.0;
1445            controller.update(&stats);
1446        }
1447
1448        let first_pass_data = controller.finalize_first_pass().expect("should succeed");
1449        assert_eq!(first_pass_data.frame_count, 30);
1450        assert!(!first_pass_data.suggested_bits.is_empty());
1451
1452        // Second pass
1453        let mut controller2 = create_test_controller();
1454        controller2.set_pass(2);
1455        controller2.set_first_pass_data(first_pass_data);
1456
1457        let output = controller2.get_rc(FrameType::Key, 1.0);
1458        assert!(output.target_bits > 0);
1459    }
1460
1461    #[test]
1462    fn test_frame_type_decision() {
1463        let mut state = FrameTypeDecisionState::new(3);
1464
1465        // First frame should be keyframe
1466        let ft = state.decide_type(250, false, false);
1467        assert_eq!(ft, FrameType::Key);
1468
1469        // Next frames should follow B-frame pattern
1470        for _ in 0..3 {
1471            let ft = state.decide_type(250, false, false);
1472            assert!(matches!(ft, FrameType::BiDir | FrameType::Inter));
1473        }
1474
1475        // Scene change should force keyframe
1476        let ft = state.decide_type(250, true, true);
1477        assert_eq!(ft, FrameType::Key);
1478    }
1479
1480    #[test]
1481    fn test_complexity_adjustment() {
1482        let mut controller = create_test_controller();
1483
1484        // Add GOP history with known complexity
1485        let mut gop = GopStats::new(0, 0);
1486        gop.average_complexity = 1.0;
1487        controller.gop_history.push(gop);
1488
1489        let low = controller.calculate_complexity_adjustment(0.5);
1490        let high = controller.calculate_complexity_adjustment(2.0);
1491
1492        // High complexity should increase QP more
1493        assert!(high > low);
1494    }
1495
1496    #[test]
1497    fn test_bit_reservoir() {
1498        let mut controller = create_test_controller();
1499
1500        // Under-spend bits
1501        for i in 0..10 {
1502            let output = controller.get_rc(FrameType::Inter, 1.0);
1503            let mut stats = FrameStats::new(i, FrameType::Inter);
1504            stats.bits = output.target_bits / 2;
1505            stats.target_bits = output.target_bits;
1506            controller.update(&stats);
1507        }
1508
1509        assert!(controller.bit_reservoir() > 0);
1510    }
1511
1512    #[test]
1513    fn test_reset() {
1514        let mut controller = create_test_controller();
1515
1516        for i in 0..10 {
1517            let mut stats = FrameStats::new(i, FrameType::Inter);
1518            stats.bits = 100_000;
1519            controller.update(&stats);
1520        }
1521
1522        controller.reset();
1523        assert_eq!(controller.frame_count(), 0);
1524        assert_eq!(controller.bit_reservoir(), 0);
1525    }
1526}