1#![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
14pub struct TwoPassExample {
19 width: u32,
20 height: u32,
21 target_bitrate: u64,
22 stats_file: String,
23}
24
25impl TwoPassExample {
26 #[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 pub fn run_first_pass(&self, frames: &[VideoFrame]) -> Result<(), String> {
42 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 for frame in frames {
52 encoder
53 .encode_frame(frame)
54 .map_err(|e| format!("First pass encoding failed: {}", e))?;
55 }
56
57 encoder
59 .save_stats(&self.stats_file)
60 .map_err(|e| format!("Failed to save stats: {}", e))?;
61
62 Ok(())
63 }
64
65 pub fn run_second_pass(&self, frames: &[VideoFrame]) -> Result<Vec<EncodingResult>, String> {
67 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 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 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
98pub struct SinglePassLookaheadExample {
103 width: u32,
104 height: u32,
105 target_bitrate: u64,
106 lookahead_frames: usize,
107}
108
109impl SinglePassLookaheadExample {
110 #[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 pub fn run(&self, frames: &[VideoFrame]) -> Result<Vec<EncodingResult>, String> {
123 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 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
147pub 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 #[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 pub fn run(&self, frames: &[VideoFrame]) -> Result<(Vec<EncodingResult>, VbvReport), String> {
180 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 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 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#[derive(Clone, Debug)]
218pub struct VbvReport {
219 pub is_compliant: bool,
221 pub underflow_count: u64,
223 pub overflow_count: u64,
225 pub utilization: crate::multipass::vbv::BufferUtilization,
227}
228
229pub 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 #[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 #[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 pub fn run(&self, frames: &[VideoFrame]) -> Result<SceneChangeReport, String> {
265 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 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#[derive(Clone, Debug)]
308pub struct SceneChangeReport {
309 pub total_frames: u64,
311 pub keyframe_count: usize,
313 pub keyframe_positions: Vec<u64>,
315 pub avg_gop_length: f64,
317}
318
319pub struct AdaptiveQuantizationExample {
324 width: u32,
325 height: u32,
326 target_bitrate: u64,
327}
328
329impl AdaptiveQuantizationExample {
330 #[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 pub fn run(&self, frames: &[VideoFrame]) -> Result<AqReport, String> {
342 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 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 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#[derive(Clone, Debug)]
401pub struct AqReport {
402 pub frame_count: usize,
404 pub avg_qp: f64,
406 pub min_qp: f64,
408 pub max_qp: f64,
410 pub qp_range: f64,
412 pub avg_complexity: f64,
414}
415
416pub struct AllocationComparisonExample {
421 width: u32,
422 height: u32,
423 target_bitrate: u64,
424}
425
426impl AllocationComparisonExample {
427 #[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 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#[derive(Clone, Debug)]
490pub struct ComparisonReport {
491 pub results: Vec<StrategyResult>,
493}
494
495#[derive(Clone, Debug)]
497pub struct StrategyResult {
498 pub strategy: AllocationStrategy,
500 pub total_bits: u64,
502 pub avg_qp: f64,
504 pub qp_variance: f64,
506 pub frame_count: usize,
508}
509
510impl ComparisonReport {
511 #[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 #[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}