Skip to main content

oximedia_codec/reconstruct/
buffer.rs

1//! Frame buffer management for decoded video frames.
2//!
3//! This module provides buffer types for storing decoded video data,
4//! including frame buffers, plane buffers, reference frame management,
5//! and a buffer pool for efficient memory reuse.
6
7#![forbid(unsafe_code)]
8#![allow(clippy::unreadable_literal)]
9#![allow(clippy::items_after_statements)]
10#![allow(clippy::unnecessary_wraps)]
11#![allow(clippy::struct_excessive_bools)]
12#![allow(clippy::identity_op)]
13#![allow(clippy::range_plus_one)]
14#![allow(clippy::needless_range_loop)]
15#![allow(clippy::useless_conversion)]
16#![allow(clippy::redundant_closure_for_method_calls)]
17#![allow(clippy::single_match_else)]
18#![allow(dead_code)]
19#![allow(clippy::doc_markdown)]
20#![allow(clippy::unused_self)]
21#![allow(clippy::trivially_copy_pass_by_ref)]
22#![allow(clippy::cast_possible_truncation)]
23#![allow(clippy::cast_sign_loss)]
24#![allow(clippy::missing_errors_doc)]
25#![allow(clippy::similar_names)]
26#![allow(clippy::cast_precision_loss)]
27#![allow(clippy::cast_lossless)]
28#![allow(clippy::many_single_char_names)]
29#![allow(clippy::manual_div_ceil)]
30#![allow(clippy::too_many_arguments)]
31
32use super::{ChromaSubsampling, PlaneType, ReconstructResult, ReconstructionError, NUM_REF_FRAMES};
33use std::collections::VecDeque;
34
35// =============================================================================
36// Plane Buffer
37// =============================================================================
38
39/// Buffer for a single video plane (Y, U, or V).
40#[derive(Clone, Debug)]
41pub struct PlaneBuffer {
42    /// Pixel data stored as i16 for intermediate precision.
43    data: Vec<i16>,
44    /// Width in pixels.
45    width: u32,
46    /// Height in pixels.
47    height: u32,
48    /// Row stride in samples.
49    stride: usize,
50    /// Bit depth.
51    bit_depth: u8,
52    /// Plane type.
53    plane_type: PlaneType,
54}
55
56impl PlaneBuffer {
57    /// Create a new plane buffer.
58    #[must_use]
59    pub fn new(width: u32, height: u32, bit_depth: u8, plane_type: PlaneType) -> Self {
60        // Add padding for SIMD alignment (16-byte aligned, 8 i16 values)
61        let aligned_width = ((width as usize + 7) / 8) * 8;
62        let stride = aligned_width;
63        let size = stride * height as usize;
64
65        Self {
66            data: vec![0i16; size],
67            width,
68            height,
69            stride,
70            bit_depth,
71            plane_type,
72        }
73    }
74
75    /// Create a plane buffer with custom stride.
76    #[must_use]
77    pub fn with_stride(
78        width: u32,
79        height: u32,
80        stride: usize,
81        bit_depth: u8,
82        plane_type: PlaneType,
83    ) -> Self {
84        let size = stride * height as usize;
85
86        Self {
87            data: vec![0i16; size],
88            width,
89            height,
90            stride,
91            bit_depth,
92            plane_type,
93        }
94    }
95
96    /// Get the plane width.
97    #[must_use]
98    pub const fn width(&self) -> u32 {
99        self.width
100    }
101
102    /// Get the plane height.
103    #[must_use]
104    pub const fn height(&self) -> u32 {
105        self.height
106    }
107
108    /// Get the row stride.
109    #[must_use]
110    pub const fn stride(&self) -> usize {
111        self.stride
112    }
113
114    /// Get the bit depth.
115    #[must_use]
116    pub const fn bit_depth(&self) -> u8 {
117        self.bit_depth
118    }
119
120    /// Get the plane type.
121    #[must_use]
122    pub const fn plane_type(&self) -> PlaneType {
123        self.plane_type
124    }
125
126    /// Get the maximum pixel value for this bit depth.
127    #[must_use]
128    pub fn max_value(&self) -> i16 {
129        (1i16 << self.bit_depth) - 1
130    }
131
132    /// Get a pixel value at (x, y).
133    #[must_use]
134    pub fn get(&self, x: u32, y: u32) -> i16 {
135        if x >= self.width || y >= self.height {
136            return 0;
137        }
138        let idx = y as usize * self.stride + x as usize;
139        self.data.get(idx).copied().unwrap_or(0)
140    }
141
142    /// Set a pixel value at (x, y).
143    pub fn set(&mut self, x: u32, y: u32, value: i16) {
144        if x < self.width && y < self.height {
145            let idx = y as usize * self.stride + x as usize;
146            if idx < self.data.len() {
147                self.data[idx] = value;
148            }
149        }
150    }
151
152    /// Set a pixel value with clamping to valid range.
153    pub fn set_clamped(&mut self, x: u32, y: u32, value: i16) {
154        let max_val = self.max_value();
155        let clamped = value.clamp(0, max_val);
156        self.set(x, y, clamped);
157    }
158
159    /// Get a row of pixels.
160    #[must_use]
161    pub fn row(&self, y: u32) -> &[i16] {
162        if y >= self.height {
163            return &[];
164        }
165        let start = y as usize * self.stride;
166        let end = start + self.width as usize;
167        if end <= self.data.len() {
168            &self.data[start..end]
169        } else {
170            &[]
171        }
172    }
173
174    /// Get a mutable row of pixels.
175    pub fn row_mut(&mut self, y: u32) -> &mut [i16] {
176        if y >= self.height {
177            return &mut [];
178        }
179        let start = y as usize * self.stride;
180        let end = start + self.width as usize;
181        if end <= self.data.len() {
182            &mut self.data[start..end]
183        } else {
184            &mut []
185        }
186    }
187
188    /// Get the raw data slice.
189    #[must_use]
190    pub fn data(&self) -> &[i16] {
191        &self.data
192    }
193
194    /// Get the raw data as mutable slice.
195    pub fn data_mut(&mut self) -> &mut [i16] {
196        &mut self.data
197    }
198
199    /// Clear the buffer to zero.
200    pub fn clear(&mut self) {
201        self.data.fill(0);
202    }
203
204    /// Fill the buffer with a constant value.
205    pub fn fill(&mut self, value: i16) {
206        self.data.fill(value);
207    }
208
209    /// Copy from another plane buffer.
210    ///
211    /// # Errors
212    ///
213    /// Returns error if dimensions don't match.
214    pub fn copy_from(&mut self, other: &PlaneBuffer) -> ReconstructResult<()> {
215        if self.width != other.width || self.height != other.height {
216            return Err(ReconstructionError::InvalidDimensions {
217                width: other.width,
218                height: other.height,
219            });
220        }
221
222        if self.stride == other.stride {
223            self.data.copy_from_slice(&other.data);
224        } else {
225            let copy_width = self.width as usize;
226            for y in 0..self.height {
227                let src_row = other.row(y);
228                let dst_row = self.row_mut(y);
229                dst_row[..copy_width].copy_from_slice(&src_row[..copy_width]);
230            }
231        }
232
233        Ok(())
234    }
235
236    /// Copy a block from another plane buffer.
237    pub fn copy_block_from(
238        &mut self,
239        other: &PlaneBuffer,
240        src_x: u32,
241        src_y: u32,
242        dst_x: u32,
243        dst_y: u32,
244        width: u32,
245        height: u32,
246    ) {
247        for dy in 0..height {
248            for dx in 0..width {
249                let value = other.get(src_x + dx, src_y + dy);
250                self.set(dst_x + dx, dst_y + dy, value);
251            }
252        }
253    }
254
255    /// Convert to 8-bit output buffer.
256    #[must_use]
257    pub fn to_u8(&self) -> Vec<u8> {
258        let mut output = Vec::with_capacity(self.width as usize * self.height as usize);
259
260        for y in 0..self.height {
261            let row = self.row(y);
262            for &pixel in row.iter().take(self.width as usize) {
263                let value = if self.bit_depth <= 8 {
264                    pixel.clamp(0, 255) as u8
265                } else {
266                    // Scale down from higher bit depth
267                    let shift = self.bit_depth - 8;
268                    ((pixel >> shift).clamp(0, 255)) as u8
269                };
270                output.push(value);
271            }
272        }
273
274        output
275    }
276
277    /// Calculate the sum of absolute differences (SAD) with another plane.
278    #[must_use]
279    pub fn sad(&self, other: &PlaneBuffer, block_x: u32, block_y: u32, size: u32) -> u32 {
280        let mut sad: u32 = 0;
281        for dy in 0..size {
282            for dx in 0..size {
283                let a = self.get(block_x + dx, block_y + dy);
284                let b = other.get(block_x + dx, block_y + dy);
285                sad += (a - b).unsigned_abs() as u32;
286            }
287        }
288        sad
289    }
290}
291
292// =============================================================================
293// Frame Buffer
294// =============================================================================
295
296/// Buffer for a complete video frame (all planes).
297#[derive(Clone, Debug)]
298pub struct FrameBuffer {
299    /// Y (luma) plane.
300    y_plane: PlaneBuffer,
301    /// U (chroma blue) plane.
302    u_plane: Option<PlaneBuffer>,
303    /// V (chroma red) plane.
304    v_plane: Option<PlaneBuffer>,
305    /// Frame width.
306    width: u32,
307    /// Frame height.
308    height: u32,
309    /// Bit depth.
310    bit_depth: u8,
311    /// Chroma subsampling.
312    subsampling: ChromaSubsampling,
313    /// Frame timestamp.
314    timestamp: i64,
315    /// Frame is a keyframe.
316    is_keyframe: bool,
317    /// Unique buffer ID.
318    id: u64,
319}
320
321impl FrameBuffer {
322    /// Create a new frame buffer.
323    #[must_use]
324    pub fn new(width: u32, height: u32, bit_depth: u8, subsampling: ChromaSubsampling) -> Self {
325        static BUFFER_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
326
327        let y_plane = PlaneBuffer::new(width, height, bit_depth, PlaneType::Y);
328
329        let (u_plane, v_plane) = match subsampling {
330            ChromaSubsampling::Mono => (None, None),
331            _ => {
332                let (cw, ch) = subsampling.chroma_size(width, height);
333                (
334                    Some(PlaneBuffer::new(cw, ch, bit_depth, PlaneType::U)),
335                    Some(PlaneBuffer::new(cw, ch, bit_depth, PlaneType::V)),
336                )
337            }
338        };
339
340        Self {
341            y_plane,
342            u_plane,
343            v_plane,
344            width,
345            height,
346            bit_depth,
347            subsampling,
348            timestamp: 0,
349            is_keyframe: false,
350            id: BUFFER_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
351        }
352    }
353
354    /// Get the frame width.
355    #[must_use]
356    pub const fn width(&self) -> u32 {
357        self.width
358    }
359
360    /// Get the frame height.
361    #[must_use]
362    pub const fn height(&self) -> u32 {
363        self.height
364    }
365
366    /// Get the bit depth.
367    #[must_use]
368    pub const fn bit_depth(&self) -> u8 {
369        self.bit_depth
370    }
371
372    /// Get the chroma subsampling.
373    #[must_use]
374    pub const fn subsampling(&self) -> ChromaSubsampling {
375        self.subsampling
376    }
377
378    /// Get the buffer ID.
379    #[must_use]
380    pub const fn id(&self) -> u64 {
381        self.id
382    }
383
384    /// Get the timestamp.
385    #[must_use]
386    pub const fn timestamp(&self) -> i64 {
387        self.timestamp
388    }
389
390    /// Set the timestamp.
391    pub fn set_timestamp(&mut self, timestamp: i64) {
392        self.timestamp = timestamp;
393    }
394
395    /// Check if this is a keyframe.
396    #[must_use]
397    pub const fn is_keyframe(&self) -> bool {
398        self.is_keyframe
399    }
400
401    /// Set keyframe flag.
402    pub fn set_keyframe(&mut self, is_keyframe: bool) {
403        self.is_keyframe = is_keyframe;
404    }
405
406    /// Get the Y plane.
407    #[must_use]
408    pub fn y_plane(&self) -> &PlaneBuffer {
409        &self.y_plane
410    }
411
412    /// Get the Y plane mutably.
413    pub fn y_plane_mut(&mut self) -> &mut PlaneBuffer {
414        &mut self.y_plane
415    }
416
417    /// Get the U plane.
418    #[must_use]
419    pub fn u_plane(&self) -> Option<&PlaneBuffer> {
420        self.u_plane.as_ref()
421    }
422
423    /// Get the U plane mutably.
424    pub fn u_plane_mut(&mut self) -> Option<&mut PlaneBuffer> {
425        self.u_plane.as_mut()
426    }
427
428    /// Get the V plane.
429    #[must_use]
430    pub fn v_plane(&self) -> Option<&PlaneBuffer> {
431        self.v_plane.as_ref()
432    }
433
434    /// Get the V plane mutably.
435    pub fn v_plane_mut(&mut self) -> Option<&mut PlaneBuffer> {
436        self.v_plane.as_mut()
437    }
438
439    /// Get a plane by type.
440    #[must_use]
441    pub fn plane(&self, plane_type: PlaneType) -> Option<&PlaneBuffer> {
442        match plane_type {
443            PlaneType::Y => Some(&self.y_plane),
444            PlaneType::U => self.u_plane.as_ref(),
445            PlaneType::V => self.v_plane.as_ref(),
446        }
447    }
448
449    /// Get a plane mutably by type.
450    pub fn plane_mut(&mut self, plane_type: PlaneType) -> Option<&mut PlaneBuffer> {
451        match plane_type {
452            PlaneType::Y => Some(&mut self.y_plane),
453            PlaneType::U => self.u_plane.as_mut(),
454            PlaneType::V => self.v_plane.as_mut(),
455        }
456    }
457
458    /// Get the number of planes.
459    #[must_use]
460    pub const fn num_planes(&self) -> usize {
461        self.subsampling.num_planes()
462    }
463
464    /// Clear all planes.
465    pub fn clear(&mut self) {
466        self.y_plane.clear();
467        if let Some(ref mut u) = self.u_plane {
468            u.clear();
469        }
470        if let Some(ref mut v) = self.v_plane {
471            v.clear();
472        }
473    }
474
475    /// Copy from another frame buffer.
476    ///
477    /// # Errors
478    ///
479    /// Returns error if dimensions or format don't match.
480    pub fn copy_from(&mut self, other: &FrameBuffer) -> ReconstructResult<()> {
481        if self.width != other.width
482            || self.height != other.height
483            || self.subsampling != other.subsampling
484        {
485            return Err(ReconstructionError::InvalidDimensions {
486                width: other.width,
487                height: other.height,
488            });
489        }
490
491        self.y_plane.copy_from(&other.y_plane)?;
492
493        if let (Some(ref mut dst), Some(ref src)) = (&mut self.u_plane, &other.u_plane) {
494            dst.copy_from(src)?;
495        }
496
497        if let (Some(ref mut dst), Some(ref src)) = (&mut self.v_plane, &other.v_plane) {
498            dst.copy_from(src)?;
499        }
500
501        Ok(())
502    }
503
504    /// Calculate total buffer size in bytes.
505    #[must_use]
506    pub fn size_bytes(&self) -> usize {
507        let y_size = self.y_plane.data().len() * 2; // i16 = 2 bytes
508        let u_size = self.u_plane.as_ref().map_or(0, |p| p.data().len() * 2);
509        let v_size = self.v_plane.as_ref().map_or(0, |p| p.data().len() * 2);
510        y_size + u_size + v_size
511    }
512}
513
514// =============================================================================
515// Reference Frame Manager
516// =============================================================================
517
518/// Manages reference frames for inter prediction.
519#[derive(Debug)]
520pub struct ReferenceFrameManager {
521    /// Reference frame slots.
522    frames: [Option<FrameBuffer>; NUM_REF_FRAMES],
523    /// Frame order hints for each slot.
524    order_hints: [u32; NUM_REF_FRAMES],
525    /// Current frame number.
526    current_frame: u64,
527}
528
529impl Default for ReferenceFrameManager {
530    fn default() -> Self {
531        Self::new()
532    }
533}
534
535impl ReferenceFrameManager {
536    /// Create a new reference frame manager.
537    #[must_use]
538    pub fn new() -> Self {
539        Self {
540            frames: Default::default(),
541            order_hints: [0; NUM_REF_FRAMES],
542            current_frame: 0,
543        }
544    }
545
546    /// Get a reference frame by slot index.
547    #[must_use]
548    pub fn get(&self, index: usize) -> Option<&FrameBuffer> {
549        self.frames.get(index).and_then(|f| f.as_ref())
550    }
551
552    /// Store a frame in a slot.
553    pub fn store(&mut self, index: usize, frame: FrameBuffer, order_hint: u32) {
554        if index < NUM_REF_FRAMES {
555            self.frames[index] = Some(frame);
556            self.order_hints[index] = order_hint;
557        }
558    }
559
560    /// Clear a reference slot.
561    pub fn clear_slot(&mut self, index: usize) {
562        if index < NUM_REF_FRAMES {
563            self.frames[index] = None;
564            self.order_hints[index] = 0;
565        }
566    }
567
568    /// Clear all reference frames.
569    pub fn clear_all(&mut self) {
570        for i in 0..NUM_REF_FRAMES {
571            self.frames[i] = None;
572            self.order_hints[i] = 0;
573        }
574        self.current_frame = 0;
575    }
576
577    /// Get the order hint for a slot.
578    #[must_use]
579    pub fn order_hint(&self, index: usize) -> u32 {
580        self.order_hints.get(index).copied().unwrap_or(0)
581    }
582
583    /// Check if a slot has a valid reference.
584    #[must_use]
585    pub fn has_reference(&self, index: usize) -> bool {
586        self.frames.get(index).is_some_and(|f| f.is_some())
587    }
588
589    /// Get the current frame number.
590    #[must_use]
591    pub const fn current_frame(&self) -> u64 {
592        self.current_frame
593    }
594
595    /// Increment the frame counter.
596    pub fn next_frame(&mut self) {
597        self.current_frame += 1;
598    }
599
600    /// Get all valid reference indices.
601    #[must_use]
602    pub fn valid_references(&self) -> Vec<usize> {
603        (0..NUM_REF_FRAMES)
604            .filter(|&i| self.has_reference(i))
605            .collect()
606    }
607}
608
609// =============================================================================
610// Buffer Pool
611// =============================================================================
612
613/// Pool of reusable frame buffers.
614#[derive(Debug)]
615pub struct BufferPool {
616    /// Available buffers.
617    available: VecDeque<FrameBuffer>,
618    /// Buffer dimensions.
619    width: u32,
620    /// Buffer height.
621    height: u32,
622    /// Bit depth.
623    bit_depth: u8,
624    /// Chroma subsampling.
625    subsampling: ChromaSubsampling,
626    /// Maximum pool size.
627    max_size: usize,
628}
629
630impl BufferPool {
631    /// Create a new buffer pool.
632    #[must_use]
633    pub fn new(
634        width: u32,
635        height: u32,
636        bit_depth: u8,
637        subsampling: ChromaSubsampling,
638        max_size: usize,
639    ) -> Self {
640        Self {
641            available: VecDeque::with_capacity(max_size),
642            width,
643            height,
644            bit_depth,
645            subsampling,
646            max_size,
647        }
648    }
649
650    /// Acquire a buffer from the pool.
651    ///
652    /// Returns a buffer from the pool if available, otherwise allocates a new one.
653    ///
654    /// # Errors
655    ///
656    /// Returns error if allocation fails.
657    pub fn acquire(&mut self) -> ReconstructResult<FrameBuffer> {
658        if let Some(mut buffer) = self.available.pop_front() {
659            buffer.clear();
660            Ok(buffer)
661        } else {
662            Ok(FrameBuffer::new(
663                self.width,
664                self.height,
665                self.bit_depth,
666                self.subsampling,
667            ))
668        }
669    }
670
671    /// Release a buffer back to the pool.
672    pub fn release(&mut self, buffer: FrameBuffer) {
673        // Only keep buffers that match current dimensions
674        if buffer.width == self.width
675            && buffer.height == self.height
676            && buffer.bit_depth == self.bit_depth
677            && self.available.len() < self.max_size
678        {
679            self.available.push_back(buffer);
680        }
681    }
682
683    /// Get the number of available buffers.
684    #[must_use]
685    pub fn available_count(&self) -> usize {
686        self.available.len()
687    }
688
689    /// Check if the pool is empty.
690    #[must_use]
691    pub fn is_empty(&self) -> bool {
692        self.available.is_empty()
693    }
694
695    /// Reset the pool, clearing all buffers.
696    pub fn reset(&mut self) {
697        self.available.clear();
698    }
699
700    /// Reconfigure the pool for new dimensions.
701    pub fn reconfigure(
702        &mut self,
703        width: u32,
704        height: u32,
705        bit_depth: u8,
706        subsampling: ChromaSubsampling,
707    ) {
708        self.width = width;
709        self.height = height;
710        self.bit_depth = bit_depth;
711        self.subsampling = subsampling;
712        self.available.clear();
713    }
714}
715
716// =============================================================================
717// Tests
718// =============================================================================
719
720#[cfg(test)]
721mod tests {
722    use super::*;
723
724    #[test]
725    fn test_plane_buffer_new() {
726        let plane = PlaneBuffer::new(64, 48, 8, PlaneType::Y);
727        assert_eq!(plane.width(), 64);
728        assert_eq!(plane.height(), 48);
729        assert_eq!(plane.bit_depth(), 8);
730        assert_eq!(plane.plane_type(), PlaneType::Y);
731    }
732
733    #[test]
734    fn test_plane_buffer_get_set() {
735        let mut plane = PlaneBuffer::new(64, 48, 8, PlaneType::Y);
736        plane.set(10, 20, 128);
737        assert_eq!(plane.get(10, 20), 128);
738        assert_eq!(plane.get(0, 0), 0);
739    }
740
741    #[test]
742    fn test_plane_buffer_set_clamped() {
743        let mut plane = PlaneBuffer::new(64, 48, 8, PlaneType::Y);
744        plane.set_clamped(10, 20, 300);
745        assert_eq!(plane.get(10, 20), 255);
746
747        plane.set_clamped(10, 20, -50);
748        assert_eq!(plane.get(10, 20), 0);
749    }
750
751    #[test]
752    fn test_plane_buffer_row() {
753        let mut plane = PlaneBuffer::new(64, 48, 8, PlaneType::Y);
754        plane.set(5, 10, 100);
755        let row = plane.row(10);
756        assert_eq!(row[5], 100);
757    }
758
759    #[test]
760    fn test_plane_buffer_max_value() {
761        let plane8 = PlaneBuffer::new(64, 48, 8, PlaneType::Y);
762        assert_eq!(plane8.max_value(), 255);
763
764        let plane10 = PlaneBuffer::new(64, 48, 10, PlaneType::Y);
765        assert_eq!(plane10.max_value(), 1023);
766
767        let plane12 = PlaneBuffer::new(64, 48, 12, PlaneType::Y);
768        assert_eq!(plane12.max_value(), 4095);
769    }
770
771    #[test]
772    fn test_plane_buffer_copy_from() {
773        let mut src = PlaneBuffer::new(64, 48, 8, PlaneType::Y);
774        src.set(10, 20, 128);
775
776        let mut dst = PlaneBuffer::new(64, 48, 8, PlaneType::Y);
777        dst.copy_from(&src).expect("should succeed");
778
779        assert_eq!(dst.get(10, 20), 128);
780    }
781
782    #[test]
783    fn test_plane_buffer_to_u8() {
784        let mut plane = PlaneBuffer::new(4, 4, 8, PlaneType::Y);
785        plane.set(0, 0, 128);
786        plane.set(1, 0, 255);
787
788        let output = plane.to_u8();
789        assert_eq!(output.len(), 16);
790        assert_eq!(output[0], 128);
791        assert_eq!(output[1], 255);
792    }
793
794    #[test]
795    fn test_frame_buffer_new() {
796        let frame = FrameBuffer::new(1920, 1080, 8, ChromaSubsampling::Cs420);
797        assert_eq!(frame.width(), 1920);
798        assert_eq!(frame.height(), 1080);
799        assert_eq!(frame.bit_depth(), 8);
800        assert_eq!(frame.num_planes(), 3);
801    }
802
803    #[test]
804    fn test_frame_buffer_mono() {
805        let frame = FrameBuffer::new(1920, 1080, 8, ChromaSubsampling::Mono);
806        assert_eq!(frame.num_planes(), 1);
807        assert!(frame.u_plane().is_none());
808        assert!(frame.v_plane().is_none());
809    }
810
811    #[test]
812    fn test_frame_buffer_planes() {
813        let mut frame = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
814
815        frame.y_plane_mut().set(10, 10, 100);
816        assert_eq!(frame.y_plane().get(10, 10), 100);
817
818        if let Some(u) = frame.u_plane_mut() {
819            u.set(5, 5, 50);
820        }
821        assert_eq!(
822            frame.u_plane().expect("get should return value").get(5, 5),
823            50
824        );
825    }
826
827    #[test]
828    fn test_frame_buffer_copy_from() {
829        let mut src = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
830        src.y_plane_mut().set(10, 10, 100);
831
832        let mut dst = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
833        dst.copy_from(&src).expect("should succeed");
834
835        assert_eq!(dst.y_plane().get(10, 10), 100);
836    }
837
838    #[test]
839    fn test_reference_frame_manager() {
840        let mut mgr = ReferenceFrameManager::new();
841
842        assert!(!mgr.has_reference(0));
843
844        let frame = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
845        mgr.store(0, frame, 1);
846
847        assert!(mgr.has_reference(0));
848        assert_eq!(mgr.order_hint(0), 1);
849
850        let refs = mgr.valid_references();
851        assert_eq!(refs.len(), 1);
852        assert_eq!(refs[0], 0);
853    }
854
855    #[test]
856    fn test_reference_frame_manager_clear() {
857        let mut mgr = ReferenceFrameManager::new();
858
859        let frame = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
860        mgr.store(0, frame, 1);
861
862        mgr.clear_all();
863        assert!(!mgr.has_reference(0));
864    }
865
866    #[test]
867    fn test_buffer_pool() {
868        let mut pool = BufferPool::new(64, 48, 8, ChromaSubsampling::Cs420, 4);
869
870        assert!(pool.is_empty());
871
872        let buf1 = pool.acquire().expect("should succeed");
873        assert_eq!(buf1.width(), 64);
874
875        pool.release(buf1);
876        assert_eq!(pool.available_count(), 1);
877
878        let buf2 = pool.acquire().expect("should succeed");
879        assert!(pool.is_empty());
880        assert_eq!(buf2.width(), 64);
881    }
882
883    #[test]
884    fn test_buffer_pool_max_size() {
885        let mut pool = BufferPool::new(64, 48, 8, ChromaSubsampling::Cs420, 2);
886
887        let buf1 = pool.acquire().expect("should succeed");
888        let buf2 = pool.acquire().expect("should succeed");
889        let buf3 = pool.acquire().expect("should succeed");
890
891        pool.release(buf1);
892        pool.release(buf2);
893        pool.release(buf3); // This one should be dropped (exceeds max_size)
894
895        assert_eq!(pool.available_count(), 2);
896    }
897
898    #[test]
899    fn test_buffer_pool_reconfigure() {
900        let mut pool = BufferPool::new(64, 48, 8, ChromaSubsampling::Cs420, 4);
901
902        let buf = pool.acquire().expect("should succeed");
903        pool.release(buf);
904
905        pool.reconfigure(128, 96, 10, ChromaSubsampling::Cs422);
906        assert!(pool.is_empty());
907
908        let new_buf = pool.acquire().expect("should succeed");
909        assert_eq!(new_buf.width(), 128);
910        assert_eq!(new_buf.height(), 96);
911        assert_eq!(new_buf.bit_depth(), 10);
912    }
913}