media_core/video/
video.rs

1use std::{
2    cmp,
3    fmt::{Display, Formatter},
4    iter, mem,
5    num::NonZeroU32,
6};
7
8use bitflags::bitflags;
9use num_enum::TryFromPrimitive;
10use strum::EnumCount;
11
12use crate::{
13    align_to, ceil_rshift,
14    error::Error,
15    frame::{Frame, PlaneDescriptor, PlaneVec},
16    invalid_param_error,
17    video::VideoFrame,
18    FrameDescriptor, FrameDescriptorSpec, MediaType, Result,
19};
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub struct Dimensions {
23    pub width: NonZeroU32,
24    pub height: NonZeroU32,
25}
26
27impl Dimensions {
28    const fn new_unchecked(width: u32, height: u32) -> Self {
29        Self {
30            width: unsafe { NonZeroU32::new_unchecked(width) },
31            height: unsafe { NonZeroU32::new_unchecked(height) },
32        }
33    }
34
35    pub fn new(width: u32, height: u32) -> Result<Self> {
36        Ok(Self {
37            width: NonZeroU32::new(width).ok_or_else(|| invalid_param_error!(width))?,
38            height: NonZeroU32::new(height).ok_or_else(|| invalid_param_error!(height))?,
39        })
40    }
41
42    pub const SQCIF: Self = Self::new_unchecked(128, 96);
43    pub const QCIF: Self = Self::new_unchecked(176, 144);
44    pub const CIF: Self = Self::new_unchecked(352, 288);
45    pub const QQVGA: Self = Self::new_unchecked(160, 120);
46    pub const QVGA: Self = Self::new_unchecked(320, 240);
47    pub const VGA: Self = Self::new_unchecked(640, 480);
48    pub const SVGA: Self = Self::new_unchecked(800, 600);
49    pub const XGA: Self = Self::new_unchecked(1024, 768);
50    pub const SXGA: Self = Self::new_unchecked(1280, 1024);
51    pub const UXGA: Self = Self::new_unchecked(1600, 1200);
52    pub const QXGA: Self = Self::new_unchecked(2048, 1536);
53    pub const SD: Self = Self::new_unchecked(720, 480);
54    pub const HD: Self = Self::new_unchecked(1280, 720);
55    pub const FHD: Self = Self::new_unchecked(1920, 1080);
56    pub const QHD: Self = Self::new_unchecked(2560, 1440);
57    pub const UHD_4K: Self = Self::new_unchecked(3840, 2160);
58    pub const UHD_8K: Self = Self::new_unchecked(7680, 4320);
59}
60
61#[derive(Clone, Copy, Debug, Default, EnumCount, Eq, PartialEq)]
62#[repr(u8)]
63pub enum ColorRange {
64    #[default]
65    Unspecified,
66    Video,
67    Full,
68}
69
70impl From<ColorRange> for usize {
71    fn from(value: ColorRange) -> Self {
72        value as usize
73    }
74}
75
76impl From<usize> for ColorRange {
77    fn from(value: usize) -> Self {
78        match value {
79            0 => ColorRange::Unspecified,
80            1 => ColorRange::Video,
81            2 => ColorRange::Full,
82            _ => ColorRange::Unspecified,
83        }
84    }
85}
86
87#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
88#[repr(u8)]
89pub enum ColorMatrix {
90    #[default]
91    Identity = 0, // The identity matrix
92    BT709,            // BT.709
93    Unspecified,      // Unspecified
94    Reserved,         // Reserved
95    FCC,              // FCC Title 47 Code of Federal Regulations 73.682(a)(20)
96    BT470BG,          // BT.601 PAL & SECAM
97    SMPTE170M,        // BT.601 NTSC
98    SMPTE240M,        // SMPTE ST 240
99    YCgCo,            // YCgCo
100    BT2020NCL,        // BT.2020 non-constant luminance system
101    BT2020CL,         // BT.2020 constant luminance system
102    SMPTE2085,        // SMPTE ST 2085 Y'D'zD'x
103    ChromaDerivedNCL, // Chromaticity-derived non-constant luminance system
104    ChromaDerivedCL,  // Chromaticity-derived constant luminance system
105    ICtCp,            // BT.2100 ICtCp
106    SMPTE2128,        // SMPTE ST 2128
107}
108
109impl From<ColorMatrix> for usize {
110    fn from(value: ColorMatrix) -> Self {
111        value as usize
112    }
113}
114
115impl TryFrom<usize> for ColorMatrix {
116    type Error = Error;
117
118    fn try_from(value: usize) -> Result<Self> {
119        match value {
120            0 => Ok(ColorMatrix::Identity),
121            1 => Ok(ColorMatrix::BT709),
122            2 => Ok(ColorMatrix::Unspecified),
123            4 => Ok(ColorMatrix::FCC),
124            5 => Ok(ColorMatrix::BT470BG),
125            6 => Ok(ColorMatrix::SMPTE170M),
126            7 => Ok(ColorMatrix::SMPTE240M),
127            8 => Ok(ColorMatrix::YCgCo),
128            9 => Ok(ColorMatrix::BT2020NCL),
129            10 => Ok(ColorMatrix::BT2020CL),
130            11 => Ok(ColorMatrix::SMPTE2085),
131            12 => Ok(ColorMatrix::ChromaDerivedNCL),
132            13 => Ok(ColorMatrix::ChromaDerivedCL),
133            14 => Ok(ColorMatrix::ICtCp),
134            15 => Ok(ColorMatrix::SMPTE2128),
135            _ => Err(invalid_param_error!(value)),
136        }
137    }
138}
139
140#[allow(non_camel_case_types)]
141#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
142#[repr(u8)]
143pub enum ColorPrimaries {
144    #[default]
145    Reserved  = 0, // Reserved
146    BT709,          // BT.709
147    Unspecified,    // Unspecified
148    BT470M    = 4,  // FCC Title 47 Code of Federal Regulations 73.682(a)(20)
149    BT470BG,        // BT.601 PAL & SECAM
150    SMPTE170M,      // BT.601 NTSC
151    SMPTE240M,      // SMPTE ST 240 / SMPTE C
152    Film,           // Generic film (color filters using illuminant C)
153    BT2020,         // BT.2020
154    SMPTE428,       // SMPTE ST 428-1 (CIE 1931 XYZ)
155    SMPTE431,       // SMPTE ST 431-2 (DCI P3)
156    SMPTE432,       // SMPTE ST 432-1 (P3 D65 / Display P3)
157    JEDEC_P22 = 22, // JEDEC P22 phosphors
158}
159
160impl From<ColorPrimaries> for usize {
161    fn from(value: ColorPrimaries) -> Self {
162        value as usize
163    }
164}
165
166impl TryFrom<usize> for ColorPrimaries {
167    type Error = Error;
168
169    fn try_from(value: usize) -> Result<Self> {
170        match value {
171            0 => Ok(ColorPrimaries::Reserved),
172            1 => Ok(ColorPrimaries::BT709),
173            2 => Ok(ColorPrimaries::Unspecified),
174            4 => Ok(ColorPrimaries::BT470M),
175            5 => Ok(ColorPrimaries::BT470BG),
176            6 => Ok(ColorPrimaries::SMPTE170M),
177            7 => Ok(ColorPrimaries::SMPTE240M),
178            8 => Ok(ColorPrimaries::Film),
179            9 => Ok(ColorPrimaries::BT2020),
180            10 => Ok(ColorPrimaries::SMPTE428),
181            11 => Ok(ColorPrimaries::SMPTE431),
182            12 => Ok(ColorPrimaries::SMPTE432),
183            22 => Ok(ColorPrimaries::JEDEC_P22),
184            _ => Err(invalid_param_error!(value)),
185        }
186    }
187}
188
189#[allow(non_camel_case_types)]
190#[repr(u8)]
191#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
192pub enum ColorTransferCharacteristics {
193    #[default]
194    Reserved = 0, // Reserved
195    BT709,        // BT.709
196    Unspecified,  // Unspecified
197    BT470M   = 4, // gamma = 2.2 / FCC Title 47 Code of Federal Regulations 73.682(a)(20)
198    BT470BG,      // gamma = 2.8 / BT.601 PAL & SECAM
199    SMPTE170M,    // BT.601 NTSC
200    SMPTE240M,    // SMPTE ST 240
201    Linear,       // Linear transfer characteristics
202    Log,          // Logarithmic transfer characteristic (100 : 1 range)
203    LogSqrt,      // Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)
204    IEC61966_2_4, // IEC 61966-2-4
205    BT1361E,      // ITU-R BT1361 Extended Colour Gamut
206    IEC61966_2_1, // IEC 61966-2-1 (sRGB or sYCC)
207    BT2020_10,    // BT.2020 10-bit systems
208    BT2020_12,    // BT.2020 12-bit systems
209    SMPTE2084,    // SMPTE ST 2084 / BT.2100 perceptual quantization (PQ) system
210    SMPTE428,     // SMPTE ST 428-1
211    ARIB_STD_B67, // ARIB STD-B67 / BT.2100 hybrid log-gamma (HLG) system
212}
213
214impl From<ColorTransferCharacteristics> for usize {
215    fn from(value: ColorTransferCharacteristics) -> Self {
216        value as usize
217    }
218}
219
220impl TryFrom<usize> for ColorTransferCharacteristics {
221    type Error = Error;
222
223    fn try_from(value: usize) -> Result<Self> {
224        match value {
225            0 => Ok(ColorTransferCharacteristics::Reserved),
226            1 => Ok(ColorTransferCharacteristics::BT709),
227            2 => Ok(ColorTransferCharacteristics::Unspecified),
228            4 => Ok(ColorTransferCharacteristics::BT470M),
229            5 => Ok(ColorTransferCharacteristics::BT470BG),
230            6 => Ok(ColorTransferCharacteristics::SMPTE170M),
231            7 => Ok(ColorTransferCharacteristics::SMPTE240M),
232            8 => Ok(ColorTransferCharacteristics::Linear),
233            9 => Ok(ColorTransferCharacteristics::Log),
234            10 => Ok(ColorTransferCharacteristics::LogSqrt),
235            11 => Ok(ColorTransferCharacteristics::IEC61966_2_4),
236            12 => Ok(ColorTransferCharacteristics::BT1361E),
237            13 => Ok(ColorTransferCharacteristics::IEC61966_2_1),
238            14 => Ok(ColorTransferCharacteristics::BT2020_10),
239            15 => Ok(ColorTransferCharacteristics::BT2020_12),
240            16 => Ok(ColorTransferCharacteristics::SMPTE2084),
241            17 => Ok(ColorTransferCharacteristics::SMPTE428),
242            18 => Ok(ColorTransferCharacteristics::ARIB_STD_B67),
243            _ => Err(invalid_param_error!(value)),
244        }
245    }
246}
247
248#[repr(u8)]
249#[derive(Clone, Copy, Debug, Default, EnumCount, Eq, PartialEq, TryFromPrimitive)]
250pub enum PixelFormat {
251    #[default]
252    ARGB32 = 0, // packed ARGB, 32 bits
253    BGRA32, // packed BGRA, 32 bits
254    ABGR32, // packed ABGR, 32 bits
255    RGBA32, // packed RGBA, 32 bits
256    RGB24,  // packed RGB, 24 bits
257    BGR24,  // packed BGR, 24 bits
258    I420,   // planar YUV 4:2:0, 12 bits
259    I422,   // planar YUV 4:2:2, 16 bits
260    I444,   // planar YUV 4:4:4, 24 bits
261    I440,   // planar YUV 4:4:0, 16 bits
262    NV12,   // biplanar YUV 4:2:0, 12 bits
263    NV21,   // biplanar YUV 4:2:0, 12 bits
264    NV16,   // biplanar YUV 4:2:2, 16 bits
265    NV61,   // biplanar YUV 4:2:2, 16 bits
266    NV24,   // biplanar YUV 4:4:4, 24 bits
267    NV42,   // biplanar YUV 4:4:4, 24 bits
268    YV12,   // planar YVU 4:2:0, 12 bits
269    YV16,   // planar YVU 4:2:2, 16 bits
270    YV24,   // planar YVU 4:4:4, 24 bits
271    YUYV,   // packed YUV 4:2:2, 16 bits, Y0 Cb Y1 Cr
272    YVYU,   // packed YUV 4:2:2, 16 bits, Y0 Cr Y1 Cb
273    UYVY,   // packed YUV 4:2:2, 16 bits, Cb Y0 Cr Y1
274    VYUY,   // packed YUV 4:2:2, 16 bits, Cr Y0 Cb Y1
275    AYUV,   // packed AYUV 4:4:4, 32 bits
276    Y8,     // greyscale, 8 bits Y
277    YA8,    // greyscale, 8 bits Y, 8 bits alpha
278    RGB30,  // packed RGB, 30 bits, 10 bits per channel, 2 bits unused(LSB)
279    BGR30,  // packed BGR, 30 bits, 10 bits per channel, 2 bits unused(LSB)
280    ARGB64, // packed ARGB, 64 bits, 16 bits per channel, 16-bit big-endian
281    BGRA64, // packed BGRA, 64 bits, 16 bits per channel, 16-bit big-endian
282    ABGR64, // packed ABGR, 64 bits, 16 bits per channel, 16-bit big-endian
283    RGBA64, // packed RGBA, 64 bits, 16 bits per channel, 16-bit big-endian
284    I010,   // planar YUV 4:2:0, 10 bits per channel
285    I210,   // planar YUV 4:2:2, 10 bits per channel
286    I410,   // planar YUV 4:4:4, 10 bits per channel
287    I44010, // planar YUV 4:4:0, 10 bits per channel
288    P010,   // biplanar YUV 4:2:0, 10 bits per channel
289    P210,   // biplanar YUV 4:2:2, 10 bits per channel
290    P410,   // biplanar YUV 4:4:4, 10 bits per channel
291    I012,   // planar YUV 4:2:2, 12 bits per channel
292    I212,   // planar YUV 4:2:2, 12 bits per channel
293    I412,   // planar YUV 4:4:4, 12 bits per channel
294    I44012, // planar YUV 4:4:0, 12 bits per channel
295    P012,   // biplanar YUV 4:2:0, 12 bits per channel
296    P212,   // biplanar YUV 4:2:2, 12 bits per channel
297    P412,   // biplanar YUV 4:4:4, 12 bits per channel
298    I016,   // planar YUV 4:2:0, 16 bits per channel
299    I216,   // planar YUV 4:2:2, 16 bits per channel
300    I416,   // planar YUV 4:4:4, 16 bits per channel
301    I44016, // planar YUV 4:4:0, 16 bits per channel
302    P016,   // biplanar YUV 4:2:0, 16 bits per channel
303    P216,   // biplanar YUV 4:2:2, 16 bits per channel
304    P416,   // biplanar YUV 4:4:4, 16 bits per channel
305}
306
307impl From<PixelFormat> for usize {
308    fn from(value: PixelFormat) -> Self {
309        value as usize
310    }
311}
312
313impl TryFrom<usize> for PixelFormat {
314    type Error = Error;
315
316    fn try_from(value: usize) -> Result<Self> {
317        if value < PixelFormat::COUNT {
318            Ok(unsafe { mem::transmute::<u8, PixelFormat>(value as u8) })
319        } else {
320            Err(invalid_param_error!(value))
321        }
322    }
323}
324
325bitflags! {
326    #[repr(transparent)]
327    struct PixelFormatFlags: u32 {
328        const Alpha    = 1 << 0;
329        const RGB      = 1 << 1;
330        const YUV      = 1 << 2;
331        const Planar   = 1 << 3;
332        const Packed   = 1 << 4;
333        const BiPlanar = 1 << 5;
334    }
335}
336
337struct PixelFormatDescriptor {
338    components: u8,
339    chroma_shift_x: u8,
340    chroma_shift_y: u8,
341    depth: u8,
342    flags: PixelFormatFlags,
343    component_bytes: [u8; 4],
344}
345
346macro_rules! pix_fmt_flags {
347    ($($flag:ident)|+) => {
348        PixelFormatFlags::from_bits_truncate(0 $(| PixelFormatFlags::$flag.bits())+)
349    };
350    ($flag:ident) => {
351        PixelFormatFlags::from_bits_truncate(PixelFormatFlags::$flag.bits())
352    };
353}
354
355static PIXEL_FORMAT_DESC: [PixelFormatDescriptor; PixelFormat::COUNT] = [
356    // ARGB32
357    PixelFormatDescriptor {
358        components: 1,
359        chroma_shift_x: 0,
360        chroma_shift_y: 0,
361        depth: 8,
362        flags: pix_fmt_flags!(Alpha | RGB | Packed),
363        component_bytes: [4, 0, 0, 0],
364    },
365    // BGRA32
366    PixelFormatDescriptor {
367        components: 1,
368        chroma_shift_x: 0,
369        chroma_shift_y: 0,
370        depth: 8,
371        flags: pix_fmt_flags!(Alpha | RGB | Packed),
372        component_bytes: [4, 0, 0, 0],
373    },
374    // ABGR32
375    PixelFormatDescriptor {
376        components: 1,
377        chroma_shift_x: 0,
378        chroma_shift_y: 0,
379        depth: 8,
380        flags: pix_fmt_flags!(Alpha | RGB | Packed),
381        component_bytes: [4, 0, 0, 0],
382    },
383    // RGBA32
384    PixelFormatDescriptor {
385        components: 1,
386        chroma_shift_x: 0,
387        chroma_shift_y: 0,
388        depth: 8,
389        flags: pix_fmt_flags!(Alpha | RGB | Packed),
390        component_bytes: [4, 0, 0, 0],
391    },
392    // RGB24
393    PixelFormatDescriptor {
394        components: 1,
395        chroma_shift_x: 0,
396        chroma_shift_y: 0,
397        depth: 8,
398        flags: pix_fmt_flags!(RGB | Packed),
399        component_bytes: [3, 0, 0, 0],
400    },
401    // BGR24
402    PixelFormatDescriptor {
403        components: 1,
404        chroma_shift_x: 0,
405        chroma_shift_y: 0,
406        depth: 8,
407        flags: pix_fmt_flags!(RGB | Packed),
408        component_bytes: [3, 0, 0, 0],
409    },
410    // I420
411    PixelFormatDescriptor {
412        components: 3,
413        chroma_shift_x: 1,
414        chroma_shift_y: 1,
415        depth: 8,
416        flags: pix_fmt_flags!(YUV | Planar),
417        component_bytes: [1, 1, 1, 0],
418    },
419    // I422
420    PixelFormatDescriptor {
421        components: 3,
422        chroma_shift_x: 1,
423        chroma_shift_y: 0,
424        depth: 8,
425        flags: pix_fmt_flags!(YUV | Planar),
426        component_bytes: [1, 1, 1, 0],
427    },
428    // I444
429    PixelFormatDescriptor {
430        components: 3,
431        chroma_shift_x: 0,
432        chroma_shift_y: 0,
433        depth: 8,
434        flags: pix_fmt_flags!(YUV | Planar),
435        component_bytes: [1, 1, 1, 0],
436    },
437    // I440
438    PixelFormatDescriptor {
439        components: 3,
440        chroma_shift_x: 0,
441        chroma_shift_y: 1,
442        depth: 8,
443        flags: pix_fmt_flags!(YUV | Planar),
444        component_bytes: [1, 1, 1, 0],
445    },
446    // NV12
447    PixelFormatDescriptor {
448        components: 2,
449        chroma_shift_x: 1,
450        chroma_shift_y: 1,
451        depth: 8,
452        flags: pix_fmt_flags!(YUV | BiPlanar),
453        component_bytes: [1, 2, 0, 0],
454    },
455    // NV21
456    PixelFormatDescriptor {
457        components: 2,
458        chroma_shift_x: 1,
459        chroma_shift_y: 1,
460        depth: 8,
461        flags: pix_fmt_flags!(YUV | BiPlanar),
462        component_bytes: [1, 2, 0, 0],
463    },
464    // NV16
465    PixelFormatDescriptor {
466        components: 2,
467        chroma_shift_x: 1,
468        chroma_shift_y: 0,
469        depth: 8,
470        flags: pix_fmt_flags!(YUV | BiPlanar),
471        component_bytes: [1, 2, 0, 0],
472    },
473    // NV61
474    PixelFormatDescriptor {
475        components: 2,
476        chroma_shift_x: 1,
477        chroma_shift_y: 0,
478        depth: 8,
479        flags: pix_fmt_flags!(YUV | BiPlanar),
480        component_bytes: [1, 2, 0, 0],
481    },
482    // NV24
483    PixelFormatDescriptor {
484        components: 2,
485        chroma_shift_x: 0,
486        chroma_shift_y: 0,
487        depth: 8,
488        flags: pix_fmt_flags!(YUV | BiPlanar),
489        component_bytes: [1, 2, 0, 0],
490    },
491    // NV42
492    PixelFormatDescriptor {
493        components: 2,
494        chroma_shift_x: 0,
495        chroma_shift_y: 0,
496        depth: 8,
497        flags: pix_fmt_flags!(YUV | BiPlanar),
498        component_bytes: [1, 2, 0, 0],
499    },
500    // YV12
501    PixelFormatDescriptor {
502        components: 3,
503        chroma_shift_x: 1,
504        chroma_shift_y: 1,
505        depth: 8,
506        flags: pix_fmt_flags!(YUV | Planar),
507        component_bytes: [1, 1, 1, 0],
508    },
509    // YV16
510    PixelFormatDescriptor {
511        components: 3,
512        chroma_shift_x: 1,
513        chroma_shift_y: 0,
514        depth: 8,
515        flags: pix_fmt_flags!(YUV | Planar),
516        component_bytes: [1, 1, 1, 0],
517    },
518    // YV24
519    PixelFormatDescriptor {
520        components: 3,
521        chroma_shift_x: 0,
522        chroma_shift_y: 0,
523        depth: 8,
524        flags: pix_fmt_flags!(YUV | Planar),
525        component_bytes: [1, 1, 1, 0],
526    },
527    // YUYV
528    PixelFormatDescriptor {
529        components: 1,
530        chroma_shift_x: 1,
531        chroma_shift_y: 0,
532        depth: 8,
533        flags: pix_fmt_flags!(YUV | Packed),
534        component_bytes: [4, 0, 0, 0],
535    },
536    // YVYU
537    PixelFormatDescriptor {
538        components: 1,
539        chroma_shift_x: 1,
540        chroma_shift_y: 0,
541        depth: 8,
542        flags: pix_fmt_flags!(YUV | Packed),
543        component_bytes: [4, 0, 0, 0],
544    },
545    // UYVY
546    PixelFormatDescriptor {
547        components: 1,
548        chroma_shift_x: 1,
549        chroma_shift_y: 0,
550        depth: 8,
551        flags: pix_fmt_flags!(YUV | Packed),
552        component_bytes: [4, 0, 0, 0],
553    },
554    // VYUY
555    PixelFormatDescriptor {
556        components: 1,
557        chroma_shift_x: 1,
558        chroma_shift_y: 0,
559        depth: 8,
560        flags: pix_fmt_flags!(YUV | Packed),
561        component_bytes: [4, 0, 0, 0],
562    },
563    // AYUV
564    PixelFormatDescriptor {
565        components: 1,
566        chroma_shift_x: 0,
567        chroma_shift_y: 0,
568        depth: 8,
569        flags: pix_fmt_flags!(Alpha | YUV | Packed),
570        component_bytes: [4, 0, 0, 0],
571    },
572    // Y8
573    PixelFormatDescriptor {
574        components: 1,
575        chroma_shift_x: 0,
576        chroma_shift_y: 0,
577        depth: 8,
578        flags: PixelFormatFlags::Planar,
579        component_bytes: [1, 0, 0, 0],
580    },
581    // YA8
582    PixelFormatDescriptor {
583        components: 2,
584        chroma_shift_x: 0,
585        chroma_shift_y: 0,
586        depth: 8,
587        flags: pix_fmt_flags!(Alpha | Planar),
588        component_bytes: [1, 1, 0, 0],
589    },
590    // RGB30
591    PixelFormatDescriptor {
592        components: 1,
593        chroma_shift_x: 0,
594        chroma_shift_y: 0,
595        depth: 10,
596        flags: pix_fmt_flags!(RGB | Packed),
597        component_bytes: [4, 0, 0, 0],
598    },
599    // BGR30
600    PixelFormatDescriptor {
601        components: 1,
602        chroma_shift_x: 0,
603        chroma_shift_y: 0,
604        depth: 10,
605        flags: pix_fmt_flags!(RGB | Packed),
606        component_bytes: [4, 0, 0, 0],
607    },
608    // ARGB64
609    PixelFormatDescriptor {
610        components: 1,
611        chroma_shift_x: 0,
612        chroma_shift_y: 0,
613        depth: 16,
614        flags: pix_fmt_flags!(Alpha | RGB | Packed),
615        component_bytes: [8, 0, 0, 0],
616    },
617    // BGRA64
618    PixelFormatDescriptor {
619        components: 1,
620        chroma_shift_x: 0,
621        chroma_shift_y: 0,
622        depth: 16,
623        flags: pix_fmt_flags!(Alpha | RGB | Packed),
624        component_bytes: [8, 0, 0, 0],
625    },
626    // ABGR64
627    PixelFormatDescriptor {
628        components: 1,
629        chroma_shift_x: 0,
630        chroma_shift_y: 0,
631        depth: 16,
632        flags: pix_fmt_flags!(Alpha | RGB | Packed),
633        component_bytes: [8, 0, 0, 0],
634    },
635    // RGBA64
636    PixelFormatDescriptor {
637        components: 1,
638        chroma_shift_x: 0,
639        chroma_shift_y: 0,
640        depth: 16,
641        flags: pix_fmt_flags!(Alpha | RGB | Packed),
642        component_bytes: [8, 0, 0, 0],
643    },
644    // I010
645    PixelFormatDescriptor {
646        components: 3,
647        chroma_shift_x: 1,
648        chroma_shift_y: 1,
649        depth: 10,
650        flags: pix_fmt_flags!(YUV | Planar),
651        component_bytes: [2, 2, 2, 0],
652    },
653    // I210
654    PixelFormatDescriptor {
655        components: 3,
656        chroma_shift_x: 1,
657        chroma_shift_y: 0,
658        depth: 10,
659        flags: pix_fmt_flags!(YUV | Planar),
660        component_bytes: [2, 2, 2, 0],
661    },
662    // I410
663    PixelFormatDescriptor {
664        components: 3,
665        chroma_shift_x: 0,
666        chroma_shift_y: 0,
667        depth: 10,
668        flags: pix_fmt_flags!(YUV | Planar),
669        component_bytes: [2, 2, 2, 0],
670    },
671    // I44010
672    PixelFormatDescriptor {
673        components: 3,
674        chroma_shift_x: 0,
675        chroma_shift_y: 1,
676        depth: 10,
677        flags: pix_fmt_flags!(YUV | Planar),
678        component_bytes: [2, 2, 2, 0],
679    },
680    // P010
681    PixelFormatDescriptor {
682        components: 2,
683        chroma_shift_x: 1,
684        chroma_shift_y: 1,
685        depth: 10,
686        flags: pix_fmt_flags!(YUV | BiPlanar),
687        component_bytes: [2, 4, 0, 0],
688    },
689    // P210
690    PixelFormatDescriptor {
691        components: 2,
692        chroma_shift_x: 1,
693        chroma_shift_y: 0,
694        depth: 10,
695        flags: pix_fmt_flags!(YUV | BiPlanar),
696        component_bytes: [2, 4, 0, 0],
697    },
698    // P410
699    PixelFormatDescriptor {
700        components: 2,
701        chroma_shift_x: 0,
702        chroma_shift_y: 0,
703        depth: 10,
704        flags: pix_fmt_flags!(YUV | BiPlanar),
705        component_bytes: [2, 4, 0, 0],
706    },
707    // I012
708    PixelFormatDescriptor {
709        components: 3,
710        chroma_shift_x: 1,
711        chroma_shift_y: 1,
712        depth: 12,
713        flags: pix_fmt_flags!(YUV | Planar),
714        component_bytes: [2, 2, 2, 0],
715    },
716    // I212
717    PixelFormatDescriptor {
718        components: 3,
719        chroma_shift_x: 1,
720        chroma_shift_y: 0,
721        depth: 12,
722        flags: pix_fmt_flags!(YUV | Planar),
723        component_bytes: [2, 2, 2, 0],
724    },
725    // I412
726    PixelFormatDescriptor {
727        components: 3,
728        chroma_shift_x: 0,
729        chroma_shift_y: 0,
730        depth: 12,
731        flags: pix_fmt_flags!(YUV | Planar),
732        component_bytes: [2, 2, 2, 0],
733    },
734    // I44012
735    PixelFormatDescriptor {
736        components: 3,
737        chroma_shift_x: 0,
738        chroma_shift_y: 1,
739        depth: 12,
740        flags: pix_fmt_flags!(YUV | Planar),
741        component_bytes: [2, 2, 2, 0],
742    },
743    // P012
744    PixelFormatDescriptor {
745        components: 2,
746        chroma_shift_x: 1,
747        chroma_shift_y: 1,
748        depth: 12,
749        flags: pix_fmt_flags!(YUV | BiPlanar),
750        component_bytes: [2, 4, 0, 0],
751    },
752    // P212
753    PixelFormatDescriptor {
754        components: 2,
755        chroma_shift_x: 1,
756        chroma_shift_y: 0,
757        depth: 12,
758        flags: pix_fmt_flags!(YUV | BiPlanar),
759        component_bytes: [2, 4, 0, 0],
760    },
761    // P412
762    PixelFormatDescriptor {
763        components: 2,
764        chroma_shift_x: 0,
765        chroma_shift_y: 0,
766        depth: 12,
767        flags: pix_fmt_flags!(YUV | BiPlanar),
768        component_bytes: [2, 4, 0, 0],
769    },
770    // I016
771    PixelFormatDescriptor {
772        components: 3,
773        chroma_shift_x: 1,
774        chroma_shift_y: 1,
775        depth: 16,
776        flags: pix_fmt_flags!(YUV | Planar),
777        component_bytes: [2, 2, 2, 0],
778    },
779    // I216
780    PixelFormatDescriptor {
781        components: 3,
782        chroma_shift_x: 1,
783        chroma_shift_y: 0,
784        depth: 16,
785        flags: pix_fmt_flags!(YUV | Planar),
786        component_bytes: [2, 2, 2, 0],
787    },
788    // I416
789    PixelFormatDescriptor {
790        components: 3,
791        chroma_shift_x: 0,
792        chroma_shift_y: 0,
793        depth: 16,
794        flags: pix_fmt_flags!(YUV | Planar),
795        component_bytes: [2, 2, 2, 0],
796    },
797    // I44016
798    PixelFormatDescriptor {
799        components: 3,
800        chroma_shift_x: 0,
801        chroma_shift_y: 1,
802        depth: 16,
803        flags: pix_fmt_flags!(YUV | Planar),
804        component_bytes: [2, 2, 2, 0],
805    },
806    // P016
807    PixelFormatDescriptor {
808        components: 2,
809        chroma_shift_x: 1,
810        chroma_shift_y: 1,
811        depth: 16,
812        flags: pix_fmt_flags!(YUV | BiPlanar),
813        component_bytes: [2, 4, 0, 0],
814    },
815    // P216
816    PixelFormatDescriptor {
817        components: 2,
818        chroma_shift_x: 1,
819        chroma_shift_y: 0,
820        depth: 16,
821        flags: pix_fmt_flags!(YUV | BiPlanar),
822        component_bytes: [2, 4, 0, 0],
823    },
824    // P416
825    PixelFormatDescriptor {
826        components: 2,
827        chroma_shift_x: 0,
828        chroma_shift_y: 0,
829        depth: 16,
830        flags: pix_fmt_flags!(YUV | BiPlanar),
831        component_bytes: [2, 4, 0, 0],
832    },
833];
834
835impl PixelFormat {
836    pub fn components(&self) -> u8 {
837        PIXEL_FORMAT_DESC[*self as usize].components
838    }
839
840    pub fn component_bytes(&self, component: u8) -> u8 {
841        PIXEL_FORMAT_DESC[*self as usize].component_bytes[component as usize]
842    }
843
844    pub fn chroma_subsampling(&self) -> Option<ChromaSubsampling> {
845        if !self.is_yuv() {
846            return None;
847        }
848
849        let desc = &PIXEL_FORMAT_DESC[*self as usize];
850
851        match (desc.chroma_shift_x, desc.chroma_shift_y) {
852            (1, 1) => Some(ChromaSubsampling::YUV420),
853            (1, 0) => Some(ChromaSubsampling::YUV422),
854            (0, 0) => Some(ChromaSubsampling::YUV444),
855            _ => None,
856        }
857    }
858
859    pub fn depth(&self) -> u8 {
860        PIXEL_FORMAT_DESC[*self as usize].depth
861    }
862
863    pub fn is_rgb(&self) -> bool {
864        PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::RGB)
865    }
866
867    pub fn is_yuv(&self) -> bool {
868        PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::YUV)
869    }
870
871    pub fn is_planar(&self) -> bool {
872        PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::Planar)
873    }
874
875    pub fn is_packed(&self) -> bool {
876        PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::Packed)
877    }
878
879    pub fn is_biplanar(&self) -> bool {
880        PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::BiPlanar)
881    }
882
883    pub fn calc_plane_row_bytes(&self, plane_index: usize, width: u32) -> u32 {
884        let desc = &PIXEL_FORMAT_DESC[*self as usize];
885        let component_bytes = desc.component_bytes[plane_index];
886
887        if plane_index > 0 && (self.is_planar() || self.is_biplanar()) {
888            ceil_rshift(width, desc.chroma_shift_x as u32) * component_bytes as u32
889        } else {
890            width * component_bytes as u32
891        }
892    }
893
894    pub fn calc_plane_height(&self, plane_index: usize, height: u32) -> u32 {
895        if plane_index > 0 && (self.is_planar() || self.is_biplanar()) {
896            let desc = &PIXEL_FORMAT_DESC[*self as usize];
897            ceil_rshift(height, desc.chroma_shift_y as u32)
898        } else {
899            height
900        }
901    }
902
903    pub(crate) fn calc_data_size(&self, width: u32, height: u32, alignment: u32) -> (usize, PlaneVec<PlaneDescriptor>) {
904        let desc = &PIXEL_FORMAT_DESC[*self as usize];
905        let mut size;
906        let mut planes = PlaneVec::with_capacity(desc.components as usize);
907
908        match self {
909            PixelFormat::RGB24 | PixelFormat::BGR24 | PixelFormat::Y8 => {
910                let stride = align_to(width * desc.component_bytes[0] as u32, cmp::max(alignment, 4)) as usize;
911                planes.push(PlaneDescriptor::Video(stride, height));
912                size = stride * height as usize;
913            }
914            PixelFormat::YA8 => {
915                let stride = align_to(width * desc.component_bytes[0] as u32, cmp::max(alignment, 4)) as usize;
916                planes.extend(iter::repeat_n(PlaneDescriptor::Video(stride, height), 2));
917                size = stride * height as usize * 2;
918            }
919            PixelFormat::YUYV | PixelFormat::YVYU | PixelFormat::UYVY | PixelFormat::VYUY | PixelFormat::AYUV => {
920                let stride = align_to(ceil_rshift(width, desc.chroma_shift_x as u32) * 4, alignment) as usize;
921                planes.push(PlaneDescriptor::Video(stride, height));
922                size = stride * height as usize;
923            }
924            _ => {
925                let stride = align_to(width * desc.component_bytes[0] as u32, alignment) as usize;
926                planes.push(PlaneDescriptor::Video(stride, height));
927                size = stride * height as usize;
928                for i in 1..desc.components as usize {
929                    let stride = align_to(ceil_rshift(width, desc.chroma_shift_x as u32) * desc.component_bytes[i] as u32, alignment) as usize;
930                    let height = ceil_rshift(height, desc.chroma_shift_y as u32);
931                    planes.push(PlaneDescriptor::Video(stride, height));
932                    size += stride * height as usize;
933                }
934            }
935        }
936
937        (size, planes)
938    }
939
940    pub(crate) fn calc_data_size_with_stride(&self, height: u32, stride: u32) -> (usize, PlaneVec<PlaneDescriptor>) {
941        let desc = &PIXEL_FORMAT_DESC[*self as usize];
942        let mut size;
943        let mut planes = PlaneVec::with_capacity(desc.components as usize);
944
945        planes.push(PlaneDescriptor::Video(stride as usize, height));
946        size = stride * height;
947        for i in 1..desc.components as usize {
948            let plane_stride = ceil_rshift(stride, desc.chroma_shift_x as u32) * desc.component_bytes[i] as u32;
949            let plane_height = ceil_rshift(height, desc.chroma_shift_y as u32);
950            planes.push(PlaneDescriptor::Video(plane_stride as usize, plane_height));
951            size += plane_stride * plane_height;
952        }
953
954        (size as usize, planes)
955    }
956
957    pub(crate) fn calc_chroma_dimensions(&self, width: u32, height: u32) -> (u32, u32) {
958        let desc = &PIXEL_FORMAT_DESC[*self as usize];
959        let chroma_width = ceil_rshift(width, desc.chroma_shift_x as u32);
960        let chroma_height = ceil_rshift(height, desc.chroma_shift_y as u32);
961        (chroma_width, chroma_height)
962    }
963}
964
965#[repr(u8)]
966#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, TryFromPrimitive)]
967pub enum CompressionFormat {
968    #[default]
969    MJPEG,
970}
971
972#[derive(Clone, Copy, Debug, Eq, PartialEq)]
973pub enum VideoFormat {
974    Pixel(PixelFormat),
975    Compression(CompressionFormat),
976}
977
978impl Display for VideoFormat {
979    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
980        match self {
981            VideoFormat::Pixel(format) => write!(f, "{:?}", format),
982            VideoFormat::Compression(format) => write!(f, "{:?}", format),
983        }
984    }
985}
986
987impl VideoFormat {
988    pub fn is_compressed(&self) -> bool {
989        matches!(self, VideoFormat::Compression(_))
990    }
991
992    pub fn is_yuv(&self) -> bool {
993        match self {
994            VideoFormat::Pixel(format) => format.is_yuv(),
995            VideoFormat::Compression(CompressionFormat::MJPEG) => true,
996        }
997    }
998}
999
1000const COMPRESSION_MASK: u32 = 0x8000;
1001
1002impl From<VideoFormat> for u32 {
1003    fn from(value: VideoFormat) -> Self {
1004        match value {
1005            VideoFormat::Pixel(format) => format as u32,
1006            VideoFormat::Compression(format) => format as u32 | COMPRESSION_MASK,
1007        }
1008    }
1009}
1010
1011impl TryFrom<u32> for VideoFormat {
1012    type Error = Error;
1013
1014    fn try_from(value: u32) -> Result<Self> {
1015        if value & COMPRESSION_MASK != 0 {
1016            let format_value = value & !COMPRESSION_MASK;
1017            CompressionFormat::try_from(format_value as u8).map(VideoFormat::Compression).map_err(|e| Error::Invalid(e.to_string()))
1018        } else {
1019            PixelFormat::try_from(value as u8).map(VideoFormat::Pixel).map_err(|e| Error::Invalid(e.to_string()))
1020        }
1021    }
1022}
1023
1024#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1025pub enum ChromaLocation {
1026    #[default]
1027    Unspecified,
1028    Left,
1029    Center,
1030    TopLeft,
1031    Top,
1032    BottomLeft,
1033    Bottom,
1034}
1035
1036impl From<ChromaLocation> for usize {
1037    fn from(value: ChromaLocation) -> Self {
1038        value as usize
1039    }
1040}
1041
1042impl From<usize> for ChromaLocation {
1043    fn from(value: usize) -> Self {
1044        match value {
1045            0 => ChromaLocation::Unspecified,
1046            1 => ChromaLocation::Left,
1047            2 => ChromaLocation::Center,
1048            3 => ChromaLocation::TopLeft,
1049            4 => ChromaLocation::Top,
1050            5 => ChromaLocation::BottomLeft,
1051            6 => ChromaLocation::Bottom,
1052            _ => ChromaLocation::Unspecified,
1053        }
1054    }
1055}
1056
1057#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1058pub enum ChromaSubsampling {
1059    YUV420,
1060    YUV422,
1061    YUV444,
1062}
1063
1064#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1065pub enum Rotation {
1066    #[default]
1067    None,
1068    Rotation90,
1069    Rotation180,
1070    Rotation270,
1071}
1072
1073#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1074pub enum Origin {
1075    #[default]
1076    TopDown,
1077    BottomUp,
1078}
1079
1080#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1081pub enum ScaleFilter {
1082    Nearest,
1083    #[default]
1084    Bilinear,
1085    Bicubic,
1086}
1087
1088#[derive(Clone, Debug, Eq, PartialEq)]
1089pub struct VideoFrameDescriptor {
1090    pub format: PixelFormat,
1091    pub dimensions: Dimensions,
1092    pub color_range: ColorRange,
1093    pub color_matrix: ColorMatrix,
1094    pub color_primaries: ColorPrimaries,
1095    pub color_transfer_characteristics: ColorTransferCharacteristics,
1096    pub chroma_location: ChromaLocation,
1097    pub rotation: Rotation,
1098    pub origin: Origin,
1099    pub transparent: bool,
1100    pub extra_alpha: bool,
1101    pub crop_left: u32,
1102    pub crop_top: u32,
1103    pub crop_right: u32,
1104    pub crop_bottom: u32,
1105}
1106
1107impl VideoFrameDescriptor {
1108    pub fn new(format: PixelFormat, width: NonZeroU32, height: NonZeroU32) -> Self {
1109        Self {
1110            format,
1111            dimensions: Dimensions {
1112                width,
1113                height,
1114            },
1115            color_range: ColorRange::default(),
1116            color_matrix: ColorMatrix::default(),
1117            color_primaries: ColorPrimaries::default(),
1118            color_transfer_characteristics: ColorTransferCharacteristics::default(),
1119            chroma_location: ChromaLocation::default(),
1120            rotation: Rotation::default(),
1121            origin: Origin::default(),
1122            transparent: false,
1123            extra_alpha: false,
1124            crop_left: 0,
1125            crop_top: 0,
1126            crop_right: 0,
1127            crop_bottom: 0,
1128        }
1129    }
1130
1131    pub fn try_new(format: PixelFormat, width: u32, height: u32) -> Result<Self> {
1132        let width = NonZeroU32::new(width).ok_or_else(|| invalid_param_error!(width))?;
1133        let height = NonZeroU32::new(height).ok_or_else(|| invalid_param_error!(height))?;
1134
1135        Ok(Self::new(format, width, height))
1136    }
1137
1138    #[inline]
1139    pub fn width(&self) -> NonZeroU32 {
1140        self.dimensions.width
1141    }
1142
1143    #[inline]
1144    pub fn height(&self) -> NonZeroU32 {
1145        self.dimensions.height
1146    }
1147}
1148
1149impl From<VideoFrameDescriptor> for FrameDescriptor {
1150    fn from(desc: VideoFrameDescriptor) -> Self {
1151        FrameDescriptor::Video(desc)
1152    }
1153}
1154
1155impl TryFrom<FrameDescriptor> for VideoFrameDescriptor {
1156    type Error = Error;
1157
1158    fn try_from(value: FrameDescriptor) -> Result<Self> {
1159        match value {
1160            FrameDescriptor::Video(desc) => Ok(desc),
1161            _ => Err(invalid_param_error!(value)),
1162        }
1163    }
1164}
1165
1166impl FrameDescriptorSpec for VideoFrameDescriptor {
1167    fn media_type(&self) -> MediaType {
1168        MediaType::Video
1169    }
1170
1171    fn create_frame(&self) -> Result<Frame<'static, Self>> {
1172        VideoFrame::new_with_descriptor(self.clone())
1173    }
1174
1175    fn as_video(&self) -> Option<&VideoFrameDescriptor> {
1176        Some(self)
1177    }
1178}