1use oximedia_core::{PixelFormat, Rational, Timestamp};
4
5#[derive(Clone, Debug)]
7pub struct VideoFrame {
8 pub format: PixelFormat,
10 pub width: u32,
12 pub height: u32,
14 pub planes: Vec<Plane>,
16 pub timestamp: Timestamp,
18 pub frame_type: FrameType,
20 pub color_info: ColorInfo,
22 pub corrupt: bool,
24}
25
26impl VideoFrame {
27 #[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 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 #[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 (self.width, self.height)
70 } else {
71 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 #[must_use]
80 pub fn size_bytes(&self) -> usize {
81 self.planes.iter().map(|p| p.data.len()).sum()
82 }
83
84 #[must_use]
86 pub fn is_keyframe(&self) -> bool {
87 self.frame_type == FrameType::Key
88 }
89
90 #[must_use]
92 pub fn plane(&self, index: usize) -> &Plane {
93 &self.planes[index]
94 }
95
96 #[must_use]
98 pub fn plane_mut(&mut self, index: usize) -> &mut Plane {
99 &mut self.planes[index]
100 }
101}
102
103#[derive(Clone, Debug)]
105pub struct Plane {
106 pub data: Vec<u8>,
108 pub stride: usize,
110 pub width: u32,
112 pub height: u32,
114}
115
116impl Plane {
117 #[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 #[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 #[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 #[must_use]
153 pub const fn width(&self) -> u32 {
154 self.width
155 }
156
157 #[must_use]
159 pub const fn height(&self) -> u32 {
160 self.height
161 }
162
163 #[must_use]
165 pub const fn stride(&self) -> usize {
166 self.stride
167 }
168
169 #[must_use]
171 pub fn data(&self) -> &[u8] {
172 &self.data
173 }
174
175 #[must_use]
177 pub fn data_mut(&mut self) -> &mut [u8] {
178 &mut self.data
179 }
180}
181
182#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
184pub enum FrameType {
185 #[default]
187 Key,
188 Inter,
190 BiDir,
192 Switch,
194}
195
196impl FrameType {
197 #[must_use]
199 pub fn is_reference(&self) -> bool {
200 matches!(self, Self::Key | Self::Inter)
201 }
202}
203
204#[derive(Clone, Copy, Debug, Default)]
206pub struct ColorInfo {
207 pub primaries: ColorPrimaries,
209 pub transfer: TransferCharacteristics,
211 pub matrix: MatrixCoefficients,
213 pub full_range: bool,
215}
216
217#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
219pub enum ColorPrimaries {
220 #[default]
222 Bt709 = 1,
223 Unspecified = 2,
225 Bt470M = 4,
227 Bt470Bg = 5,
229 Smpte170M = 6,
231 Smpte240M = 7,
233 Film = 8,
235 Bt2020 = 9,
237 Smpte428 = 10,
239 Smpte431 = 11,
241 Smpte432 = 12,
243 Ebu3213 = 22,
245}
246
247#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
249pub enum TransferCharacteristics {
250 #[default]
252 Bt709 = 1,
253 Unspecified = 2,
255 Bt470M = 4,
257 Bt470Bg = 5,
259 Smpte170M = 6,
261 Smpte240M = 7,
263 Linear = 8,
265 Log100 = 9,
267 Log316 = 10,
269 Iec619662_4 = 11,
271 Bt1361 = 12,
273 Srgb = 13,
275 Bt202010 = 14,
277 Bt202012 = 15,
279 Smpte2084 = 16,
281 Smpte428 = 17,
283 AribStdB67 = 18,
285}
286
287#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
289pub enum MatrixCoefficients {
290 Identity = 0,
292 #[default]
294 Bt709 = 1,
295 Unspecified = 2,
297 Fcc = 4,
299 Bt470Bg = 5,
301 Smpte170M = 6,
303 Smpte240M = 7,
305 Ycgco = 8,
307 Bt2020Ncl = 9,
309 Bt2020Cl = 10,
311 Smpte2085 = 11,
313 ChromaDerivedNcl = 12,
315 ChromaDerivedCl = 13,
317 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 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}