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