1#![forbid(unsafe_code)]
7#![allow(clippy::unreadable_literal)]
8#![allow(clippy::items_after_statements)]
9#![allow(clippy::unnecessary_wraps)]
10#![allow(clippy::struct_excessive_bools)]
11#![allow(clippy::identity_op)]
12#![allow(clippy::range_plus_one)]
13#![allow(clippy::needless_range_loop)]
14#![allow(clippy::useless_conversion)]
15#![allow(clippy::redundant_closure_for_method_calls)]
16#![allow(clippy::single_match_else)]
17#![allow(dead_code)]
18#![allow(clippy::doc_markdown)]
19#![allow(clippy::unused_self)]
20#![allow(clippy::trivially_copy_pass_by_ref)]
21#![allow(clippy::missing_errors_doc)]
22#![allow(clippy::similar_names)]
23#![allow(clippy::cast_possible_truncation)]
24#![allow(clippy::cast_precision_loss)]
25#![allow(clippy::cast_lossless)]
26#![allow(clippy::cast_sign_loss)]
27#![allow(clippy::needless_pass_by_value)]
28
29use super::{
30 BufferPool, CdefApplicator, ChromaSubsampling, DeblockFilter, FilmGrainSynthesizer,
31 FrameBuffer, LoopFilterPipeline, OutputFormatter, ReconstructResult, ReconstructionError,
32 ResidualBuffer, SuperResUpscaler, MAX_BIT_DEPTH, MAX_FRAME_HEIGHT, MAX_FRAME_WIDTH,
33 MIN_BIT_DEPTH, NUM_REF_FRAMES,
34};
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
42pub enum PipelineStage {
43 Parse,
45 Entropy,
47 Predict,
49 Transform,
51 Deblock,
53 LoopFilter,
55 Cdef,
57 SuperRes,
59 FilmGrain,
61 Output,
63}
64
65impl PipelineStage {
66 #[must_use]
68 pub const fn name(self) -> &'static str {
69 match self {
70 Self::Parse => "Parse",
71 Self::Entropy => "Entropy",
72 Self::Predict => "Predict",
73 Self::Transform => "Transform",
74 Self::Deblock => "Deblock",
75 Self::LoopFilter => "LoopFilter",
76 Self::Cdef => "CDEF",
77 Self::SuperRes => "SuperRes",
78 Self::FilmGrain => "FilmGrain",
79 Self::Output => "Output",
80 }
81 }
82
83 #[must_use]
85 pub const fn all_ordered() -> &'static [Self] {
86 &[
87 Self::Parse,
88 Self::Entropy,
89 Self::Predict,
90 Self::Transform,
91 Self::Deblock,
92 Self::LoopFilter,
93 Self::Cdef,
94 Self::SuperRes,
95 Self::FilmGrain,
96 Self::Output,
97 ]
98 }
99
100 #[must_use]
102 pub const fn is_filter(self) -> bool {
103 matches!(
104 self,
105 Self::Deblock | Self::LoopFilter | Self::Cdef | Self::FilmGrain
106 )
107 }
108
109 #[must_use]
111 pub const fn is_optional(self) -> bool {
112 matches!(
113 self,
114 Self::Deblock | Self::LoopFilter | Self::Cdef | Self::SuperRes | Self::FilmGrain
115 )
116 }
117}
118
119#[derive(Clone, Debug)]
125pub struct StageResult {
126 pub stage: PipelineStage,
128 pub processing_time_us: u64,
130 pub skipped: bool,
132 pub metrics: StageMetrics,
134}
135
136impl StageResult {
137 #[must_use]
139 pub fn new(stage: PipelineStage) -> Self {
140 Self {
141 stage,
142 processing_time_us: 0,
143 skipped: false,
144 metrics: StageMetrics::default(),
145 }
146 }
147
148 #[must_use]
150 pub fn skipped(stage: PipelineStage) -> Self {
151 Self {
152 stage,
153 processing_time_us: 0,
154 skipped: true,
155 metrics: StageMetrics::default(),
156 }
157 }
158
159 #[must_use]
161 pub const fn with_time(mut self, time_us: u64) -> Self {
162 self.processing_time_us = time_us;
163 self
164 }
165}
166
167#[derive(Clone, Debug, Default)]
169pub struct StageMetrics {
170 pub blocks_processed: u64,
172 pub pixels_processed: u64,
174 pub operations: u64,
176}
177
178#[derive(Clone, Debug)]
184pub struct PipelineConfig {
185 pub enable_deblock: bool,
187 pub enable_loop_filter: bool,
189 pub enable_cdef: bool,
191 pub enable_super_res: bool,
193 pub enable_film_grain: bool,
195 pub width: u32,
197 pub height: u32,
199 pub bit_depth: u8,
201 pub subsampling: ChromaSubsampling,
203 pub threads: usize,
205 pub buffer_pool_size: usize,
207 pub collect_metrics: bool,
209}
210
211impl Default for PipelineConfig {
212 fn default() -> Self {
213 Self {
214 enable_deblock: true,
215 enable_loop_filter: true,
216 enable_cdef: true,
217 enable_super_res: false,
218 enable_film_grain: false,
219 width: 1920,
220 height: 1080,
221 bit_depth: 8,
222 subsampling: ChromaSubsampling::Cs420,
223 threads: 1,
224 buffer_pool_size: 4,
225 collect_metrics: false,
226 }
227 }
228}
229
230impl PipelineConfig {
231 #[must_use]
233 pub fn new(width: u32, height: u32) -> Self {
234 Self {
235 width,
236 height,
237 ..Default::default()
238 }
239 }
240
241 #[must_use]
243 pub const fn with_bit_depth(mut self, bit_depth: u8) -> Self {
244 self.bit_depth = bit_depth;
245 self
246 }
247
248 #[must_use]
250 pub const fn with_subsampling(mut self, subsampling: ChromaSubsampling) -> Self {
251 self.subsampling = subsampling;
252 self
253 }
254
255 #[must_use]
257 pub const fn with_all_filters(mut self) -> Self {
258 self.enable_deblock = true;
259 self.enable_loop_filter = true;
260 self.enable_cdef = true;
261 self
262 }
263
264 #[must_use]
266 pub const fn without_filters(mut self) -> Self {
267 self.enable_deblock = false;
268 self.enable_loop_filter = false;
269 self.enable_cdef = false;
270 self.enable_film_grain = false;
271 self
272 }
273
274 #[must_use]
276 pub const fn with_threads(mut self, threads: usize) -> Self {
277 self.threads = threads;
278 self
279 }
280
281 pub fn validate(&self) -> ReconstructResult<()> {
283 if self.width == 0 || self.width > MAX_FRAME_WIDTH {
284 return Err(ReconstructionError::InvalidDimensions {
285 width: self.width,
286 height: self.height,
287 });
288 }
289
290 if self.height == 0 || self.height > MAX_FRAME_HEIGHT {
291 return Err(ReconstructionError::InvalidDimensions {
292 width: self.width,
293 height: self.height,
294 });
295 }
296
297 if self.bit_depth < MIN_BIT_DEPTH || self.bit_depth > MAX_BIT_DEPTH {
298 return Err(ReconstructionError::UnsupportedBitDepth(self.bit_depth));
299 }
300
301 Ok(())
302 }
303}
304
305#[derive(Clone, Debug)]
311pub struct FrameContext {
312 pub decode_order: u64,
314 pub display_order: u64,
316 pub width: u32,
318 pub height: u32,
320 pub bit_depth: u8,
322 pub is_keyframe: bool,
324 pub show_frame: bool,
326 pub ref_frame_indices: [Option<usize>; NUM_REF_FRAMES],
328 pub super_res_scale: f32,
330 pub has_film_grain: bool,
332}
333
334impl Default for FrameContext {
335 fn default() -> Self {
336 Self {
337 decode_order: 0,
338 display_order: 0,
339 width: 0,
340 height: 0,
341 bit_depth: 8,
342 is_keyframe: true,
343 show_frame: true,
344 ref_frame_indices: [None; NUM_REF_FRAMES],
345 super_res_scale: 1.0,
346 has_film_grain: false,
347 }
348 }
349}
350
351impl FrameContext {
352 #[must_use]
354 pub fn new(width: u32, height: u32) -> Self {
355 Self {
356 width,
357 height,
358 ..Default::default()
359 }
360 }
361
362 #[must_use]
364 pub fn needs_super_res(&self) -> bool {
365 (self.super_res_scale - 1.0).abs() > f32::EPSILON
366 }
367}
368
369#[derive(Debug)]
375pub struct DecoderPipeline {
376 config: PipelineConfig,
378 buffer_pool: BufferPool,
380 reference_frames: Vec<Option<FrameBuffer>>,
382 deblock_filter: DeblockFilter,
384 loop_filter: LoopFilterPipeline,
386 cdef: CdefApplicator,
388 super_res: SuperResUpscaler,
390 film_grain: FilmGrainSynthesizer,
392 output: OutputFormatter,
394 work_buffer: Option<FrameBuffer>,
396 residual_buffer: ResidualBuffer,
398 frame_count: u64,
400 last_stage_results: Vec<StageResult>,
402}
403
404impl DecoderPipeline {
405 pub fn new(config: PipelineConfig) -> ReconstructResult<Self> {
411 config.validate()?;
412
413 let buffer_pool = BufferPool::new(
414 config.width,
415 config.height,
416 config.bit_depth,
417 config.subsampling,
418 config.buffer_pool_size,
419 );
420
421 let reference_frames = vec![None; NUM_REF_FRAMES];
422 let residual_buffer = ResidualBuffer::new(config.width, config.height, config.subsampling);
423
424 Ok(Self {
425 config: config.clone(),
426 buffer_pool,
427 reference_frames,
428 deblock_filter: DeblockFilter::new(),
429 loop_filter: LoopFilterPipeline::new(),
430 cdef: CdefApplicator::new(config.width, config.height, config.bit_depth),
431 super_res: SuperResUpscaler::new(),
432 film_grain: FilmGrainSynthesizer::new(config.bit_depth),
433 output: OutputFormatter::new(),
434 work_buffer: None,
435 residual_buffer,
436 frame_count: 0,
437 last_stage_results: Vec::new(),
438 })
439 }
440
441 pub fn process_frame(
447 &mut self,
448 data: &[u8],
449 context: &FrameContext,
450 ) -> ReconstructResult<FrameBuffer> {
451 self.last_stage_results.clear();
452
453 self.work_buffer = Some(self.buffer_pool.acquire()?);
455
456 self.stage_parse(data, context)?;
458 self.stage_entropy(context)?;
459 self.stage_predict(context)?;
460 self.stage_transform(context)?;
461 self.stage_deblock(context)?;
462 self.stage_loop_filter(context)?;
463 self.stage_cdef(context)?;
464 self.stage_super_res(context)?;
465 self.stage_film_grain(context)?;
466
467 let output = self.work_buffer.take().ok_or_else(|| {
469 ReconstructionError::Internal("Work buffer not available".to_string())
470 })?;
471
472 if context.show_frame || context.is_keyframe {
474 self.update_references(&output, context);
475 }
476
477 self.frame_count += 1;
478
479 Ok(output)
480 }
481
482 fn stage_parse(&mut self, _data: &[u8], _context: &FrameContext) -> ReconstructResult<()> {
484 let result = StageResult::new(PipelineStage::Parse);
485 self.last_stage_results.push(result);
486 Ok(())
487 }
488
489 fn stage_entropy(&mut self, _context: &FrameContext) -> ReconstructResult<()> {
491 let result = StageResult::new(PipelineStage::Entropy);
492 self.last_stage_results.push(result);
493 Ok(())
494 }
495
496 fn stage_predict(&mut self, _context: &FrameContext) -> ReconstructResult<()> {
498 let result = StageResult::new(PipelineStage::Predict);
499 self.last_stage_results.push(result);
500 Ok(())
501 }
502
503 fn stage_transform(&mut self, _context: &FrameContext) -> ReconstructResult<()> {
505 let result = StageResult::new(PipelineStage::Transform);
506 self.last_stage_results.push(result);
507 Ok(())
508 }
509
510 fn stage_deblock(&mut self, context: &FrameContext) -> ReconstructResult<()> {
512 if !self.config.enable_deblock {
513 self.last_stage_results
514 .push(StageResult::skipped(PipelineStage::Deblock));
515 return Ok(());
516 }
517
518 if let Some(ref mut buffer) = self.work_buffer {
519 self.deblock_filter.apply(buffer, context)?;
520 }
521
522 let result = StageResult::new(PipelineStage::Deblock);
523 self.last_stage_results.push(result);
524 Ok(())
525 }
526
527 fn stage_loop_filter(&mut self, context: &FrameContext) -> ReconstructResult<()> {
529 if !self.config.enable_loop_filter {
530 self.last_stage_results
531 .push(StageResult::skipped(PipelineStage::LoopFilter));
532 return Ok(());
533 }
534
535 if let Some(ref mut buffer) = self.work_buffer {
536 self.loop_filter.apply(buffer, context)?;
537 }
538
539 let result = StageResult::new(PipelineStage::LoopFilter);
540 self.last_stage_results.push(result);
541 Ok(())
542 }
543
544 fn stage_cdef(&mut self, context: &FrameContext) -> ReconstructResult<()> {
546 if !self.config.enable_cdef {
547 self.last_stage_results
548 .push(StageResult::skipped(PipelineStage::Cdef));
549 return Ok(());
550 }
551
552 if let Some(ref mut buffer) = self.work_buffer {
553 self.cdef.apply(buffer, context)?;
554 }
555
556 let result = StageResult::new(PipelineStage::Cdef);
557 self.last_stage_results.push(result);
558 Ok(())
559 }
560
561 fn stage_super_res(&mut self, context: &FrameContext) -> ReconstructResult<()> {
563 if !self.config.enable_super_res || !context.needs_super_res() {
564 self.last_stage_results
565 .push(StageResult::skipped(PipelineStage::SuperRes));
566 return Ok(());
567 }
568
569 if let Some(ref mut buffer) = self.work_buffer {
570 self.super_res.apply(buffer, context)?;
571 }
572
573 let result = StageResult::new(PipelineStage::SuperRes);
574 self.last_stage_results.push(result);
575 Ok(())
576 }
577
578 fn stage_film_grain(&mut self, context: &FrameContext) -> ReconstructResult<()> {
580 if !self.config.enable_film_grain || !context.has_film_grain {
581 self.last_stage_results
582 .push(StageResult::skipped(PipelineStage::FilmGrain));
583 return Ok(());
584 }
585
586 if let Some(ref mut buffer) = self.work_buffer {
587 self.film_grain.apply(buffer, context)?;
588 }
589
590 let result = StageResult::new(PipelineStage::FilmGrain);
591 self.last_stage_results.push(result);
592 Ok(())
593 }
594
595 fn update_references(&mut self, frame: &FrameBuffer, context: &FrameContext) {
597 if context.is_keyframe {
599 for ref_frame in &mut self.reference_frames {
600 *ref_frame = None;
601 }
602 }
603
604 let slot = (self.frame_count as usize) % NUM_REF_FRAMES;
607 self.reference_frames[slot] = Some(frame.clone());
608 }
609
610 pub fn get_reference(&self, index: usize) -> ReconstructResult<&FrameBuffer> {
612 self.reference_frames
613 .get(index)
614 .and_then(|r| r.as_ref())
615 .ok_or(ReconstructionError::ReferenceNotAvailable(index))
616 }
617
618 #[must_use]
620 pub fn config(&self) -> &PipelineConfig {
621 &self.config
622 }
623
624 #[must_use]
626 pub const fn frame_count(&self) -> u64 {
627 self.frame_count
628 }
629
630 #[must_use]
632 pub fn last_stage_results(&self) -> &[StageResult] {
633 &self.last_stage_results
634 }
635
636 pub fn reset(&mut self) {
638 self.frame_count = 0;
639 self.work_buffer = None;
640 self.last_stage_results.clear();
641 for ref_frame in &mut self.reference_frames {
642 *ref_frame = None;
643 }
644 self.buffer_pool.reset();
645 self.residual_buffer.clear();
646 }
647
648 pub fn reconfigure(&mut self, config: PipelineConfig) -> ReconstructResult<()> {
654 config.validate()?;
655
656 let dimensions_changed =
658 self.config.width != config.width || self.config.height != config.height;
659
660 self.config = config.clone();
661
662 if dimensions_changed {
663 self.buffer_pool = BufferPool::new(
664 config.width,
665 config.height,
666 config.bit_depth,
667 config.subsampling,
668 config.buffer_pool_size,
669 );
670 self.residual_buffer =
671 ResidualBuffer::new(config.width, config.height, config.subsampling);
672 self.cdef = CdefApplicator::new(config.width, config.height, config.bit_depth);
673 }
674
675 Ok(())
676 }
677}
678
679#[cfg(test)]
684mod tests {
685 use super::*;
686
687 #[test]
688 fn test_pipeline_stage_name() {
689 assert_eq!(PipelineStage::Parse.name(), "Parse");
690 assert_eq!(PipelineStage::Cdef.name(), "CDEF");
691 assert_eq!(PipelineStage::SuperRes.name(), "SuperRes");
692 }
693
694 #[test]
695 fn test_pipeline_stage_is_filter() {
696 assert!(!PipelineStage::Parse.is_filter());
697 assert!(PipelineStage::Deblock.is_filter());
698 assert!(PipelineStage::LoopFilter.is_filter());
699 assert!(PipelineStage::Cdef.is_filter());
700 }
701
702 #[test]
703 fn test_pipeline_stage_is_optional() {
704 assert!(!PipelineStage::Parse.is_optional());
705 assert!(!PipelineStage::Entropy.is_optional());
706 assert!(PipelineStage::Deblock.is_optional());
707 assert!(PipelineStage::SuperRes.is_optional());
708 assert!(PipelineStage::FilmGrain.is_optional());
709 }
710
711 #[test]
712 fn test_stage_result() {
713 let result = StageResult::new(PipelineStage::Parse);
714 assert_eq!(result.stage, PipelineStage::Parse);
715 assert!(!result.skipped);
716
717 let skipped = StageResult::skipped(PipelineStage::Cdef);
718 assert!(skipped.skipped);
719 }
720
721 #[test]
722 fn test_pipeline_config_default() {
723 let config = PipelineConfig::default();
724 assert_eq!(config.width, 1920);
725 assert_eq!(config.height, 1080);
726 assert_eq!(config.bit_depth, 8);
727 assert!(config.enable_deblock);
728 assert!(config.enable_loop_filter);
729 assert!(config.enable_cdef);
730 }
731
732 #[test]
733 fn test_pipeline_config_builder() {
734 let config = PipelineConfig::new(1280, 720)
735 .with_bit_depth(10)
736 .with_subsampling(ChromaSubsampling::Cs422)
737 .with_threads(4);
738
739 assert_eq!(config.width, 1280);
740 assert_eq!(config.height, 720);
741 assert_eq!(config.bit_depth, 10);
742 assert_eq!(config.subsampling, ChromaSubsampling::Cs422);
743 assert_eq!(config.threads, 4);
744 }
745
746 #[test]
747 fn test_pipeline_config_validation() {
748 let valid = PipelineConfig::new(1920, 1080);
749 assert!(valid.validate().is_ok());
750
751 let invalid_width = PipelineConfig::new(0, 1080);
752 assert!(invalid_width.validate().is_err());
753
754 let invalid_height = PipelineConfig::new(1920, 0);
755 assert!(invalid_height.validate().is_err());
756
757 let invalid_bit_depth = PipelineConfig::new(1920, 1080).with_bit_depth(7);
758 assert!(invalid_bit_depth.validate().is_err());
759 }
760
761 #[test]
762 fn test_frame_context_default() {
763 let ctx = FrameContext::default();
764 assert_eq!(ctx.width, 0);
765 assert_eq!(ctx.height, 0);
766 assert!(ctx.is_keyframe);
767 assert!(ctx.show_frame);
768 }
769
770 #[test]
771 fn test_frame_context_super_res() {
772 let mut ctx = FrameContext::new(1920, 1080);
773 assert!(!ctx.needs_super_res());
774
775 ctx.super_res_scale = 1.5;
776 assert!(ctx.needs_super_res());
777 }
778
779 #[test]
780 fn test_decoder_pipeline_creation() {
781 let config = PipelineConfig::new(1920, 1080);
782 let pipeline = DecoderPipeline::new(config);
783 assert!(pipeline.is_ok());
784 }
785
786 #[test]
787 fn test_decoder_pipeline_process_frame() {
788 let config = PipelineConfig::new(64, 64).without_filters();
789 let mut pipeline = DecoderPipeline::new(config).expect("should succeed");
790
791 let context = FrameContext::new(64, 64);
792 let result = pipeline.process_frame(&[], &context);
793 assert!(result.is_ok());
794
795 assert_eq!(pipeline.frame_count(), 1);
796 }
797
798 #[test]
799 fn test_decoder_pipeline_reset() {
800 let config = PipelineConfig::new(64, 64).without_filters();
801 let mut pipeline = DecoderPipeline::new(config).expect("should succeed");
802
803 let context = FrameContext::new(64, 64);
804 let _ = pipeline.process_frame(&[], &context);
805
806 pipeline.reset();
807 assert_eq!(pipeline.frame_count(), 0);
808 }
809
810 #[test]
811 fn test_decoder_pipeline_reconfigure() {
812 let config = PipelineConfig::new(64, 64);
813 let mut pipeline = DecoderPipeline::new(config).expect("should succeed");
814
815 let new_config = PipelineConfig::new(128, 128);
816 assert!(pipeline.reconfigure(new_config).is_ok());
817 assert_eq!(pipeline.config().width, 128);
818 assert_eq!(pipeline.config().height, 128);
819 }
820
821 #[test]
822 fn test_stage_results() {
823 let config = PipelineConfig::new(64, 64).without_filters();
824 let mut pipeline = DecoderPipeline::new(config).expect("should succeed");
825
826 let context = FrameContext::new(64, 64);
827 let _ = pipeline.process_frame(&[], &context);
828
829 let results = pipeline.last_stage_results();
830 assert!(!results.is_empty());
831
832 for result in results {
834 if result.stage.is_filter() {
835 assert!(result.skipped);
836 }
837 }
838 }
839}