oximedia_codec/multipass/
vbv.rs1#![forbid(unsafe_code)]
7#![allow(clippy::cast_precision_loss)]
8#![allow(clippy::cast_possible_truncation)]
9#![allow(clippy::cast_sign_loss)]
10#![allow(clippy::cast_lossless)]
11
12use crate::frame::FrameType;
13
14#[derive(Clone, Debug)]
16pub struct VbvConfig {
17 pub buffer_size: u64,
19 pub max_bitrate: u64,
21 pub initial_fullness: f64,
23 pub framerate_num: u32,
25 pub framerate_den: u32,
27}
28
29impl VbvConfig {
30 #[must_use]
32 pub fn new(buffer_size: u64, max_bitrate: u64, framerate_num: u32, framerate_den: u32) -> Self {
33 Self {
34 buffer_size,
35 max_bitrate,
36 initial_fullness: 0.75,
37 framerate_num,
38 framerate_den,
39 }
40 }
41
42 #[must_use]
44 pub fn with_initial_fullness(mut self, fullness: f64) -> Self {
45 self.initial_fullness = fullness.clamp(0.0, 1.0);
46 self
47 }
48
49 #[must_use]
51 pub fn frame_period(&self) -> f64 {
52 self.framerate_den as f64 / self.framerate_num as f64
53 }
54
55 #[must_use]
57 pub fn bits_per_frame(&self) -> f64 {
58 self.max_bitrate as f64 * self.frame_period()
59 }
60}
61
62pub struct VbvBuffer {
64 config: VbvConfig,
65 current_fullness: f64,
67 min_fullness: f64,
69 max_fullness: f64,
71 frame_count: u64,
73 underflow_count: u64,
75 overflow_count: u64,
77}
78
79impl VbvBuffer {
80 #[must_use]
82 pub fn new(config: VbvConfig) -> Self {
83 let current_fullness = config.buffer_size as f64 * config.initial_fullness;
84
85 Self {
86 config,
87 current_fullness,
88 min_fullness: current_fullness,
89 max_fullness: current_fullness,
90 frame_count: 0,
91 underflow_count: 0,
92 overflow_count: 0,
93 }
94 }
95
96 #[must_use]
98 pub fn can_send_frame(&self, frame_bits: u64) -> bool {
99 let new_fullness = self.current_fullness - frame_bits as f64;
100 new_fullness >= 0.0
101 }
102
103 #[must_use]
105 pub fn max_frame_size(&self) -> u64 {
106 self.current_fullness as u64
107 }
108
109 #[must_use]
111 pub fn min_frame_size(&self) -> u64 {
112 let bits_added = self.config.bits_per_frame();
113 let max_fullness = self.config.buffer_size as f64;
114 let available_after_refill = self.current_fullness + bits_added;
115
116 if available_after_refill > max_fullness {
117 (available_after_refill - max_fullness) as u64
118 } else {
119 0
120 }
121 }
122
123 pub fn update(&mut self, frame_bits: u64) -> VbvUpdateResult {
125 self.current_fullness -= frame_bits as f64;
127
128 let mut result = VbvUpdateResult {
129 underflow: false,
130 overflow: false,
131 fullness_ratio: 0.0,
132 available_bits: 0,
133 };
134
135 if self.current_fullness < 0.0 {
137 result.underflow = true;
138 self.underflow_count += 1;
139 self.current_fullness = 0.0;
140 }
141
142 let bits_added = self.config.bits_per_frame();
144 self.current_fullness += bits_added;
145
146 if self.current_fullness > self.config.buffer_size as f64 {
148 result.overflow = true;
149 self.overflow_count += 1;
150 self.current_fullness = self.config.buffer_size as f64;
151 }
152
153 self.min_fullness = self.min_fullness.min(self.current_fullness);
155 self.max_fullness = self.max_fullness.max(self.current_fullness);
156
157 result.fullness_ratio = self.current_fullness / self.config.buffer_size as f64;
158 result.available_bits = self.current_fullness as u64;
159
160 self.frame_count += 1;
161 result
162 }
163
164 #[must_use]
166 pub fn target_frame_size(&self, frame_type: FrameType, base_size: f64) -> u64 {
167 let fullness_ratio = self.current_fullness / self.config.buffer_size as f64;
168
169 let buffer_factor = if fullness_ratio > 0.75 {
173 1.2 } else if fullness_ratio > 0.5 {
175 1.0 } else if fullness_ratio > 0.25 {
177 0.8 } else {
179 0.6 };
181
182 let type_factor = match frame_type {
184 FrameType::Key => 3.0, FrameType::Inter => 1.0, FrameType::BiDir => 0.5, FrameType::Switch => 2.0, };
189
190 let target = base_size * buffer_factor * type_factor;
191
192 target.min(self.max_frame_size() as f64) as u64
194 }
195
196 #[must_use]
198 pub fn fullness_ratio(&self) -> f64 {
199 self.current_fullness / self.config.buffer_size as f64
200 }
201
202 #[must_use]
204 pub fn current_fullness(&self) -> u64 {
205 self.current_fullness as u64
206 }
207
208 #[must_use]
210 pub fn statistics(&self) -> VbvStatistics {
211 VbvStatistics {
212 frame_count: self.frame_count,
213 underflow_count: self.underflow_count,
214 overflow_count: self.overflow_count,
215 current_fullness: self.current_fullness,
216 min_fullness: self.min_fullness,
217 max_fullness: self.max_fullness,
218 buffer_size: self.config.buffer_size,
219 }
220 }
221
222 pub fn reset(&mut self) {
224 self.current_fullness = self.config.buffer_size as f64 * self.config.initial_fullness;
225 self.min_fullness = self.current_fullness;
226 self.max_fullness = self.current_fullness;
227 self.frame_count = 0;
228 self.underflow_count = 0;
229 self.overflow_count = 0;
230 }
231}
232
233#[derive(Clone, Debug)]
235pub struct VbvUpdateResult {
236 pub underflow: bool,
238 pub overflow: bool,
240 pub fullness_ratio: f64,
242 pub available_bits: u64,
244}
245
246#[derive(Clone, Debug)]
248pub struct VbvStatistics {
249 pub frame_count: u64,
251 pub underflow_count: u64,
253 pub overflow_count: u64,
255 pub current_fullness: f64,
257 pub min_fullness: f64,
259 pub max_fullness: f64,
261 pub buffer_size: u64,
263}
264
265impl VbvStatistics {
266 #[must_use]
268 pub fn is_compliant(&self) -> bool {
269 self.underflow_count == 0 && self.overflow_count == 0
270 }
271
272 #[must_use]
274 pub fn utilization(&self) -> BufferUtilization {
275 BufferUtilization {
276 min_ratio: self.min_fullness / self.buffer_size as f64,
277 max_ratio: self.max_fullness / self.buffer_size as f64,
278 current_ratio: self.current_fullness / self.buffer_size as f64,
279 }
280 }
281}
282
283#[derive(Clone, Debug)]
285pub struct BufferUtilization {
286 pub min_ratio: f64,
288 pub max_ratio: f64,
290 pub current_ratio: f64,
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn test_vbv_config_new() {
300 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
301 assert_eq!(config.buffer_size, 1_000_000);
302 assert_eq!(config.max_bitrate, 5_000_000);
303 assert_eq!(config.initial_fullness, 0.75);
304 }
305
306 #[test]
307 fn test_vbv_config_frame_period() {
308 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
309 let period = config.frame_period();
310 assert!((period - 1.0 / 30.0).abs() < 1e-6);
311 }
312
313 #[test]
314 fn test_vbv_buffer_new() {
315 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
316 let buffer = VbvBuffer::new(config);
317 assert_eq!(buffer.current_fullness, 750_000.0);
318 }
319
320 #[test]
321 fn test_vbv_buffer_can_send() {
322 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
323 let buffer = VbvBuffer::new(config);
324
325 assert!(buffer.can_send_frame(100_000));
326 assert!(buffer.can_send_frame(750_000));
327 assert!(!buffer.can_send_frame(800_000));
328 }
329
330 #[test]
331 fn test_vbv_buffer_update() {
332 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
333 let mut buffer = VbvBuffer::new(config);
334
335 let result = buffer.update(100_000);
336 assert!(!result.underflow);
337 assert!(!result.overflow);
338 assert!(result.fullness_ratio > 0.0);
339 }
340
341 #[test]
342 fn test_vbv_buffer_underflow() {
343 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
344 let mut buffer = VbvBuffer::new(config);
345
346 let result = buffer.update(1_000_000);
348 assert!(result.underflow);
349 assert_eq!(buffer.underflow_count, 1);
350 }
351
352 #[test]
353 fn test_vbv_target_frame_size() {
354 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
355 let buffer = VbvBuffer::new(config);
356
357 let target = buffer.target_frame_size(FrameType::Inter, 50_000.0);
358 assert!(target > 0);
359
360 let key_target = buffer.target_frame_size(FrameType::Key, 50_000.0);
361 assert!(key_target > target); }
363
364 #[test]
365 fn test_vbv_statistics() {
366 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
367 let mut buffer = VbvBuffer::new(config);
368
369 buffer.update(100_000);
370 buffer.update(100_000);
371
372 let stats = buffer.statistics();
373 assert_eq!(stats.frame_count, 2);
374 assert!(stats.is_compliant());
375 }
376
377 #[test]
378 fn test_vbv_reset() {
379 let config = VbvConfig::new(1_000_000, 5_000_000, 30, 1);
380 let mut buffer = VbvBuffer::new(config);
381
382 buffer.update(100_000);
383 assert_eq!(buffer.frame_count, 1);
384
385 buffer.reset();
386 assert_eq!(buffer.frame_count, 0);
387 assert_eq!(buffer.current_fullness, 750_000.0);
388 }
389}