Skip to main content

oximedia_codec/multipass/
examples.rs

1//! Examples and usage documentation for multi-pass encoding.
2//!
3//! This module provides comprehensive examples of how to use the multi-pass
4//! encoding system in various scenarios.
5
6#![forbid(unsafe_code)]
7#![allow(dead_code)]
8
9use crate::frame::{FrameType, VideoFrame};
10use crate::multipass::{
11    allocation::AllocationStrategy, EncoderConfig, EncodingResult, MultiPassEncoder, PassType,
12};
13
14/// Example: Two-pass encoding for optimal quality.
15///
16/// This example demonstrates how to perform two-pass encoding to achieve
17/// optimal bitrate allocation across frames.
18pub struct TwoPassExample {
19    width: u32,
20    height: u32,
21    target_bitrate: u64,
22    stats_file: String,
23}
24
25impl TwoPassExample {
26    /// Create a new two-pass encoding example.
27    #[must_use]
28    pub fn new(width: u32, height: u32, target_bitrate: u64) -> Self {
29        Self {
30            width,
31            height,
32            target_bitrate,
33            stats_file: "/tmp/multipass_stats.txt".to_string(),
34        }
35    }
36
37    /// Run the first pass to collect statistics.
38    pub fn run_first_pass(&self, frames: &[VideoFrame]) -> Result<(), String> {
39        // Configure first pass
40        let config = EncoderConfig::new(self.width, self.height)
41            .with_pass(PassType::FirstPass)
42            .with_target_bitrate(self.target_bitrate)
43            .with_stats_file(&self.stats_file);
44
45        let mut encoder = MultiPassEncoder::new(config);
46
47        // Process all frames
48        for frame in frames {
49            encoder
50                .encode_frame(frame)
51                .map_err(|e| format!("First pass encoding failed: {}", e))?;
52        }
53
54        // Save statistics
55        encoder
56            .save_stats(&self.stats_file)
57            .map_err(|e| format!("Failed to save stats: {}", e))?;
58
59        Ok(())
60    }
61
62    /// Run the second pass with optimal bitrate allocation.
63    pub fn run_second_pass(&self, frames: &[VideoFrame]) -> Result<Vec<EncodingResult>, String> {
64        // Configure second pass
65        let config = EncoderConfig::new(self.width, self.height)
66            .with_pass(PassType::SecondPass)
67            .with_target_bitrate(self.target_bitrate)
68            .with_stats_file(&self.stats_file)
69            .with_lookahead_frames(40)
70            .with_allocation_strategy(AllocationStrategy::TwoPass);
71
72        let mut encoder = MultiPassEncoder::new(config);
73
74        // Load first-pass statistics
75        encoder
76            .load_stats(&self.stats_file)
77            .map_err(|e| format!("Failed to load stats: {}", e))?;
78
79        let mut results = Vec::new();
80
81        // Process all frames with optimal allocation
82        for frame in frames {
83            if let Some(result) = encoder
84                .encode_frame(frame)
85                .map_err(|e| format!("Second pass encoding failed: {}", e))?
86            {
87                results.push(result);
88            }
89        }
90
91        Ok(results)
92    }
93}
94
95/// Example: Single-pass encoding with look-ahead for live streaming.
96///
97/// This example shows how to use look-ahead in single-pass mode for
98/// applications where two-pass encoding is not feasible.
99pub struct SinglePassLookaheadExample {
100    width: u32,
101    height: u32,
102    target_bitrate: u64,
103    lookahead_frames: usize,
104}
105
106impl SinglePassLookaheadExample {
107    /// Create a new single-pass look-ahead example.
108    #[must_use]
109    pub fn new(width: u32, height: u32, target_bitrate: u64, lookahead_frames: usize) -> Self {
110        Self {
111            width,
112            height,
113            target_bitrate,
114            lookahead_frames,
115        }
116    }
117
118    /// Run single-pass encoding with look-ahead.
119    pub fn run(&self, frames: &[VideoFrame]) -> Result<Vec<EncodingResult>, String> {
120        // Configure single-pass with look-ahead
121        let config = EncoderConfig::new(self.width, self.height)
122            .with_pass(PassType::SinglePassLookahead)
123            .with_target_bitrate(self.target_bitrate)
124            .with_lookahead_frames(self.lookahead_frames)
125            .with_allocation_strategy(AllocationStrategy::Perceptual);
126
127        let mut encoder = MultiPassEncoder::new(config);
128        let mut results = Vec::new();
129
130        // Process frames with look-ahead
131        for frame in frames {
132            if let Some(result) = encoder
133                .encode_frame(frame)
134                .map_err(|e| format!("Encoding failed: {}", e))?
135            {
136                results.push(result);
137            }
138        }
139
140        Ok(results)
141    }
142}
143
144/// Example: VBV-constrained encoding for streaming.
145///
146/// This example demonstrates how to use VBV buffer constraints for
147/// streaming applications that require strict bitrate compliance.
148pub struct VbvConstrainedExample {
149    width: u32,
150    height: u32,
151    target_bitrate: u64,
152    max_bitrate: u64,
153    buffer_size: u64,
154}
155
156impl VbvConstrainedExample {
157    /// Create a new VBV-constrained encoding example.
158    #[must_use]
159    pub fn new(
160        width: u32,
161        height: u32,
162        target_bitrate: u64,
163        max_bitrate: u64,
164        buffer_size: u64,
165    ) -> Self {
166        Self {
167            width,
168            height,
169            target_bitrate,
170            max_bitrate,
171            buffer_size,
172        }
173    }
174
175    /// Run VBV-constrained encoding.
176    pub fn run(&self, frames: &[VideoFrame]) -> Result<(Vec<EncodingResult>, VbvReport), String> {
177        // Configure with VBV constraints
178        let config = EncoderConfig::new(self.width, self.height)
179            .with_pass(PassType::SinglePassLookahead)
180            .with_target_bitrate(self.target_bitrate)
181            .with_vbv(self.buffer_size, self.max_bitrate)
182            .with_lookahead_frames(40);
183
184        let mut encoder = MultiPassEncoder::new(config);
185        let mut results = Vec::new();
186
187        // Process frames with VBV constraints
188        for frame in frames {
189            if let Some(result) = encoder
190                .encode_frame(frame)
191                .map_err(|e| format!("VBV encoding failed: {}", e))?
192            {
193                results.push(result);
194            }
195        }
196
197        // Get VBV statistics
198        let vbv_stats = encoder
199            .vbv_statistics()
200            .ok_or("VBV statistics not available")?;
201
202        let report = VbvReport {
203            is_compliant: vbv_stats.is_compliant(),
204            underflow_count: vbv_stats.underflow_count,
205            overflow_count: vbv_stats.overflow_count,
206            utilization: vbv_stats.utilization(),
207        };
208
209        Ok((results, report))
210    }
211}
212
213/// VBV compliance report.
214#[derive(Clone, Debug)]
215pub struct VbvReport {
216    /// Whether encoding was VBV compliant.
217    pub is_compliant: bool,
218    /// Number of buffer underflows.
219    pub underflow_count: u64,
220    /// Number of buffer overflows.
221    pub overflow_count: u64,
222    /// Buffer utilization.
223    pub utilization: crate::multipass::vbv::BufferUtilization,
224}
225
226/// Example: Scene change detection with adaptive keyframes.
227///
228/// This example shows how the encoder automatically detects scene changes
229/// and inserts keyframes at appropriate locations.
230pub struct SceneChangeExample {
231    width: u32,
232    height: u32,
233    scene_threshold: f64,
234    min_keyint: u32,
235    max_keyint: u32,
236}
237
238impl SceneChangeExample {
239    /// Create a new scene change detection example.
240    #[must_use]
241    pub fn new(width: u32, height: u32) -> Self {
242        Self {
243            width,
244            height,
245            scene_threshold: 0.4,
246            min_keyint: 10,
247            max_keyint: 250,
248        }
249    }
250
251    /// Set scene change detection parameters.
252    #[must_use]
253    pub fn with_params(mut self, threshold: f64, min_keyint: u32, max_keyint: u32) -> Self {
254        self.scene_threshold = threshold;
255        self.min_keyint = min_keyint;
256        self.max_keyint = max_keyint;
257        self
258    }
259
260    /// Run encoding with scene change detection.
261    pub fn run(&self, frames: &[VideoFrame]) -> Result<SceneChangeReport, String> {
262        // Configure with scene change detection
263        let config = EncoderConfig::new(self.width, self.height)
264            .with_pass(PassType::SinglePassLookahead)
265            .with_lookahead_frames(40)
266            .with_keyint_range(self.min_keyint, self.max_keyint);
267
268        let mut encoder = MultiPassEncoder::new(config);
269        let mut keyframe_positions = Vec::new();
270        let mut total_frames = 0;
271
272        // Process frames and track keyframes
273        for frame in frames {
274            if let Some(result) = encoder
275                .encode_frame(frame)
276                .map_err(|e| format!("Encoding failed: {}", e))?
277            {
278                if result.frame_type == FrameType::Key {
279                    keyframe_positions.push(result.frame_index);
280                }
281                total_frames += 1;
282            }
283        }
284
285        let keyframe_count = keyframe_positions.len();
286        let avg_gop_length = if keyframe_count > 1 {
287            total_frames as f64 / keyframe_count as f64
288        } else {
289            total_frames as f64
290        };
291
292        let report = SceneChangeReport {
293            total_frames,
294            keyframe_count,
295            keyframe_positions,
296            avg_gop_length,
297        };
298
299        Ok(report)
300    }
301}
302
303/// Scene change detection report.
304#[derive(Clone, Debug)]
305pub struct SceneChangeReport {
306    /// Total frames processed.
307    pub total_frames: u64,
308    /// Number of keyframes inserted.
309    pub keyframe_count: usize,
310    /// Positions of keyframes in the stream.
311    pub keyframe_positions: Vec<u64>,
312    /// Average GOP (Group of Pictures) length.
313    pub avg_gop_length: f64,
314}
315
316/// Example: Adaptive quantization based on frame complexity.
317///
318/// This example demonstrates how the encoder adjusts QP based on frame
319/// complexity to maintain consistent perceptual quality.
320pub struct AdaptiveQuantizationExample {
321    width: u32,
322    height: u32,
323    target_bitrate: u64,
324}
325
326impl AdaptiveQuantizationExample {
327    /// Create a new adaptive quantization example.
328    #[must_use]
329    pub fn new(width: u32, height: u32, target_bitrate: u64) -> Self {
330        Self {
331            width,
332            height,
333            target_bitrate,
334        }
335    }
336
337    /// Run encoding with adaptive quantization.
338    pub fn run(&self, frames: &[VideoFrame]) -> Result<AqReport, String> {
339        // Configure with perceptual allocation (uses AQ).
340        // Use a lookahead that fits within the number of available frames.
341        let lookahead = if frames.len() > 10 {
342            frames.len() / 3
343        } else {
344            10
345        };
346        let config = EncoderConfig::new(self.width, self.height)
347            .with_pass(PassType::SinglePassLookahead)
348            .with_target_bitrate(self.target_bitrate)
349            .with_lookahead_frames(lookahead)
350            .with_allocation_strategy(AllocationStrategy::Perceptual);
351
352        let mut encoder = MultiPassEncoder::new(config);
353        let mut qp_values = Vec::new();
354        let mut complexity_values = Vec::new();
355
356        // Process frames and track QP and complexity
357        for frame in frames {
358            if let Some(result) = encoder
359                .encode_frame(frame)
360                .map_err(|e| format!("AQ encoding failed: {}", e))?
361            {
362                qp_values.push(result.qp);
363                complexity_values.push(result.complexity);
364            }
365        }
366
367        // Calculate statistics
368        let avg_qp = qp_values.iter().sum::<f64>() / qp_values.len() as f64;
369        let avg_complexity = complexity_values.iter().sum::<f64>() / complexity_values.len() as f64;
370
371        let min_qp = qp_values
372            .iter()
373            .copied()
374            .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
375            .unwrap_or(0.0);
376
377        let max_qp = qp_values
378            .iter()
379            .copied()
380            .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
381            .unwrap_or(0.0);
382
383        let report = AqReport {
384            frame_count: qp_values.len(),
385            avg_qp,
386            min_qp,
387            max_qp,
388            qp_range: max_qp - min_qp,
389            avg_complexity,
390        };
391
392        Ok(report)
393    }
394}
395
396/// Adaptive quantization report.
397#[derive(Clone, Debug)]
398pub struct AqReport {
399    /// Total frames processed.
400    pub frame_count: usize,
401    /// Average QP across all frames.
402    pub avg_qp: f64,
403    /// Minimum QP used.
404    pub min_qp: f64,
405    /// Maximum QP used.
406    pub max_qp: f64,
407    /// QP range (max - min).
408    pub qp_range: f64,
409    /// Average frame complexity.
410    pub avg_complexity: f64,
411}
412
413/// Example: Comparing different allocation strategies.
414///
415/// This example compares the performance of different bitrate allocation
416/// strategies on the same content.
417pub struct AllocationComparisonExample {
418    width: u32,
419    height: u32,
420    target_bitrate: u64,
421}
422
423impl AllocationComparisonExample {
424    /// Create a new allocation comparison example.
425    #[must_use]
426    pub fn new(width: u32, height: u32, target_bitrate: u64) -> Self {
427        Self {
428            width,
429            height,
430            target_bitrate,
431        }
432    }
433
434    /// Run comparison across all allocation strategies.
435    pub fn run(&self, frames: &[VideoFrame]) -> Result<ComparisonReport, String> {
436        let strategies = [
437            AllocationStrategy::Uniform,
438            AllocationStrategy::Complexity,
439            AllocationStrategy::Perceptual,
440        ];
441
442        let mut results = Vec::new();
443
444        for strategy in &strategies {
445            let config = EncoderConfig::new(self.width, self.height)
446                .with_pass(PassType::SinglePassLookahead)
447                .with_target_bitrate(self.target_bitrate)
448                .with_lookahead_frames(40)
449                .with_allocation_strategy(*strategy);
450
451            let mut encoder = MultiPassEncoder::new(config);
452            let mut total_bits = 0u64;
453            let mut qp_values = Vec::new();
454
455            for frame in frames {
456                if let Some(result) = encoder
457                    .encode_frame(frame)
458                    .map_err(|e| format!("Comparison encoding failed: {}", e))?
459                {
460                    total_bits += result.target_bits;
461                    qp_values.push(result.qp);
462                }
463            }
464
465            let avg_qp = qp_values.iter().sum::<f64>() / qp_values.len() as f64;
466            let qp_variance = qp_values
467                .iter()
468                .map(|qp| (qp - avg_qp).powi(2))
469                .sum::<f64>()
470                / qp_values.len() as f64;
471
472            results.push(StrategyResult {
473                strategy: *strategy,
474                total_bits,
475                avg_qp,
476                qp_variance,
477                frame_count: qp_values.len(),
478            });
479        }
480
481        Ok(ComparisonReport { results })
482    }
483}
484
485/// Comparison report across strategies.
486#[derive(Clone, Debug)]
487pub struct ComparisonReport {
488    /// Results for each strategy.
489    pub results: Vec<StrategyResult>,
490}
491
492/// Result for a single allocation strategy.
493#[derive(Clone, Debug)]
494pub struct StrategyResult {
495    /// Allocation strategy used.
496    pub strategy: AllocationStrategy,
497    /// Total bits used.
498    pub total_bits: u64,
499    /// Average QP.
500    pub avg_qp: f64,
501    /// QP variance (measure of consistency).
502    pub qp_variance: f64,
503    /// Number of frames encoded.
504    pub frame_count: usize,
505}
506
507impl ComparisonReport {
508    /// Find the strategy with lowest QP variance (most consistent quality).
509    #[must_use]
510    pub fn most_consistent(&self) -> Option<&StrategyResult> {
511        self.results.iter().min_by(|a, b| {
512            a.qp_variance
513                .partial_cmp(&b.qp_variance)
514                .unwrap_or(std::cmp::Ordering::Equal)
515        })
516    }
517
518    /// Find the strategy that used fewest bits.
519    #[must_use]
520    pub fn most_efficient(&self) -> Option<&StrategyResult> {
521        self.results.iter().min_by_key(|r| r.total_bits)
522    }
523}
524
525#[cfg(test)]
526mod tests {
527    use super::*;
528    use crate::frame::Plane;
529    use oximedia_core::{PixelFormat, Rational, Timestamp};
530
531    fn create_test_frames(count: usize) -> Vec<VideoFrame> {
532        (0..count)
533            .map(|i| {
534                let mut frame = VideoFrame::new(PixelFormat::Yuv420p, 320, 240);
535                let size = 320 * 240;
536                let data = vec![(i % 256) as u8; size];
537                frame.planes.push(Plane::new(data, 320));
538                frame.timestamp = Timestamp::new(i as i64, Rational::new(1, 30));
539                frame
540            })
541            .collect()
542    }
543
544    #[test]
545    fn test_single_pass_lookahead_example() {
546        let example = SinglePassLookaheadExample::new(320, 240, 1_000_000, 20);
547        let frames = create_test_frames(30);
548        let result = example.run(&frames);
549        assert!(result.is_ok());
550    }
551
552    #[test]
553    fn test_scene_change_example() {
554        let example = SceneChangeExample::new(320, 240);
555        let frames = create_test_frames(50);
556        let result = example.run(&frames);
557        assert!(result.is_ok());
558    }
559
560    #[test]
561    fn test_adaptive_quantization_example() {
562        let example = AdaptiveQuantizationExample::new(320, 240, 1_000_000);
563        let frames = create_test_frames(30);
564        let result = example.run(&frames);
565        assert!(result.is_ok());
566
567        if let Ok(report) = result {
568            assert!(report.frame_count > 0);
569            assert!(report.avg_qp > 0.0);
570        }
571    }
572
573    #[test]
574    fn test_allocation_comparison_example() {
575        let example = AllocationComparisonExample::new(320, 240, 1_000_000);
576        let frames = create_test_frames(20);
577        let result = example.run(&frames);
578        assert!(result.is_ok());
579
580        if let Ok(report) = result {
581            assert_eq!(report.results.len(), 3);
582            assert!(report.most_consistent().is_some());
583            assert!(report.most_efficient().is_some());
584        }
585    }
586}