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