Skip to main content

oximedia_codec/
frame.rs

1//! Video frame types.
2
3use oximedia_core::{PixelFormat, Rational, Timestamp};
4
5/// Decoded video frame.
6#[derive(Clone, Debug)]
7pub struct VideoFrame {
8    /// Pixel format.
9    pub format: PixelFormat,
10    /// Frame width in pixels.
11    pub width: u32,
12    /// Frame height in pixels.
13    pub height: u32,
14    /// Plane data.
15    pub planes: Vec<Plane>,
16    /// Presentation timestamp.
17    pub timestamp: Timestamp,
18    /// Frame type (I/P/B).
19    pub frame_type: FrameType,
20    /// Color information.
21    pub color_info: ColorInfo,
22    /// Frame is corrupt (concealment applied).
23    pub corrupt: bool,
24}
25
26impl VideoFrame {
27    /// Create a new video frame.
28    #[must_use]
29    pub fn new(format: PixelFormat, width: u32, height: u32) -> Self {
30        Self {
31            format,
32            width,
33            height,
34            planes: Vec::new(),
35            timestamp: Timestamp::new(0, Rational::new(1, 1000)),
36            frame_type: FrameType::Key,
37            color_info: ColorInfo::default(),
38            corrupt: false,
39        }
40    }
41
42    /// Allocate planes for the frame format.
43    pub fn allocate(&mut self) {
44        let plane_count = self.format.plane_count();
45        self.planes.clear();
46
47        for i in 0..plane_count {
48            let (width, height) = self.plane_dimensions(i as usize);
49            let stride = width as usize;
50            let size = stride * height as usize;
51            let data = vec![0u8; size];
52
53            self.planes.push(Plane {
54                data,
55                stride,
56                width,
57                height,
58            });
59        }
60    }
61
62    /// Get plane dimensions.
63    #[must_use]
64    pub fn plane_dimensions(&self, plane_index: usize) -> (u32, u32) {
65        let (h_ratio, v_ratio) = self.format.chroma_subsampling();
66
67        if plane_index == 0 {
68            // Luma plane
69            (self.width, self.height)
70        } else {
71            // Chroma planes - use ratio directly for division
72            let chroma_width = self.width.div_ceil(h_ratio);
73            let chroma_height = self.height.div_ceil(v_ratio);
74            (chroma_width, chroma_height)
75        }
76    }
77
78    /// Get total frame size in bytes.
79    #[must_use]
80    pub fn size_bytes(&self) -> usize {
81        self.planes.iter().map(|p| p.data.len()).sum()
82    }
83
84    /// Check if frame is a keyframe.
85    #[must_use]
86    pub fn is_keyframe(&self) -> bool {
87        self.frame_type == FrameType::Key
88    }
89
90    /// Get reference to plane by index.
91    #[must_use]
92    pub fn plane(&self, index: usize) -> &Plane {
93        &self.planes[index]
94    }
95
96    /// Get mutable reference to plane by index.
97    #[must_use]
98    pub fn plane_mut(&mut self, index: usize) -> &mut Plane {
99        &mut self.planes[index]
100    }
101}
102
103/// Single plane of video data.
104#[derive(Clone, Debug)]
105pub struct Plane {
106    /// Pixel data.
107    pub data: Vec<u8>,
108    /// Row stride in bytes.
109    pub stride: usize,
110    /// Plane width in pixels.
111    pub width: u32,
112    /// Plane height in pixels.
113    pub height: u32,
114}
115
116impl Plane {
117    /// Create a new plane.
118    #[must_use]
119    pub fn new(data: Vec<u8>, stride: usize) -> Self {
120        Self {
121            data,
122            stride,
123            width: 0,
124            height: 0,
125        }
126    }
127
128    /// Create a new plane with width and height.
129    #[must_use]
130    pub fn with_dimensions(data: Vec<u8>, stride: usize, width: u32, height: u32) -> Self {
131        Self {
132            data,
133            stride,
134            width,
135            height,
136        }
137    }
138
139    /// Get row at given y coordinate.
140    #[must_use]
141    pub fn row(&self, y: usize) -> &[u8] {
142        let start = y * self.stride;
143        let end = start + self.stride;
144        if end <= self.data.len() {
145            &self.data[start..end]
146        } else {
147            &[]
148        }
149    }
150
151    /// Get plane width.
152    #[must_use]
153    pub const fn width(&self) -> u32 {
154        self.width
155    }
156
157    /// Get plane height.
158    #[must_use]
159    pub const fn height(&self) -> u32 {
160        self.height
161    }
162
163    /// Get plane stride.
164    #[must_use]
165    pub const fn stride(&self) -> usize {
166        self.stride
167    }
168
169    /// Get immutable reference to plane data.
170    #[must_use]
171    pub fn data(&self) -> &[u8] {
172        &self.data
173    }
174
175    /// Get mutable reference to plane data.
176    #[must_use]
177    pub fn data_mut(&mut self) -> &mut [u8] {
178        &mut self.data
179    }
180}
181
182/// Frame type.
183#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
184pub enum FrameType {
185    /// Keyframe (I-frame) - can be decoded independently.
186    #[default]
187    Key,
188    /// Inter frame (P-frame) - references previous frames.
189    Inter,
190    /// Bidirectional frame (B-frame) - references past and future.
191    BiDir,
192    /// Switch frame - allows stream switching.
193    Switch,
194}
195
196impl FrameType {
197    /// Check if this is a reference frame.
198    #[must_use]
199    pub fn is_reference(&self) -> bool {
200        matches!(self, Self::Key | Self::Inter)
201    }
202}
203
204/// Color information for video frames.
205#[derive(Clone, Copy, Debug, Default)]
206pub struct ColorInfo {
207    /// Color primaries.
208    pub primaries: ColorPrimaries,
209    /// Transfer characteristics.
210    pub transfer: TransferCharacteristics,
211    /// Matrix coefficients.
212    pub matrix: MatrixCoefficients,
213    /// Full range (0-255) vs limited range (16-235).
214    pub full_range: bool,
215}
216
217/// Color primaries (ITU-T H.273).
218#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
219pub enum ColorPrimaries {
220    /// BT.709 (sRGB, HD).
221    #[default]
222    Bt709 = 1,
223    /// Unspecified.
224    Unspecified = 2,
225    /// BT.470M (obsolete NTSC).
226    Bt470M = 4,
227    /// BT.470BG (PAL/SECAM).
228    Bt470Bg = 5,
229    /// SMPTE 170M (NTSC).
230    Smpte170M = 6,
231    /// SMPTE 240M.
232    Smpte240M = 7,
233    /// Generic film.
234    Film = 8,
235    /// BT.2020 (UHD).
236    Bt2020 = 9,
237    /// SMPTE ST 428-1.
238    Smpte428 = 10,
239    /// SMPTE RP 431-2.
240    Smpte431 = 11,
241    /// SMPTE EG 432-1 (P3).
242    Smpte432 = 12,
243    /// EBU Tech 3213-E.
244    Ebu3213 = 22,
245}
246
247/// Transfer characteristics (ITU-T H.273).
248#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
249pub enum TransferCharacteristics {
250    /// BT.709.
251    #[default]
252    Bt709 = 1,
253    /// Unspecified.
254    Unspecified = 2,
255    /// BT.470M.
256    Bt470M = 4,
257    /// BT.470BG.
258    Bt470Bg = 5,
259    /// SMPTE 170M.
260    Smpte170M = 6,
261    /// SMPTE 240M.
262    Smpte240M = 7,
263    /// Linear.
264    Linear = 8,
265    /// Logarithmic 100:1.
266    Log100 = 9,
267    /// Logarithmic 100*sqrt(10):1.
268    Log316 = 10,
269    /// IEC 61966-2-4.
270    Iec619662_4 = 11,
271    /// BT.1361.
272    Bt1361 = 12,
273    /// sRGB/sYCC.
274    Srgb = 13,
275    /// BT.2020 10-bit.
276    Bt202010 = 14,
277    /// BT.2020 12-bit.
278    Bt202012 = 15,
279    /// SMPTE ST 2084 (PQ/HDR10).
280    Smpte2084 = 16,
281    /// SMPTE ST 428-1.
282    Smpte428 = 17,
283    /// ARIB STD-B67 (HLG).
284    AribStdB67 = 18,
285}
286
287/// Matrix coefficients (ITU-T H.273).
288#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
289pub enum MatrixCoefficients {
290    /// Identity (RGB).
291    Identity = 0,
292    /// BT.709.
293    #[default]
294    Bt709 = 1,
295    /// Unspecified.
296    Unspecified = 2,
297    /// FCC.
298    Fcc = 4,
299    /// BT.470BG.
300    Bt470Bg = 5,
301    /// SMPTE 170M.
302    Smpte170M = 6,
303    /// SMPTE 240M.
304    Smpte240M = 7,
305    /// `YCgCo`.
306    Ycgco = 8,
307    /// BT.2020 non-constant.
308    Bt2020Ncl = 9,
309    /// BT.2020 constant.
310    Bt2020Cl = 10,
311    /// SMPTE 2085.
312    Smpte2085 = 11,
313    /// Chromaticity-derived non-constant.
314    ChromaDerivedNcl = 12,
315    /// Chromaticity-derived constant.
316    ChromaDerivedCl = 13,
317    /// `ICtCp`.
318    Ictcp = 14,
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324
325    #[test]
326    fn test_video_frame_new() {
327        let frame = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
328        assert_eq!(frame.width, 1920);
329        assert_eq!(frame.height, 1080);
330        assert_eq!(frame.format, PixelFormat::Yuv420p);
331    }
332
333    #[test]
334    fn test_plane_dimensions() {
335        let frame = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
336        assert_eq!(frame.plane_dimensions(0), (1920, 1080));
337        assert_eq!(frame.plane_dimensions(1), (960, 540));
338        assert_eq!(frame.plane_dimensions(2), (960, 540));
339    }
340
341    #[test]
342    fn test_frame_allocate() {
343        let mut frame = VideoFrame::new(PixelFormat::Yuv420p, 1920, 1080);
344        frame.allocate();
345        assert_eq!(frame.planes.len(), 3);
346        // Y: 1920 * 1080, U: 960 * 540, V: 960 * 540
347        assert_eq!(frame.planes[0].data.len(), 1920 * 1080);
348        assert_eq!(frame.planes[1].data.len(), 960 * 540);
349    }
350
351    #[test]
352    fn test_frame_type() {
353        assert!(FrameType::Key.is_reference());
354        assert!(FrameType::Inter.is_reference());
355        assert!(!FrameType::BiDir.is_reference());
356    }
357}