Skip to main content

oximedia_codec/rate_control/
buffer.rs

1//! Rate buffer management for HRD compliance.
2//!
3//! This module implements buffer models used for bitrate control:
4//! - Leaky bucket model for CBR/VBR
5//! - CPB (Coded Picture Buffer) management
6//! - HRD (Hypothetical Reference Decoder) compliance
7
8#![allow(clippy::cast_lossless)]
9#![allow(clippy::cast_precision_loss)]
10#![allow(clippy::cast_possible_truncation)]
11#![allow(clippy::cast_sign_loss)]
12#![forbid(unsafe_code)]
13
14/// Rate buffer using leaky bucket model.
15///
16/// The buffer simulates an encoder's output buffer:
17/// - Bits are added when frames are encoded
18/// - Bits are removed at a constant rate (target bitrate)
19#[derive(Clone, Debug)]
20pub struct RateBuffer {
21    /// Buffer capacity in bits.
22    capacity: u64,
23    /// Current buffer level in bits.
24    level: u64,
25    /// Initial buffer fullness ratio.
26    initial_fullness: f32,
27}
28
29impl RateBuffer {
30    /// Create a new rate buffer.
31    ///
32    /// # Arguments
33    ///
34    /// * `capacity` - Buffer size in bits
35    /// * `initial_fullness` - Initial fill level as fraction (0.0-1.0)
36    #[must_use]
37    pub fn new(capacity: u64, initial_fullness: f32) -> Self {
38        let fullness = initial_fullness.clamp(0.0, 1.0);
39        let level = (capacity as f64 * fullness as f64) as u64;
40        Self {
41            capacity,
42            level,
43            initial_fullness: fullness,
44        }
45    }
46
47    /// Add bits to the buffer (frame encoded).
48    ///
49    /// Returns the number of bits that overflowed (couldn't fit).
50    pub fn add_bits(&mut self, bits: u64) -> u64 {
51        let new_level = self.level.saturating_add(bits);
52        if new_level > self.capacity {
53            let overflow = new_level - self.capacity;
54            self.level = self.capacity;
55            overflow
56        } else {
57            self.level = new_level;
58            0
59        }
60    }
61
62    /// Remove bits from the buffer (drain at target bitrate).
63    ///
64    /// Returns the number of bits that underflowed (buffer went negative).
65    pub fn remove_bits(&mut self, bits: u64) -> u64 {
66        if bits > self.level {
67            let underflow = bits - self.level;
68            self.level = 0;
69            underflow
70        } else {
71            self.level -= bits;
72            0
73        }
74    }
75
76    /// Get current buffer level in bits.
77    #[must_use]
78    pub fn level(&self) -> u64 {
79        self.level
80    }
81
82    /// Get buffer capacity in bits.
83    #[must_use]
84    pub fn capacity(&self) -> u64 {
85        self.capacity
86    }
87
88    /// Get buffer fullness as fraction (0.0-1.0).
89    #[must_use]
90    pub fn fullness(&self) -> f32 {
91        if self.capacity == 0 {
92            return 0.0;
93        }
94        self.level as f32 / self.capacity as f32
95    }
96
97    /// Get available space in bits.
98    #[must_use]
99    pub fn available_space(&self) -> u64 {
100        self.capacity.saturating_sub(self.level)
101    }
102
103    /// Check if buffer is empty.
104    #[must_use]
105    pub fn is_empty(&self) -> bool {
106        self.level == 0
107    }
108
109    /// Check if buffer is full.
110    #[must_use]
111    pub fn is_full(&self) -> bool {
112        self.level >= self.capacity
113    }
114
115    /// Reset buffer to initial state.
116    pub fn reset(&mut self) {
117        self.level = (self.capacity as f64 * self.initial_fullness as f64) as u64;
118    }
119
120    /// Set buffer level directly (use with caution).
121    pub fn set_level(&mut self, level: u64) {
122        self.level = level.min(self.capacity);
123    }
124}
125
126impl Default for RateBuffer {
127    fn default() -> Self {
128        Self::new(5_000_000, 0.5)
129    }
130}
131
132/// Buffer model for HRD (Hypothetical Reference Decoder) compliance.
133///
134/// Implements the CPB (Coded Picture Buffer) model as defined in
135/// video coding standards for streaming compliance.
136#[derive(Clone, Debug)]
137pub struct BufferModel {
138    /// CPB size in bits.
139    cpb_size: u64,
140    /// Current CPB occupancy in bits.
141    cpb_level: u64,
142    /// Target bitrate in bits per second.
143    bitrate: u64,
144    /// Frame rate (frames per second).
145    framerate: f64,
146    /// Initial CPB removal delay in seconds.
147    initial_delay: f64,
148    /// Time since last removal (for drift tracking).
149    time_since_removal: f64,
150    /// Overflow events count.
151    overflow_count: u64,
152    /// Underflow events count.
153    underflow_count: u64,
154}
155
156impl BufferModel {
157    /// Create a new buffer model.
158    #[must_use]
159    pub fn new(cpb_size: u64, bitrate: u64, framerate: f64, initial_delay: f64) -> Self {
160        // Initial CPB level based on delay
161        let initial_level = (bitrate as f64 * initial_delay) as u64;
162
163        Self {
164            cpb_size,
165            cpb_level: initial_level.min(cpb_size),
166            bitrate,
167            framerate,
168            initial_delay,
169            time_since_removal: 0.0,
170            overflow_count: 0,
171            underflow_count: 0,
172        }
173    }
174
175    /// Fill buffer at constant bitrate for one frame duration.
176    pub fn fill_for_frame(&mut self) {
177        let frame_duration = 1.0 / self.framerate;
178        let bits_to_add = (self.bitrate as f64 * frame_duration) as u64;
179
180        let new_level = self.cpb_level.saturating_add(bits_to_add);
181        if new_level > self.cpb_size {
182            self.overflow_count += 1;
183            self.cpb_level = self.cpb_size;
184        } else {
185            self.cpb_level = new_level;
186        }
187
188        self.time_since_removal += frame_duration;
189    }
190
191    /// Remove bits for an encoded frame.
192    pub fn remove_frame_bits(&mut self, bits: u64) {
193        if bits > self.cpb_level {
194            self.underflow_count += 1;
195            self.cpb_level = 0;
196        } else {
197            self.cpb_level -= bits;
198        }
199        self.time_since_removal = 0.0;
200    }
201
202    /// Simulate encoding a frame with the given bits.
203    ///
204    /// Fills the buffer, then removes the frame bits.
205    pub fn encode_frame(&mut self, bits: u64) {
206        self.fill_for_frame();
207        self.remove_frame_bits(bits);
208    }
209
210    /// Get maximum bits allowed for next frame (before overflow).
211    #[must_use]
212    pub fn max_frame_bits(&self) -> u64 {
213        // After filling for one frame, how many bits can we remove?
214        let frame_duration = 1.0 / self.framerate;
215        let bits_added = (self.bitrate as f64 * frame_duration) as u64;
216
217        // We can remove all bits in the buffer
218        self.cpb_level.saturating_add(bits_added).min(self.cpb_size)
219    }
220
221    /// Get minimum bits required for next frame (before underflow).
222    #[must_use]
223    pub fn min_frame_bits(&self) -> u64 {
224        // After filling for one frame, what's the minimum we must remove?
225        let frame_duration = 1.0 / self.framerate;
226        let bits_added = (self.bitrate as f64 * frame_duration) as u64;
227        let future_level = self.cpb_level.saturating_add(bits_added);
228
229        // If would overflow, we must remove at least this much
230        future_level.saturating_sub(self.cpb_size)
231    }
232
233    /// Check if the buffer is in a healthy state.
234    #[must_use]
235    pub fn is_healthy(&self) -> bool {
236        let fullness = self.fullness();
237        (0.2..=0.8).contains(&fullness)
238    }
239
240    /// Get buffer fullness as fraction.
241    #[must_use]
242    pub fn fullness(&self) -> f32 {
243        if self.cpb_size == 0 {
244            return 0.0;
245        }
246        self.cpb_level as f32 / self.cpb_size as f32
247    }
248
249    /// Get CPB level in bits.
250    #[must_use]
251    pub fn level(&self) -> u64 {
252        self.cpb_level
253    }
254
255    /// Get CPB size in bits.
256    #[must_use]
257    pub fn cpb_size(&self) -> u64 {
258        self.cpb_size
259    }
260
261    /// Get overflow count.
262    #[must_use]
263    pub fn overflow_count(&self) -> u64 {
264        self.overflow_count
265    }
266
267    /// Get underflow count.
268    #[must_use]
269    pub fn underflow_count(&self) -> u64 {
270        self.underflow_count
271    }
272
273    /// Reset the buffer model.
274    pub fn reset(&mut self) {
275        let initial_level = (self.bitrate as f64 * self.initial_delay) as u64;
276        self.cpb_level = initial_level.min(self.cpb_size);
277        self.time_since_removal = 0.0;
278        self.overflow_count = 0;
279        self.underflow_count = 0;
280    }
281}
282
283impl Default for BufferModel {
284    fn default() -> Self {
285        Self::new(5_000_000, 5_000_000, 30.0, 0.5)
286    }
287}
288
289/// VBV (Video Buffering Verifier) parameters.
290#[derive(Clone, Debug)]
291pub struct VbvParams {
292    /// Maximum buffer size in bits.
293    pub max_buffer_size: u64,
294    /// Maximum bitrate in bits per second.
295    pub max_bitrate: u64,
296    /// Initial buffer delay in seconds.
297    pub initial_delay: f64,
298    /// Whether VBV is enabled.
299    pub enabled: bool,
300}
301
302impl VbvParams {
303    /// Create VBV parameters from bitrate.
304    #[must_use]
305    pub fn from_bitrate(bitrate: u64) -> Self {
306        Self {
307            max_buffer_size: bitrate * 2, // 2 seconds of buffer
308            max_bitrate: bitrate,
309            initial_delay: 0.5,
310            enabled: true,
311        }
312    }
313
314    /// Create VBV parameters for streaming.
315    #[must_use]
316    pub fn for_streaming(bitrate: u64, buffer_seconds: f64) -> Self {
317        Self {
318            max_buffer_size: (bitrate as f64 * buffer_seconds) as u64,
319            max_bitrate: bitrate,
320            initial_delay: buffer_seconds / 2.0,
321            enabled: true,
322        }
323    }
324
325    /// Disable VBV.
326    #[must_use]
327    pub fn disabled() -> Self {
328        Self {
329            max_buffer_size: u64::MAX,
330            max_bitrate: u64::MAX,
331            initial_delay: 0.0,
332            enabled: false,
333        }
334    }
335}
336
337impl Default for VbvParams {
338    fn default() -> Self {
339        Self::from_bitrate(5_000_000)
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346
347    #[test]
348    fn test_rate_buffer_creation() {
349        let buffer = RateBuffer::new(1_000_000, 0.5);
350        assert_eq!(buffer.capacity(), 1_000_000);
351        assert_eq!(buffer.level(), 500_000);
352        assert!((buffer.fullness() - 0.5).abs() < f32::EPSILON);
353    }
354
355    #[test]
356    fn test_rate_buffer_add_bits() {
357        let mut buffer = RateBuffer::new(1_000_000, 0.0);
358        assert_eq!(buffer.level(), 0);
359
360        buffer.add_bits(500_000);
361        assert_eq!(buffer.level(), 500_000);
362
363        // Add more bits, causing overflow
364        let overflow = buffer.add_bits(600_000);
365        assert_eq!(overflow, 100_000);
366        assert_eq!(buffer.level(), 1_000_000);
367        assert!(buffer.is_full());
368    }
369
370    #[test]
371    fn test_rate_buffer_remove_bits() {
372        let mut buffer = RateBuffer::new(1_000_000, 0.5);
373
374        buffer.remove_bits(200_000);
375        assert_eq!(buffer.level(), 300_000);
376
377        // Remove more than available, causing underflow
378        let underflow = buffer.remove_bits(400_000);
379        assert_eq!(underflow, 100_000);
380        assert_eq!(buffer.level(), 0);
381        assert!(buffer.is_empty());
382    }
383
384    #[test]
385    fn test_rate_buffer_available_space() {
386        let buffer = RateBuffer::new(1_000_000, 0.5);
387        assert_eq!(buffer.available_space(), 500_000);
388    }
389
390    #[test]
391    fn test_rate_buffer_reset() {
392        let mut buffer = RateBuffer::new(1_000_000, 0.5);
393        buffer.add_bits(500_000);
394        buffer.reset();
395        assert_eq!(buffer.level(), 500_000);
396    }
397
398    #[test]
399    fn test_buffer_model_creation() {
400        let model = BufferModel::new(5_000_000, 5_000_000, 30.0, 0.5);
401        assert_eq!(model.cpb_size(), 5_000_000);
402        // Initial level should be 0.5 seconds worth = 2,500,000 bits
403        assert_eq!(model.level(), 2_500_000);
404    }
405
406    #[test]
407    fn test_buffer_model_encode_frame() {
408        let mut model = BufferModel::new(5_000_000, 5_000_000, 30.0, 0.5);
409        let initial_level = model.level();
410
411        // Encode a frame with target bits (bitrate / fps)
412        let target_bits = 5_000_000 / 30; // ~166,666 bits
413        model.encode_frame(target_bits);
414
415        // Level should be approximately the same (filled then removed)
416        let diff = (model.level() as i64 - initial_level as i64).abs();
417        assert!(diff < 100); // Allow small rounding errors
418    }
419
420    #[test]
421    fn test_buffer_model_max_min_bits() {
422        let model = BufferModel::new(5_000_000, 5_000_000, 30.0, 0.5);
423
424        let max_bits = model.max_frame_bits();
425        let min_bits = model.min_frame_bits();
426
427        assert!(max_bits > 0);
428        assert!(max_bits >= min_bits);
429    }
430
431    #[test]
432    fn test_buffer_model_overflow() {
433        let mut model = BufferModel::new(1_000_000, 5_000_000, 30.0, 0.9);
434
435        // Keep filling without removing enough bits
436        for _ in 0..60 {
437            model.fill_for_frame();
438            model.remove_frame_bits(1000); // Remove very little
439        }
440
441        assert!(model.overflow_count() > 0);
442    }
443
444    #[test]
445    fn test_buffer_model_underflow() {
446        let mut model = BufferModel::new(1_000_000, 5_000_000, 30.0, 0.1);
447
448        // Remove lots of bits
449        model.remove_frame_bits(500_000);
450
451        assert!(model.underflow_count() > 0 || model.level() == 0);
452    }
453
454    #[test]
455    fn test_vbv_params() {
456        let vbv = VbvParams::from_bitrate(5_000_000);
457        assert!(vbv.enabled);
458        assert_eq!(vbv.max_bitrate, 5_000_000);
459        assert_eq!(vbv.max_buffer_size, 10_000_000);
460
461        let vbv = VbvParams::for_streaming(5_000_000, 2.0);
462        assert_eq!(vbv.max_buffer_size, 10_000_000);
463        assert!((vbv.initial_delay - 1.0).abs() < f64::EPSILON);
464
465        let vbv = VbvParams::disabled();
466        assert!(!vbv.enabled);
467    }
468}