1#![forbid(unsafe_code)]
90#![allow(clippy::cast_precision_loss)]
91#![allow(clippy::cast_possible_truncation)]
92#![allow(clippy::cast_sign_loss)]
93#![allow(clippy::cast_lossless)]
94#![allow(clippy::module_name_repetitions)]
95
96pub mod allocation;
97pub mod complexity;
98pub mod examples;
99pub mod lookahead;
100pub mod stats;
101pub mod vbv;
102
103use crate::frame::{FrameType, VideoFrame};
104use crate::traits::EncodedPacket;
105use allocation::{AllocationConfig, AllocationStrategy, BitrateAllocator, VbvAwareAllocator};
106use complexity::ComplexityAnalyzer;
107use lookahead::{LookaheadBuffer, LookaheadConfig};
108use stats::{FrameStatistics, PassStatistics};
109use vbv::{VbvBuffer, VbvConfig};
110
111pub use allocation::{FrameAllocation, VbvAwareAllocator as Allocator};
113pub use complexity::{ComplexityAnalyzer as Analyzer, FrameComplexity};
114pub use lookahead::{LookaheadAnalysis, LookaheadFrame, SceneChangeDetector};
115pub use stats::{ComplexityStats, FrameStatistics as Stats, PassStatistics as PassStats};
116pub use vbv::{VbvBuffer as Buffer, VbvConfig as BufferConfig, VbvStatistics};
117
118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
120pub enum PassType {
121 FirstPass,
123 SecondPass,
125 SinglePassLookahead,
127}
128
129#[derive(Clone, Debug)]
131pub struct EncoderConfig {
132 pub width: u32,
134 pub height: u32,
136 pub pass: PassType,
138 pub lookahead_frames: usize,
140 pub target_bitrate: u64,
142 pub max_bitrate: Option<u64>,
144 pub vbv_buffer_size: Option<u64>,
146 pub framerate_num: u32,
148 pub framerate_den: u32,
150 pub min_keyint: u32,
152 pub max_keyint: u32,
154 pub scene_change_threshold: f64,
156 pub enable_aq: bool,
158 pub stats_file: Option<String>,
160 pub allocation_strategy: AllocationStrategy,
162}
163
164impl Default for EncoderConfig {
165 fn default() -> Self {
166 Self {
167 width: 1920,
168 height: 1080,
169 pass: PassType::SinglePassLookahead,
170 lookahead_frames: 40,
171 target_bitrate: 5_000_000,
172 max_bitrate: None,
173 vbv_buffer_size: None,
174 framerate_num: 30,
175 framerate_den: 1,
176 min_keyint: 10,
177 max_keyint: 250,
178 scene_change_threshold: 0.4,
179 enable_aq: true,
180 stats_file: None,
181 allocation_strategy: AllocationStrategy::Complexity,
182 }
183 }
184}
185
186impl EncoderConfig {
187 #[must_use]
189 pub fn new(width: u32, height: u32) -> Self {
190 Self {
191 width,
192 height,
193 ..Default::default()
194 }
195 }
196
197 #[must_use]
199 pub fn with_pass(mut self, pass: PassType) -> Self {
200 self.pass = pass;
201 self
202 }
203
204 #[must_use]
206 pub fn with_lookahead_frames(mut self, frames: usize) -> Self {
207 self.lookahead_frames = frames.clamp(10, 250);
208 self
209 }
210
211 #[must_use]
213 pub fn with_target_bitrate(mut self, bitrate: u64) -> Self {
214 self.target_bitrate = bitrate;
215 self
216 }
217
218 #[must_use]
220 pub fn with_vbv(mut self, buffer_size: u64, max_bitrate: u64) -> Self {
221 self.vbv_buffer_size = Some(buffer_size);
222 self.max_bitrate = Some(max_bitrate);
223 self
224 }
225
226 #[must_use]
228 pub fn with_framerate(mut self, num: u32, den: u32) -> Self {
229 self.framerate_num = num;
230 self.framerate_den = den;
231 self
232 }
233
234 #[must_use]
236 pub fn with_keyint_range(mut self, min: u32, max: u32) -> Self {
237 self.min_keyint = min;
238 self.max_keyint = max;
239 self
240 }
241
242 #[must_use]
244 pub fn with_stats_file(mut self, path: impl Into<String>) -> Self {
245 self.stats_file = Some(path.into());
246 self
247 }
248
249 #[must_use]
251 pub fn with_allocation_strategy(mut self, strategy: AllocationStrategy) -> Self {
252 self.allocation_strategy = strategy;
253 self
254 }
255}
256
257pub struct MultiPassEncoder {
259 config: EncoderConfig,
260 pass: PassType,
261 lookahead_buffer: Option<LookaheadBuffer>,
262 complexity_analyzer: ComplexityAnalyzer,
263 bitrate_allocator: BitrateAllocator,
264 vbv_buffer: Option<VbvBuffer>,
265 pass_statistics: PassStatistics,
266 frame_count: u64,
267}
268
269impl MultiPassEncoder {
270 #[must_use]
272 pub fn new(config: EncoderConfig) -> Self {
273 let complexity_analyzer = ComplexityAnalyzer::new(config.width, config.height);
274
275 let lookahead_buffer = if config.pass == PassType::SinglePassLookahead
276 || config.pass == PassType::SecondPass
277 {
278 let lookahead_config = LookaheadConfig::new(config.lookahead_frames)
279 .with_keyint_range(config.min_keyint, config.max_keyint)
280 .with_scene_threshold(config.scene_change_threshold);
281
282 Some(LookaheadBuffer::new(
283 lookahead_config,
284 config.width,
285 config.height,
286 ))
287 } else {
288 None
289 };
290
291 let allocation_config =
292 AllocationConfig::new(config.allocation_strategy, config.target_bitrate)
293 .with_framerate(config.framerate_num, config.framerate_den);
294
295 let bitrate_allocator = BitrateAllocator::new(allocation_config);
296
297 let vbv_buffer = if let (Some(buffer_size), Some(max_bitrate)) =
298 (config.vbv_buffer_size, config.max_bitrate)
299 {
300 let vbv_config = VbvConfig::new(
301 buffer_size,
302 max_bitrate,
303 config.framerate_num,
304 config.framerate_den,
305 );
306 Some(VbvBuffer::new(vbv_config))
307 } else {
308 None
309 };
310
311 let pass_statistics = PassStatistics::new(
312 config.width,
313 config.height,
314 config.framerate_num,
315 config.framerate_den,
316 );
317
318 Self {
319 pass: config.pass,
320 config,
321 lookahead_buffer,
322 complexity_analyzer,
323 bitrate_allocator,
324 vbv_buffer,
325 pass_statistics,
326 frame_count: 0,
327 }
328 }
329
330 pub fn encode_frame(
332 &mut self,
333 frame: &VideoFrame,
334 ) -> Result<Option<EncodingResult>, EncoderError> {
335 match self.pass {
336 PassType::FirstPass => self.encode_first_pass(frame),
337 PassType::SecondPass => self.encode_second_pass(frame),
338 PassType::SinglePassLookahead => self.encode_single_pass(frame),
339 }
340 }
341
342 fn encode_first_pass(
344 &mut self,
345 frame: &VideoFrame,
346 ) -> Result<Option<EncodingResult>, EncoderError> {
347 let complexity = self.complexity_analyzer.analyze(frame, self.frame_count);
349
350 let allocation = self.bitrate_allocator.allocate(
352 self.frame_count,
353 frame.frame_type,
354 complexity.combined_complexity,
355 );
356
357 let frame_stats = FrameStatistics::new(
359 self.frame_count,
360 frame.frame_type,
361 28.0, allocation.target_bits,
363 complexity,
364 );
365
366 self.pass_statistics.add_frame(frame_stats);
367 self.frame_count += 1;
368
369 Ok(None)
371 }
372
373 fn encode_second_pass(
375 &mut self,
376 frame: &VideoFrame,
377 ) -> Result<Option<EncodingResult>, EncoderError> {
378 if let Some(ref mut lookahead) = self.lookahead_buffer {
380 lookahead.add_frame(frame.clone());
381
382 if !lookahead.is_full() {
384 return Ok(None);
385 }
386
387 if let Some(lookahead_frame) = lookahead.get_next_frame() {
389 let complexity = lookahead_frame.complexity.combined_complexity;
390 let allocation = self.bitrate_allocator.allocate(
391 self.frame_count,
392 lookahead_frame.assigned_type,
393 complexity,
394 );
395
396 let result = self.create_encoding_result(
397 &lookahead_frame.frame,
398 lookahead_frame.assigned_type,
399 allocation,
400 lookahead_frame.qp_offset,
401 );
402
403 self.frame_count += 1;
404 return Ok(Some(result));
405 }
406 } else {
407 let complexity = self.complexity_analyzer.analyze(frame, self.frame_count);
409 let allocation = self.bitrate_allocator.allocate(
410 self.frame_count,
411 frame.frame_type,
412 complexity.combined_complexity,
413 );
414
415 let result = self.create_encoding_result(frame, frame.frame_type, allocation, 0);
416 self.frame_count += 1;
417 return Ok(Some(result));
418 }
419
420 Ok(None)
421 }
422
423 fn encode_single_pass(
425 &mut self,
426 frame: &VideoFrame,
427 ) -> Result<Option<EncodingResult>, EncoderError> {
428 if let Some(ref mut lookahead) = self.lookahead_buffer {
429 lookahead.add_frame(frame.clone());
430
431 if !lookahead.is_full() {
433 return Ok(None);
434 }
435
436 if let Some(lookahead_frame) = lookahead.get_next_frame() {
438 let complexity = lookahead_frame.complexity.combined_complexity;
439 let allocation = self.bitrate_allocator.allocate(
440 self.frame_count,
441 lookahead_frame.assigned_type,
442 complexity,
443 );
444
445 let target_bits = if let Some(ref vbv) = self.vbv_buffer {
447 vbv.target_frame_size(
448 lookahead_frame.assigned_type,
449 allocation.target_bits as f64,
450 )
451 } else {
452 allocation.target_bits
453 };
454
455 let mut result = self.create_encoding_result(
456 &lookahead_frame.frame,
457 lookahead_frame.assigned_type,
458 allocation,
459 lookahead_frame.qp_offset,
460 );
461
462 result.target_bits = target_bits;
463
464 if let Some(ref mut vbv) = self.vbv_buffer {
466 vbv.update(target_bits);
467 }
468
469 self.frame_count += 1;
470 return Ok(Some(result));
471 }
472 }
473
474 Ok(None)
475 }
476
477 fn create_encoding_result(
479 &self,
480 frame: &VideoFrame,
481 frame_type: FrameType,
482 allocation: allocation::FrameAllocation,
483 qp_offset: i32,
484 ) -> EncodingResult {
485 let base_qp = self.bits_to_qp(allocation.target_bits);
487 let adjusted_qp = (base_qp + allocation.qp_adjustment + qp_offset as f64).clamp(1.0, 63.0);
488
489 EncodingResult {
490 frame_index: self.frame_count,
491 frame_type,
492 target_bits: allocation.target_bits,
493 min_bits: allocation.min_bits,
494 max_bits: allocation.max_bits,
495 qp: adjusted_qp,
496 complexity: 0.5, }
498 }
499
500 fn bits_to_qp(&self, target_bits: u64) -> f64 {
502 let pixels = (self.config.width as u64) * (self.config.height as u64);
503 let bpp = target_bits as f64 / pixels as f64;
504
505 if bpp > 0.0 {
507 (69.0 - 12.0 * bpp.log2()).clamp(1.0, 63.0)
508 } else {
509 51.0 }
511 }
512
513 pub fn save_stats(&self, path: &str) -> Result<(), EncoderError> {
515 self.pass_statistics
516 .save_to_file(path)
517 .map_err(|e| EncoderError::IoError(e.to_string()))
518 }
519
520 pub fn load_stats(&mut self, path: &str) -> Result<(), EncoderError> {
522 let stats = PassStatistics::load_from_file(path)
523 .map_err(|e| EncoderError::IoError(e.to_string()))?;
524
525 self.bitrate_allocator.set_first_pass_stats(stats);
526 Ok(())
527 }
528
529 #[must_use]
531 pub fn statistics(&self) -> &PassStatistics {
532 &self.pass_statistics
533 }
534
535 #[must_use]
537 pub fn vbv_statistics(&self) -> Option<VbvStatistics> {
538 self.vbv_buffer.as_ref().map(|vbv| vbv.statistics())
539 }
540
541 #[must_use]
543 pub fn frame_count(&self) -> u64 {
544 self.frame_count
545 }
546
547 pub fn reset(&mut self) {
549 self.frame_count = 0;
550 self.complexity_analyzer.reset();
551 self.bitrate_allocator.reset();
552
553 if let Some(ref mut lookahead) = self.lookahead_buffer {
554 lookahead.reset();
555 }
556
557 if let Some(ref mut vbv) = self.vbv_buffer {
558 vbv.reset();
559 }
560
561 self.pass_statistics = PassStatistics::new(
562 self.config.width,
563 self.config.height,
564 self.config.framerate_num,
565 self.config.framerate_den,
566 );
567 }
568}
569
570#[derive(Clone, Debug)]
572pub struct EncodingResult {
573 pub frame_index: u64,
575 pub frame_type: FrameType,
577 pub target_bits: u64,
579 pub min_bits: u64,
581 pub max_bits: u64,
583 pub qp: f64,
585 pub complexity: f64,
587}
588
589#[derive(Debug)]
591pub enum EncoderError {
592 IoError(String),
594 ConfigError(String),
596 EncodingError(String),
598}
599
600impl std::fmt::Display for EncoderError {
601 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
602 match self {
603 Self::IoError(msg) => write!(f, "I/O error: {}", msg),
604 Self::ConfigError(msg) => write!(f, "Configuration error: {}", msg),
605 Self::EncodingError(msg) => write!(f, "Encoding error: {}", msg),
606 }
607 }
608}
609
610impl std::error::Error for EncoderError {}
611
612#[cfg(test)]
613mod tests {
614 use super::*;
615 use crate::frame::Plane;
616 use oximedia_core::{PixelFormat, Rational, Timestamp};
617
618 fn create_test_frame(width: u32, height: u32) -> VideoFrame {
619 let mut frame = VideoFrame::new(PixelFormat::Yuv420p, width, height);
620 let size = (width * height) as usize;
621 let data = vec![128u8; size];
622 frame.planes.push(Plane::new(data, width as usize));
623 frame.timestamp = Timestamp::new(0, Rational::new(1, 30));
624 frame
625 }
626
627 #[test]
628 fn test_encoder_config_new() {
629 let config = EncoderConfig::new(1920, 1080);
630 assert_eq!(config.width, 1920);
631 assert_eq!(config.height, 1080);
632 assert_eq!(config.pass, PassType::SinglePassLookahead);
633 }
634
635 #[test]
636 fn test_encoder_config_builder() {
637 let config = EncoderConfig::new(1920, 1080)
638 .with_pass(PassType::FirstPass)
639 .with_lookahead_frames(50)
640 .with_target_bitrate(10_000_000);
641
642 assert_eq!(config.pass, PassType::FirstPass);
643 assert_eq!(config.lookahead_frames, 50);
644 assert_eq!(config.target_bitrate, 10_000_000);
645 }
646
647 #[test]
648 fn test_multipass_encoder_new() {
649 let config = EncoderConfig::new(1920, 1080);
650 let encoder = MultiPassEncoder::new(config);
651 assert_eq!(encoder.frame_count(), 0);
652 }
653
654 #[test]
655 fn test_first_pass_encoding() {
656 let config = EncoderConfig::new(320, 240).with_pass(PassType::FirstPass);
657 let mut encoder = MultiPassEncoder::new(config);
658
659 let frame = create_test_frame(320, 240);
660 let result = encoder.encode_frame(&frame);
661
662 assert!(result.is_ok());
663 assert!(result.expect("should succeed").is_none()); assert_eq!(encoder.frame_count(), 1);
665 }
666
667 #[test]
668 fn test_single_pass_lookahead() {
669 let config = EncoderConfig::new(320, 240)
670 .with_pass(PassType::SinglePassLookahead)
671 .with_lookahead_frames(10);
672
673 let mut encoder = MultiPassEncoder::new(config);
674
675 for _ in 0..15 {
677 let frame = create_test_frame(320, 240);
678 let result = encoder.encode_frame(&frame);
679 assert!(result.is_ok());
680 }
681
682 assert!(encoder.frame_count() > 0);
683 }
684}