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, BT709, Unspecified, Reserved, FCC, BT470BG, SMPTE170M, SMPTE240M, YCgCo, BT2020NCL, BT2020CL, SMPTE2085, ChromaDerivedNCL, ChromaDerivedCL, ICtCp, SMPTE2128, }
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, BT709, Unspecified, BT470M = 4, BT470BG, SMPTE170M, SMPTE240M, Film, BT2020, SMPTE428, SMPTE431, SMPTE432, JEDEC_P22 = 22, }
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, 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, }
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, 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, 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}