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, BT709, Unspecified, Reserved, FCC, BT470BG, SMPTE170M, SMPTE240M, YCgCo, BT2020NCL, BT2020CL, SMPTE2085, ChromaDerivedNCL, ChromaDerivedCL, ICtCp, SMPTE2128, }
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, BT709, Unspecified, BT470M = 4, BT470BG, SMPTE170M, SMPTE240M, Film, BT2020, SMPTE428, SMPTE431, SMPTE432, JEDEC_P22 = 22, }
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, BT709, Unspecified, BT470M = 4, BT470BG, SMPTE170M, SMPTE240M, Linear, Log, LogSqrt, IEC61966_2_4, BT1361E, IEC61966_2_1, BT2020_10, BT2020_12, SMPTE2084, SMPTE428, ARIB_STD_B67, }
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, BGRA32, ABGR32, RGBA32, RGB24, BGR24, I420, I422, I444, I440, NV12, NV21, NV16, NV61, NV24, NV42, YV12, YV16, YV24, YUYV, YVYU, UYVY, VYUY, AYUV, Y8, YA8, RGB30, BGR30, ARGB64, BGRA64, ABGR64, RGBA64, I010, I210, I410, I44010, P010, P210, P410, I012, I212, I412, I44012, P012, P212, P412, I016, I216, I416, I44016, P016, P216, P416, }
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}