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