1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
57#[error("width ({width}) or height ({height}) is zero")]
58pub struct ZeroDimension {
59 width: u32,
60 height: u32,
61}
62
63impl ZeroDimension {
64 #[inline]
66 pub const fn new(width: u32, height: u32) -> Self {
67 Self { width, height }
68 }
69 #[inline]
71 pub const fn width(&self) -> u32 {
72 self.width
73 }
74 #[inline]
76 pub const fn height(&self) -> u32 {
77 self.height
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
83#[error("dimensions {width} × {height} overflow")]
84pub struct DimensionOverflow {
85 width: u32,
86 height: u32,
87}
88
89impl DimensionOverflow {
90 #[inline]
92 pub const fn new(width: u32, height: u32) -> Self {
93 Self { width, height }
94 }
95 #[inline]
97 pub const fn width(&self) -> u32 {
98 self.width
99 }
100 #[inline]
102 pub const fn height(&self) -> u32 {
103 self.height
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
111#[error("stride ({stride}) is smaller than minimum ({min})")]
112pub struct InsufficientStride {
113 stride: u32,
114 min: u32,
115}
116
117impl InsufficientStride {
118 #[inline]
120 pub const fn new(stride: u32, min: u32) -> Self {
121 Self { stride, min }
122 }
123 #[inline]
125 pub const fn stride(&self) -> u32 {
126 self.stride
127 }
128 #[inline]
130 pub const fn min(&self) -> u32 {
131 self.min
132 }
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
137#[error("plane has {actual} bytes/samples but at least {expected} are required")]
138pub struct InsufficientPlane {
139 expected: usize,
140 actual: usize,
141}
142
143impl InsufficientPlane {
144 #[inline]
146 pub const fn new(expected: usize, actual: usize) -> Self {
147 Self { expected, actual }
148 }
149 #[inline]
151 pub const fn expected(&self) -> usize {
152 self.expected
153 }
154 #[inline]
156 pub const fn actual(&self) -> usize {
157 self.actual
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
163#[error("declared geometry overflows usize: stride={stride} * rows={rows}")]
164pub struct GeometryOverflow {
165 stride: u32,
166 rows: u32,
167}
168
169impl GeometryOverflow {
170 #[inline]
172 pub const fn new(stride: u32, rows: u32) -> Self {
173 Self { stride, rows }
174 }
175 #[inline]
177 pub const fn stride(&self) -> u32 {
178 self.stride
179 }
180 #[inline]
182 pub const fn rows(&self) -> u32 {
183 self.rows
184 }
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
189#[error("width ({width}) {required}")]
190pub struct WidthAlignment {
191 width: usize,
193 required: WidthAlignmentRequirement,
195}
196
197impl WidthAlignment {
198 #[inline]
200 const fn new(width: usize, required: WidthAlignmentRequirement) -> Self {
201 Self { width, required }
202 }
203
204 #[inline]
206 pub const fn odd(width: usize) -> Self {
207 Self::new(width, WidthAlignmentRequirement::Even)
208 }
209
210 #[inline]
212 pub const fn multiple_of_four(width: usize) -> Self {
213 Self::new(width, WidthAlignmentRequirement::MultipleOfFour)
214 }
215
216 #[inline]
218 pub const fn width(&self) -> usize {
219 self.width
220 }
221
222 #[inline]
224 pub const fn required(&self) -> WidthAlignmentRequirement {
225 self.required
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant, Display)]
231#[non_exhaustive]
232pub enum WidthAlignmentRequirement {
233 #[display("is odd")]
235 Even,
236 #[display("is not a multiple of 4")]
243 MultipleOfFour,
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
248#[error("width ({width}) overflow")]
249pub struct WidthOverflow {
250 width: u32,
251}
252
253impl WidthOverflow {
254 #[inline]
256 pub const fn new(width: u32) -> Self {
257 Self { width }
258 }
259 #[inline]
261 pub const fn width(&self) -> u32 {
262 self.width
263 }
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
268#[error("unsupported BITS ({bits})")]
269pub struct UnsupportedBits {
270 bits: u32,
271}
272
273impl UnsupportedBits {
274 #[inline]
276 pub const fn new(bits: u32) -> Self {
277 Self { bits }
278 }
279 #[inline]
281 pub const fn bits(&self) -> u32 {
282 self.bits
283 }
284}
285
286#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
304#[cfg_attr(
305 feature = "quickcheck",
306 derive(::quickcheck_richderive::Arbitrary),
307 quickcheck(arbitrary = "crate::quickcheck_helpers::coded::dimensions")
308)]
309#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
310pub struct Dimensions {
311 width: u32,
312 height: u32,
313}
314
315impl Dimensions {
316 #[cfg_attr(not(tarpaulin), inline(always))]
319 pub const fn new(width: u32, height: u32) -> Self {
320 Self { width, height }
321 }
322
323 #[cfg_attr(not(tarpaulin), inline(always))]
325 pub const fn width(&self) -> u32 {
326 self.width
327 }
328
329 #[cfg_attr(not(tarpaulin), inline(always))]
331 pub const fn height(&self) -> u32 {
332 self.height
333 }
334
335 #[must_use]
337 #[cfg_attr(not(tarpaulin), inline(always))]
338 pub const fn with_width(mut self, width: u32) -> Self {
339 self.width = width;
340 self
341 }
342
343 #[cfg_attr(not(tarpaulin), inline(always))]
345 pub const fn set_width(&mut self, width: u32) -> &mut Self {
346 self.width = width;
347 self
348 }
349
350 #[must_use]
352 #[cfg_attr(not(tarpaulin), inline(always))]
353 pub const fn with_height(mut self, height: u32) -> Self {
354 self.height = height;
355 self
356 }
357
358 #[cfg_attr(not(tarpaulin), inline(always))]
360 pub const fn set_height(&mut self, height: u32) -> &mut Self {
361 self.height = height;
362 self
363 }
364
365 #[cfg_attr(not(tarpaulin), inline(always))]
368 pub const fn is_zero(&self) -> bool {
369 self.width == 0 && self.height == 0
370 }
371}
372
373impl core::fmt::Display for Dimensions {
374 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
375 write!(f, "{}x{}", self.width, self.height)
376 }
377}
378
379#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
384#[cfg_attr(
385 feature = "quickcheck",
386 derive(::quickcheck_richderive::Arbitrary),
387 quickcheck(arbitrary = "crate::quickcheck_helpers::coded::rect")
388)]
389#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
390pub struct Rect {
391 x: u32,
392 y: u32,
393 width: u32,
394 height: u32,
395}
396
397impl Rect {
398 #[cfg_attr(not(tarpaulin), inline(always))]
400 pub const fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
401 Self {
402 x,
403 y,
404 width,
405 height,
406 }
407 }
408
409 #[cfg_attr(not(tarpaulin), inline(always))]
411 pub const fn x(&self) -> u32 {
412 self.x
413 }
414
415 #[cfg_attr(not(tarpaulin), inline(always))]
417 pub const fn y(&self) -> u32 {
418 self.y
419 }
420
421 #[cfg_attr(not(tarpaulin), inline(always))]
423 pub const fn width(&self) -> u32 {
424 self.width
425 }
426
427 #[cfg_attr(not(tarpaulin), inline(always))]
429 pub const fn height(&self) -> u32 {
430 self.height
431 }
432
433 #[must_use]
435 #[cfg_attr(not(tarpaulin), inline(always))]
436 pub const fn with_x(mut self, x: u32) -> Self {
437 self.x = x;
438 self
439 }
440 #[must_use]
442 #[cfg_attr(not(tarpaulin), inline(always))]
443 pub const fn with_y(mut self, y: u32) -> Self {
444 self.y = y;
445 self
446 }
447 #[must_use]
449 #[cfg_attr(not(tarpaulin), inline(always))]
450 pub const fn with_width(mut self, w: u32) -> Self {
451 self.width = w;
452 self
453 }
454 #[must_use]
456 #[cfg_attr(not(tarpaulin), inline(always))]
457 pub const fn with_height(mut self, h: u32) -> Self {
458 self.height = h;
459 self
460 }
461
462 #[cfg_attr(not(tarpaulin), inline(always))]
464 pub const fn set_x(&mut self, x: u32) -> &mut Self {
465 self.x = x;
466 self
467 }
468 #[cfg_attr(not(tarpaulin), inline(always))]
470 pub const fn set_y(&mut self, y: u32) -> &mut Self {
471 self.y = y;
472 self
473 }
474 #[cfg_attr(not(tarpaulin), inline(always))]
476 pub const fn set_width(&mut self, w: u32) -> &mut Self {
477 self.width = w;
478 self
479 }
480 #[cfg_attr(not(tarpaulin), inline(always))]
482 pub const fn set_height(&mut self, h: u32) -> &mut Self {
483 self.height = h;
484 self
485 }
486}
487
488#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
505#[display("{}", self.as_str())]
506#[non_exhaustive]
507#[cfg_attr(
508 feature = "quickcheck",
509 derive(::quickcheck_richderive::Arbitrary),
510 quickcheck(arbitrary = "crate::quickcheck_helpers::coded::rotation")
511)]
512pub enum Rotation {
513 Unknown(u32),
517 #[default]
519 D0,
520 D90,
522 D180,
524 D270,
526}
527
528impl Rotation {
529 #[cfg_attr(not(tarpaulin), inline(always))]
532 pub const fn as_str(&self) -> &'static str {
533 match self {
534 Self::Unknown(_) => "unknown",
535 Self::D0 => "0",
536 Self::D90 => "90",
537 Self::D180 => "180",
538 Self::D270 => "270",
539 }
540 }
541
542 #[cfg_attr(not(tarpaulin), inline(always))]
547 pub const fn to_u32(&self) -> u32 {
548 match self {
549 Self::Unknown(v) => *v,
550 Self::D0 => 0,
551 Self::D90 => 1,
552 Self::D180 => 2,
553 Self::D270 => 3,
554 }
555 }
556
557 #[cfg_attr(not(tarpaulin), inline(always))]
561 pub const fn from_u32(v: u32) -> Self {
562 match v {
563 0 => Self::D0,
564 1 => Self::D90,
565 2 => Self::D180,
566 3 => Self::D270,
567 _ => Self::Unknown(v),
568 }
569 }
570}
571
572#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
590#[cfg_attr(
591 feature = "quickcheck",
592 derive(::quickcheck_richderive::Arbitrary),
593 quickcheck(arbitrary = "crate::quickcheck_helpers::coded::sample_aspect_ratio")
594)]
595#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
596pub struct SampleAspectRatio(Rational);
597
598impl Default for SampleAspectRatio {
599 #[cfg_attr(not(tarpaulin), inline(always))]
601 fn default() -> Self {
602 Self(Rational::default())
603 }
604}
605
606impl SampleAspectRatio {
607 #[cfg_attr(not(tarpaulin), inline(always))]
610 pub const fn new(num: u32, den: core::num::NonZeroU32) -> Self {
611 Self(Rational::new(num, den))
612 }
613
614 #[cfg_attr(not(tarpaulin), inline(always))]
616 pub const fn num(&self) -> u32 {
617 self.0.num()
618 }
619
620 #[cfg_attr(not(tarpaulin), inline(always))]
622 pub const fn den(&self) -> core::num::NonZeroU32 {
623 self.0.den()
624 }
625
626 #[cfg_attr(not(tarpaulin), inline(always))]
628 pub const fn is_square(&self) -> bool {
629 self.0.num() == self.0.den().get()
630 }
631
632 #[cfg_attr(not(tarpaulin), inline(always))]
636 pub const fn rational(&self) -> Rational {
637 self.0
638 }
639
640 #[cfg_attr(not(tarpaulin), inline(always))]
643 pub const fn as_rational(&self) -> Rational {
644 self.rational()
645 }
646
647 #[must_use]
649 #[cfg_attr(not(tarpaulin), inline(always))]
650 pub const fn with_num(mut self, num: u32) -> Self {
651 self.0 = self.0.with_num(num);
652 self
653 }
654
655 #[must_use]
657 #[cfg_attr(not(tarpaulin), inline(always))]
658 pub const fn with_den(mut self, den: core::num::NonZeroU32) -> Self {
659 self.0 = self.0.with_den(den);
660 self
661 }
662
663 #[cfg_attr(not(tarpaulin), inline(always))]
665 pub const fn set_num(&mut self, num: u32) -> &mut Self {
666 self.0.set_num(num);
667 self
668 }
669
670 #[cfg_attr(not(tarpaulin), inline(always))]
672 pub const fn set_den(&mut self, den: core::num::NonZeroU32) -> &mut Self {
673 self.0.set_den(den);
674 self
675 }
676}
677
678impl core::fmt::Display for SampleAspectRatio {
679 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
680 write!(f, "{}:{}", self.0.num(), self.0.den())
681 }
682}
683
684impl From<SampleAspectRatio> for Rational {
685 #[cfg_attr(not(tarpaulin), inline(always))]
689 fn from(sar: SampleAspectRatio) -> Self {
690 sar.0
691 }
692}
693
694impl From<Rational> for SampleAspectRatio {
695 #[cfg_attr(not(tarpaulin), inline(always))]
697 fn from(rate: Rational) -> Self {
698 Self(rate)
699 }
700}
701
702#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
717#[cfg_attr(
718 feature = "quickcheck",
719 derive(::quickcheck_richderive::Arbitrary),
720 quickcheck(arbitrary = "crate::quickcheck_helpers::coded::rational")
721)]
722#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
723pub struct Rational {
724 num: u32,
725 den: core::num::NonZeroU32,
726}
727
728impl Default for Rational {
729 #[cfg_attr(not(tarpaulin), inline(always))]
731 fn default() -> Self {
732 Self {
733 num: 1,
734 den: core::num::NonZeroU32::MIN,
735 }
736 }
737}
738
739impl Rational {
740 #[cfg_attr(not(tarpaulin), inline(always))]
743 pub const fn new(num: u32, den: core::num::NonZeroU32) -> Self {
744 Self { num, den }
745 }
746
747 #[cfg_attr(not(tarpaulin), inline(always))]
749 pub const fn num(&self) -> u32 {
750 self.num
751 }
752
753 #[cfg_attr(not(tarpaulin), inline(always))]
755 pub const fn den(&self) -> core::num::NonZeroU32 {
756 self.den
757 }
758
759 #[cfg_attr(not(tarpaulin), inline(always))]
762 pub const fn is_zero(&self) -> bool {
763 self.num == 0
764 }
765
766 #[must_use]
768 #[cfg_attr(not(tarpaulin), inline(always))]
769 pub const fn with_num(mut self, num: u32) -> Self {
770 self.num = num;
771 self
772 }
773
774 #[must_use]
776 #[cfg_attr(not(tarpaulin), inline(always))]
777 pub const fn with_den(mut self, den: core::num::NonZeroU32) -> Self {
778 self.den = den;
779 self
780 }
781
782 #[cfg_attr(not(tarpaulin), inline(always))]
784 pub const fn set_num(&mut self, num: u32) -> &mut Self {
785 self.num = num;
786 self
787 }
788
789 #[cfg_attr(not(tarpaulin), inline(always))]
791 pub const fn set_den(&mut self, den: core::num::NonZeroU32) -> &mut Self {
792 self.den = den;
793 self
794 }
795}
796
797impl core::fmt::Display for Rational {
798 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
799 write!(f, "{}/{}", self.num, self.den)
800 }
801}
802
803#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
821#[cfg_attr(
822 feature = "quickcheck",
823 derive(::quickcheck_richderive::Arbitrary),
824 quickcheck(arbitrary = "crate::quickcheck_helpers::coded::frame_rate")
825)]
826#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
827pub struct FrameRate {
828 rate: Rational,
829 is_vfr: bool,
830}
831
832impl FrameRate {
833 #[cfg_attr(not(tarpaulin), inline(always))]
836 pub const fn new(rate: Rational, is_vfr: bool) -> Self {
837 Self { rate, is_vfr }
838 }
839
840 #[cfg_attr(not(tarpaulin), inline(always))]
842 pub const fn rate(&self) -> Rational {
843 self.rate
844 }
845
846 #[cfg_attr(not(tarpaulin), inline(always))]
849 pub const fn is_vfr(&self) -> bool {
850 self.is_vfr
851 }
852
853 #[must_use]
855 #[cfg_attr(not(tarpaulin), inline(always))]
856 pub const fn with_rate(mut self, rate: Rational) -> Self {
857 self.rate = rate;
858 self
859 }
860
861 #[must_use]
864 #[cfg_attr(not(tarpaulin), inline(always))]
865 pub const fn with_is_vfr(mut self) -> Self {
866 self.is_vfr = true;
867 self
868 }
869
870 #[must_use]
872 #[cfg_attr(not(tarpaulin), inline(always))]
873 pub const fn maybe_is_vfr(mut self, is_vfr: bool) -> Self {
874 self.is_vfr = is_vfr;
875 self
876 }
877
878 #[cfg_attr(not(tarpaulin), inline(always))]
880 pub const fn set_rate(&mut self, rate: Rational) -> &mut Self {
881 self.rate = rate;
882 self
883 }
884
885 #[cfg_attr(not(tarpaulin), inline(always))]
887 pub const fn set_is_vfr(&mut self) -> &mut Self {
888 self.is_vfr = true;
889 self
890 }
891
892 #[cfg_attr(not(tarpaulin), inline(always))]
894 pub const fn update_is_vfr(&mut self, is_vfr: bool) -> &mut Self {
895 self.is_vfr = is_vfr;
896 self
897 }
898
899 #[cfg_attr(not(tarpaulin), inline(always))]
901 pub const fn clear_is_vfr(&mut self) -> &mut Self {
902 self.is_vfr = false;
903 self
904 }
905}
906
907#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
922#[display("{}", self.as_str())]
923#[non_exhaustive]
924#[cfg_attr(
925 feature = "quickcheck",
926 derive(::quickcheck_richderive::Arbitrary),
927 quickcheck(arbitrary = "crate::quickcheck_helpers::coded::field_order")
928)]
929pub enum FieldOrder {
930 Unknown(u32),
935 Progressive,
937 Tt,
939 Bb,
941 Tb,
943 Bt,
945}
946
947impl Default for FieldOrder {
948 #[cfg_attr(not(tarpaulin), inline(always))]
950 fn default() -> Self {
951 Self::Unknown(0)
952 }
953}
954
955impl FieldOrder {
956 #[cfg_attr(not(tarpaulin), inline(always))]
960 pub const fn as_str(&self) -> &'static str {
961 match self {
962 Self::Unknown(_) => "unknown",
963 Self::Progressive => "progressive",
964 Self::Tt => "tt",
965 Self::Bb => "bb",
966 Self::Tb => "tb",
967 Self::Bt => "bt",
968 }
969 }
970
971 #[cfg_attr(not(tarpaulin), inline(always))]
977 pub const fn to_u32(&self) -> u32 {
978 match self {
979 Self::Unknown(v) => *v,
980 Self::Progressive => 1,
981 Self::Tt => 2,
982 Self::Bb => 3,
983 Self::Tb => 4,
984 Self::Bt => 5,
985 }
986 }
987
988 #[cfg_attr(not(tarpaulin), inline(always))]
993 pub const fn from_u32(v: u32) -> Self {
994 match v {
995 1 => Self::Progressive,
996 2 => Self::Tt,
997 3 => Self::Bb,
998 4 => Self::Tb,
999 5 => Self::Bt,
1000 _ => Self::Unknown(v),
1001 }
1002 }
1003}
1004
1005#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
1024#[display("{}", self.as_str())]
1025#[non_exhaustive]
1026#[cfg_attr(
1027 feature = "quickcheck",
1028 derive(::quickcheck_richderive::Arbitrary),
1029 quickcheck(arbitrary = "crate::quickcheck_helpers::coded::stereo_mode")
1030)]
1031pub enum StereoMode {
1032 Unknown(u32),
1036 Mono,
1039 SideBySide,
1041 TopBottom,
1043 FrameSequence,
1045 Checkerboard,
1047 SideBySideQuincunx,
1049 Lines,
1051 Columns,
1053}
1054
1055impl Default for StereoMode {
1056 #[cfg_attr(not(tarpaulin), inline(always))]
1060 fn default() -> Self {
1061 Self::Mono
1062 }
1063}
1064
1065impl StereoMode {
1066 #[cfg_attr(not(tarpaulin), inline(always))]
1069 pub const fn as_str(&self) -> &'static str {
1070 match self {
1071 Self::Unknown(_) => "unknown",
1072 Self::Mono => "mono",
1073 Self::SideBySide => "side-by-side",
1074 Self::TopBottom => "top-bottom",
1075 Self::FrameSequence => "frame-sequence",
1076 Self::Checkerboard => "checkerboard",
1077 Self::SideBySideQuincunx => "side-by-side-quincunx",
1078 Self::Lines => "lines",
1079 Self::Columns => "columns",
1080 }
1081 }
1082
1083 #[cfg_attr(not(tarpaulin), inline(always))]
1090 pub const fn to_u32(&self) -> u32 {
1091 match self {
1092 Self::Unknown(v) => *v,
1093 Self::Mono => 0,
1094 Self::SideBySide => 1,
1095 Self::TopBottom => 2,
1096 Self::FrameSequence => 3,
1097 Self::Checkerboard => 4,
1098 Self::SideBySideQuincunx => 5,
1099 Self::Lines => 6,
1100 Self::Columns => 7,
1101 }
1102 }
1103
1104 #[cfg_attr(not(tarpaulin), inline(always))]
1110 pub const fn from_u32(v: u32) -> Self {
1111 match v {
1112 0 => Self::Mono,
1113 1 => Self::SideBySide,
1114 2 => Self::TopBottom,
1115 3 => Self::FrameSequence,
1116 4 => Self::Checkerboard,
1117 5 => Self::SideBySideQuincunx,
1118 6 => Self::Lines,
1119 7 => Self::Columns,
1120 _ => Self::Unknown(v),
1121 }
1122 }
1123}
1124
1125#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1136pub struct Plane<B> {
1137 data: B,
1138 stride: u32,
1139}
1140
1141impl<B> Plane<B> {
1142 #[cfg_attr(not(tarpaulin), inline(always))]
1144 pub const fn new(data: B, stride: u32) -> Self {
1145 Self { data, stride }
1146 }
1147
1148 #[cfg_attr(not(tarpaulin), inline(always))]
1150 pub const fn stride(&self) -> u32 {
1151 self.stride
1152 }
1153
1154 #[cfg_attr(not(tarpaulin), inline(always))]
1156 pub const fn data_ref(&self) -> &B {
1157 &self.data
1158 }
1159
1160 #[cfg_attr(not(tarpaulin), inline(always))]
1162 pub fn data_mut(&mut self) -> &mut B {
1163 &mut self.data
1164 }
1165
1166 #[cfg_attr(not(tarpaulin), inline(always))]
1168 pub fn into_data(self) -> B {
1169 self.data
1170 }
1171
1172 #[must_use]
1174 #[cfg_attr(not(tarpaulin), inline(always))]
1175 pub const fn with_stride(mut self, stride: u32) -> Self {
1176 self.stride = stride;
1177 self
1178 }
1179
1180 #[cfg_attr(not(tarpaulin), inline(always))]
1182 pub const fn set_stride(&mut self, stride: u32) -> &mut Self {
1183 self.stride = stride;
1184 self
1185 }
1186}
1187
1188#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1211pub struct VideoFrame<P, B> {
1212 dimensions: Dimensions,
1213 visible_rect: Option<Rect>,
1214 pixel_format: P,
1215 plane_count: u8,
1216 planes: [Plane<B>; 4],
1217 color: crate::color::Info,
1218}
1219
1220impl<P, B> VideoFrame<P, B> {
1221 #[cfg_attr(not(tarpaulin), inline(always))]
1231 pub const fn new(
1232 dimensions: Dimensions,
1233 pixel_format: P,
1234 planes: [Plane<B>; 4],
1235 plane_count: u8,
1236 ) -> Self {
1237 assert!(
1238 plane_count as usize <= 4,
1239 "VideoFrame::new: plane_count exceeds the fixed 4-plane array",
1240 );
1241 Self {
1242 dimensions,
1243 visible_rect: None,
1244 pixel_format,
1245 plane_count,
1246 planes,
1247 color: crate::color::Info::UNSPECIFIED,
1248 }
1249 }
1250
1251 #[cfg_attr(not(tarpaulin), inline(always))]
1253 pub const fn dimensions(&self) -> Dimensions {
1254 self.dimensions
1255 }
1256
1257 #[cfg_attr(not(tarpaulin), inline(always))]
1259 pub const fn width(&self) -> u32 {
1260 self.dimensions.width()
1261 }
1262
1263 #[cfg_attr(not(tarpaulin), inline(always))]
1265 pub const fn height(&self) -> u32 {
1266 self.dimensions.height()
1267 }
1268
1269 #[cfg_attr(not(tarpaulin), inline(always))]
1271 pub const fn visible_rect(&self) -> Option<Rect> {
1272 self.visible_rect
1273 }
1274
1275 #[cfg_attr(not(tarpaulin), inline(always))]
1277 pub const fn pixel_format_ref(&self) -> &P {
1278 &self.pixel_format
1279 }
1280
1281 #[cfg_attr(not(tarpaulin), inline(always))]
1283 pub const fn plane_count(&self) -> u8 {
1284 self.plane_count
1285 }
1286
1287 #[cfg_attr(not(tarpaulin), inline(always))]
1289 pub fn planes(&self) -> &[Plane<B>] {
1290 &self.planes[..self.plane_count as usize]
1291 }
1292
1293 #[cfg_attr(not(tarpaulin), inline(always))]
1295 pub fn plane(&self, i: usize) -> Option<&Plane<B>> {
1296 if i < self.plane_count as usize {
1297 self.planes.get(i)
1298 } else {
1299 None
1300 }
1301 }
1302
1303 #[cfg_attr(not(tarpaulin), inline(always))]
1305 pub const fn color(&self) -> crate::color::Info {
1306 self.color
1307 }
1308
1309 #[must_use]
1311 #[cfg_attr(not(tarpaulin), inline(always))]
1312 pub const fn with_visible_rect(mut self, v: Rect) -> Self {
1313 self.visible_rect = Some(v);
1314 self
1315 }
1316
1317 #[must_use]
1319 #[cfg_attr(not(tarpaulin), inline(always))]
1320 pub const fn maybe_visible_rect(mut self, v: Option<Rect>) -> Self {
1321 self.visible_rect = v;
1322 self
1323 }
1324
1325 #[must_use]
1327 #[cfg_attr(not(tarpaulin), inline(always))]
1328 pub const fn with_color(mut self, v: crate::color::Info) -> Self {
1329 self.color = v;
1330 self
1331 }
1332
1333 #[cfg_attr(not(tarpaulin), inline(always))]
1335 pub const fn set_visible_rect(&mut self, v: Rect) -> &mut Self {
1336 self.visible_rect = Some(v);
1337 self
1338 }
1339
1340 #[cfg_attr(not(tarpaulin), inline(always))]
1342 pub const fn update_visible_rect(&mut self, v: Option<Rect>) -> &mut Self {
1343 self.visible_rect = v;
1344 self
1345 }
1346
1347 #[cfg_attr(not(tarpaulin), inline(always))]
1349 pub const fn clear_visible_rect(&mut self) -> &mut Self {
1350 self.visible_rect = None;
1351 self
1352 }
1353
1354 #[cfg_attr(not(tarpaulin), inline(always))]
1356 pub const fn set_color(&mut self, v: crate::color::Info) -> &mut Self {
1357 self.color = v;
1358 self
1359 }
1360}
1361
1362#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1384pub struct TimestampedFrame<F> {
1385 pts: Option<mediatime::Timestamp>,
1386 duration: Option<mediatime::Timestamp>,
1388 frame: F,
1389}
1390
1391impl<F> TimestampedFrame<F> {
1392 #[cfg_attr(not(tarpaulin), inline(always))]
1395 pub const fn new(frame: F) -> Self {
1396 Self {
1397 pts: None,
1398 duration: None,
1399 frame,
1400 }
1401 }
1402
1403 #[cfg_attr(not(tarpaulin), inline(always))]
1405 pub const fn pts(&self) -> Option<mediatime::Timestamp> {
1406 self.pts
1407 }
1408
1409 #[cfg_attr(not(tarpaulin), inline(always))]
1411 pub const fn duration(&self) -> Option<mediatime::Timestamp> {
1412 self.duration
1413 }
1414
1415 #[cfg_attr(not(tarpaulin), inline(always))]
1417 pub const fn frame_ref(&self) -> &F {
1418 &self.frame
1419 }
1420
1421 #[cfg_attr(not(tarpaulin), inline(always))]
1423 pub const fn frame_mut(&mut self) -> &mut F {
1424 &mut self.frame
1425 }
1426
1427 #[cfg_attr(not(tarpaulin), inline(always))]
1429 pub fn into_frame(self) -> F {
1430 self.frame
1431 }
1432
1433 #[must_use]
1435 #[cfg_attr(not(tarpaulin), inline(always))]
1436 pub const fn with_pts(mut self, v: mediatime::Timestamp) -> Self {
1437 self.pts = Some(v);
1438 self
1439 }
1440
1441 #[must_use]
1443 #[cfg_attr(not(tarpaulin), inline(always))]
1444 pub const fn maybe_pts(mut self, v: Option<mediatime::Timestamp>) -> Self {
1445 self.pts = v;
1446 self
1447 }
1448
1449 #[must_use]
1451 #[cfg_attr(not(tarpaulin), inline(always))]
1452 pub const fn with_duration(mut self, v: mediatime::Timestamp) -> Self {
1453 self.duration = Some(v);
1454 self
1455 }
1456
1457 #[must_use]
1459 #[cfg_attr(not(tarpaulin), inline(always))]
1460 pub const fn maybe_duration(mut self, v: Option<mediatime::Timestamp>) -> Self {
1461 self.duration = v;
1462 self
1463 }
1464
1465 #[cfg_attr(not(tarpaulin), inline(always))]
1467 pub const fn set_pts(&mut self, v: mediatime::Timestamp) -> &mut Self {
1468 self.pts = Some(v);
1469 self
1470 }
1471
1472 #[cfg_attr(not(tarpaulin), inline(always))]
1474 pub const fn update_pts(&mut self, v: Option<mediatime::Timestamp>) -> &mut Self {
1475 self.pts = v;
1476 self
1477 }
1478
1479 #[cfg_attr(not(tarpaulin), inline(always))]
1481 pub const fn clear_pts(&mut self) -> &mut Self {
1482 self.pts = None;
1483 self
1484 }
1485
1486 #[cfg_attr(not(tarpaulin), inline(always))]
1488 pub const fn set_duration(&mut self, v: mediatime::Timestamp) -> &mut Self {
1489 self.duration = Some(v);
1490 self
1491 }
1492
1493 #[cfg_attr(not(tarpaulin), inline(always))]
1495 pub const fn update_duration(&mut self, v: Option<mediatime::Timestamp>) -> &mut Self {
1496 self.duration = v;
1497 self
1498 }
1499
1500 #[cfg_attr(not(tarpaulin), inline(always))]
1502 pub const fn clear_duration(&mut self) -> &mut Self {
1503 self.duration = None;
1504 self
1505 }
1506}
1507
1508#[cfg(feature = "yuv-planar")]
1511#[cfg_attr(docsrs, doc(cfg(feature = "yuv-planar")))]
1512mod planar_8bit;
1513#[cfg(feature = "yuv-planar")]
1514#[cfg_attr(docsrs, doc(cfg(feature = "yuv-planar")))]
1515mod subsampled_high_bit_planar;
1516use derive_more::{Display, IsVariant};
1517#[cfg(feature = "yuv-planar")]
1518pub use planar_8bit::*;
1519#[cfg(feature = "yuv-planar")]
1520pub use subsampled_high_bit_planar::*;
1521
1522#[cfg(feature = "yuv-semi-planar")]
1523#[cfg_attr(docsrs, doc(cfg(feature = "yuv-semi-planar")))]
1524mod semi_planar_8bit;
1525#[cfg(feature = "yuv-semi-planar")]
1526#[cfg_attr(docsrs, doc(cfg(feature = "yuv-semi-planar")))]
1527mod subsampled_high_bit_pn;
1528#[cfg(feature = "yuv-semi-planar")]
1529pub use semi_planar_8bit::*;
1530#[cfg(feature = "yuv-semi-planar")]
1531pub use subsampled_high_bit_pn::*;
1532
1533#[cfg(feature = "yuva")]
1534#[cfg_attr(docsrs, doc(cfg(feature = "yuva")))]
1535mod yuva;
1536#[cfg(feature = "yuva")]
1537pub use yuva::*;
1538
1539#[cfg(feature = "yuv-packed")]
1540#[cfg_attr(docsrs, doc(cfg(feature = "yuv-packed")))]
1541mod packed_yuv_4_1_1;
1542#[cfg(feature = "yuv-packed")]
1543#[cfg_attr(docsrs, doc(cfg(feature = "yuv-packed")))]
1544mod packed_yuv_8bit;
1545#[cfg(feature = "yuv-packed")]
1546pub use packed_yuv_4_1_1::*;
1547#[cfg(feature = "yuv-packed")]
1548pub use packed_yuv_8bit::*;
1549
1550#[cfg(feature = "yuv-444-packed")]
1551#[cfg_attr(docsrs, doc(cfg(feature = "yuv-444-packed")))]
1552mod packed_yuv_4_4_4;
1553#[cfg(feature = "yuv-444-packed")]
1554pub use packed_yuv_4_4_4::*;
1555
1556#[cfg(feature = "y2xx")]
1557#[cfg_attr(docsrs, doc(cfg(feature = "y2xx")))]
1558mod y2xx;
1559#[cfg(feature = "y2xx")]
1560pub use y2xx::*;
1561
1562#[cfg(feature = "v210")]
1563#[cfg_attr(docsrs, doc(cfg(feature = "v210")))]
1564mod v210;
1565#[cfg(feature = "v210")]
1566pub use v210::*;
1567
1568#[cfg(feature = "rgb")]
1569#[cfg_attr(docsrs, doc(cfg(feature = "rgb")))]
1570mod packed_rgb_10bit;
1571#[cfg(feature = "rgb")]
1572#[cfg_attr(docsrs, doc(cfg(feature = "rgb")))]
1573mod packed_rgb_16bit;
1574#[cfg(feature = "rgb")]
1575#[cfg_attr(docsrs, doc(cfg(feature = "rgb")))]
1576mod packed_rgb_8bit;
1577#[cfg(feature = "rgb")]
1578pub use packed_rgb_8bit::*;
1579#[cfg(feature = "rgb")]
1580pub use packed_rgb_10bit::*;
1581#[cfg(feature = "rgb")]
1582pub use packed_rgb_16bit::*;
1583
1584#[cfg(feature = "rgb-float")]
1585#[cfg_attr(docsrs, doc(cfg(feature = "rgb-float")))]
1586mod packed_rgb_f16;
1587#[cfg(feature = "rgb-float")]
1588#[cfg_attr(docsrs, doc(cfg(feature = "rgb-float")))]
1589mod packed_rgb_float;
1590#[cfg(feature = "rgb-float")]
1591pub use packed_rgb_f16::*;
1592#[cfg(feature = "rgb-float")]
1593pub use packed_rgb_float::*;
1594
1595#[cfg(feature = "rgb-legacy")]
1596#[cfg_attr(docsrs, doc(cfg(feature = "rgb-legacy")))]
1597mod legacy_rgb;
1598#[cfg(feature = "rgb-legacy")]
1599pub use legacy_rgb::*;
1600
1601#[cfg(feature = "gbr")]
1602#[cfg_attr(docsrs, doc(cfg(feature = "gbr")))]
1603mod planar_gbr_8bit;
1604#[cfg(feature = "gbr")]
1605#[cfg_attr(docsrs, doc(cfg(feature = "gbr")))]
1606mod planar_gbr_float;
1607#[cfg(feature = "gbr")]
1608#[cfg_attr(docsrs, doc(cfg(feature = "gbr")))]
1609mod planar_gbr_high_bit;
1610#[cfg(feature = "gbr")]
1611pub use planar_gbr_8bit::*;
1612#[cfg(feature = "gbr")]
1613pub use planar_gbr_float::*;
1614#[cfg(feature = "gbr")]
1615pub use planar_gbr_high_bit::*;
1616
1617#[cfg(feature = "gray")]
1618#[cfg_attr(docsrs, doc(cfg(feature = "gray")))]
1619mod gray;
1620#[cfg(feature = "gray")]
1621pub use gray::*;
1622
1623#[cfg(feature = "bayer")]
1624#[cfg_attr(docsrs, doc(cfg(feature = "bayer")))]
1625mod bayer;
1626#[cfg(feature = "bayer")]
1627pub use bayer::*;
1628
1629#[cfg(feature = "xyz")]
1630#[cfg_attr(docsrs, doc(cfg(feature = "xyz")))]
1631mod xyz12;
1632#[cfg(feature = "xyz")]
1633pub use xyz12::*;
1634
1635#[cfg(feature = "mono")]
1636#[cfg_attr(docsrs, doc(cfg(feature = "mono")))]
1637mod mono1bit;
1638#[cfg(feature = "mono")]
1639#[cfg_attr(docsrs, doc(cfg(feature = "mono")))]
1640mod pal8;
1641#[cfg(feature = "mono")]
1642pub use mono1bit::*;
1643#[cfg(feature = "mono")]
1644pub use pal8::*;
1645
1646#[cfg(test)]
1649mod tests_primitives {
1650 use super::*;
1651
1652 #[test]
1653 fn dimensions_construction_and_accessors() {
1654 let d = Dimensions::new(1920, 1080);
1655 assert_eq!(d.width(), 1920);
1656 assert_eq!(d.height(), 1080);
1657 assert!(!d.is_zero());
1658 assert!(Dimensions::default().is_zero());
1659 }
1660
1661 #[test]
1662 fn dimensions_builder() {
1663 let d = Dimensions::new(0, 0).with_width(640).with_height(480);
1664 assert_eq!(d.width(), 640);
1665 assert_eq!(d.height(), 480);
1666 }
1667
1668 #[cfg(feature = "std")]
1669 #[test]
1670 fn dimensions_display() {
1671 assert_eq!(std::format!("{}", Dimensions::new(1920, 1080)), "1920x1080");
1672 }
1673
1674 #[test]
1675 fn rect_construction_and_accessors() {
1676 let r = Rect::new(10, 20, 1280, 720);
1677 assert_eq!(r.x(), 10);
1678 assert_eq!(r.y(), 20);
1679 assert_eq!(r.width(), 1280);
1680 assert_eq!(r.height(), 720);
1681 }
1682
1683 #[test]
1684 fn rect_builder_chains() {
1685 let r = Rect::default()
1686 .with_x(8)
1687 .with_y(8)
1688 .with_width(640)
1689 .with_height(360);
1690 assert_eq!((r.x(), r.y(), r.width(), r.height()), (8, 8, 640, 360));
1691 }
1692
1693 #[test]
1694 fn rotation_defaults_and_as_str() {
1695 assert!(matches!(Rotation::default(), Rotation::D0));
1696 assert_eq!(Rotation::D0.as_str(), "0");
1697 assert_eq!(Rotation::D90.as_str(), "90");
1698 assert_eq!(Rotation::D180.as_str(), "180");
1699 assert_eq!(Rotation::D270.as_str(), "270");
1700 assert!(Rotation::D90.is_d_90());
1701 }
1702
1703 #[test]
1704 fn rotation_u32_round_trip_and_unknown() {
1705 for r in [
1706 Rotation::D0,
1707 Rotation::D90,
1708 Rotation::D180,
1709 Rotation::D270,
1710 Rotation::Unknown(99),
1711 Rotation::Unknown(4242),
1712 ] {
1713 assert_eq!(Rotation::from_u32(r.to_u32()), r);
1714 }
1715 assert_eq!(Rotation::from_u32(0), Rotation::D0);
1716 assert_eq!(Rotation::from_u32(3), Rotation::D270);
1717 assert_eq!(Rotation::from_u32(99), Rotation::Unknown(99));
1719 assert_eq!(Rotation::from_u32(99).to_u32(), 99);
1720 }
1721
1722 #[test]
1723 fn sample_aspect_ratio_default_is_square() {
1724 let s = SampleAspectRatio::default();
1725 assert_eq!(s.num(), 1);
1726 assert_eq!(s.den().get(), 1);
1727 assert!(s.is_square());
1728 }
1729
1730 #[test]
1731 fn sample_aspect_ratio_construction_and_builders() {
1732 let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
1733 let s = SampleAspectRatio::new(40, nz(33));
1734 assert_eq!(s.num(), 40);
1735 assert_eq!(s.den().get(), 33);
1736 assert!(!s.is_square());
1737 let s2 = SampleAspectRatio::default().with_num(16).with_den(nz(9));
1738 assert_eq!((s2.num(), s2.den().get()), (16, 9));
1739 let mut s3 = SampleAspectRatio::default();
1740 s3.set_num(4).set_den(nz(3));
1741 assert_eq!((s3.num(), s3.den().get()), (4, 3));
1742 }
1743
1744 #[cfg(feature = "std")]
1745 #[test]
1746 fn sample_aspect_ratio_display() {
1747 let nz = core::num::NonZeroU32::new(11).unwrap();
1748 assert_eq!(std::format!("{}", SampleAspectRatio::new(10, nz)), "10:11");
1749 }
1750
1751 #[test]
1752 fn plane_holds_owned_buffer() {
1753 let p: Plane<[u8; 4]> = Plane::new([1, 2, 3, 4], 4);
1754 assert_eq!(p.stride(), 4);
1755 assert_eq!(p.data_ref(), &[1, 2, 3, 4]);
1756 let raw = p.into_data();
1757 assert_eq!(raw, [1, 2, 3, 4]);
1758 }
1759
1760 #[test]
1761 fn plane_holds_borrowed_buffer() {
1762 let backing = [10u8, 20, 30, 40];
1763 let p: Plane<&[u8]> = Plane::new(&backing[..], 2);
1764 assert_eq!(p.stride(), 2);
1765 assert_eq!(*p.data_ref(), &[10, 20, 30, 40][..]);
1766 }
1767
1768 #[test]
1769 fn plane_with_stride_builder() {
1770 let p = Plane::new([0u8; 2], 0).with_stride(64);
1771 assert_eq!(p.stride(), 64);
1772 }
1773
1774 use crate::{color::Info, pixel_format::PixelFormat};
1777
1778 #[test]
1779 fn video_frame_construction_defaults() {
1780 let planes: [Plane<&[u8]>; 4] = [
1781 Plane::new(&[][..], 16),
1782 Plane::new(&[][..], 8),
1783 Plane::new(&[][..], 8),
1784 Plane::new(&[][..], 0),
1785 ];
1786 let vf = VideoFrame::new(Dimensions::new(16, 16), PixelFormat::Yuv420p, planes, 3);
1787 assert_eq!(vf.dimensions(), Dimensions::new(16, 16));
1788 assert_eq!(vf.width(), 16);
1789 assert_eq!(vf.height(), 16);
1790 assert_eq!(*vf.pixel_format_ref(), PixelFormat::Yuv420p);
1791 assert_eq!(vf.plane_count(), 3);
1792 assert!(vf.visible_rect().is_none());
1793 assert_eq!(vf.color(), Info::UNSPECIFIED);
1794 }
1795
1796 #[test]
1797 fn video_frame_planes_slice_uses_plane_count() {
1798 let planes: [Plane<u32>; 4] = [
1799 Plane::new(1, 0),
1800 Plane::new(2, 0),
1801 Plane::new(3, 0),
1802 Plane::new(4, 0),
1803 ];
1804 let vf = VideoFrame::new(Dimensions::new(2, 2), PixelFormat::Yuv420p, planes, 2);
1805 assert_eq!(vf.planes().len(), 2);
1806 assert_eq!(*vf.plane(0).unwrap().data_ref(), 1);
1807 assert_eq!(*vf.plane(1).unwrap().data_ref(), 2);
1808 assert!(vf.plane(2).is_none());
1809 assert!(vf.plane(7).is_none());
1810 }
1811
1812 #[test]
1813 #[should_panic(expected = "plane_count exceeds the fixed 4-plane array")]
1814 fn video_frame_new_panics_on_plane_count_over_4() {
1815 let planes: [Plane<()>; 4] = [Plane::new((), 0); 4];
1816 let _ = VideoFrame::new(Dimensions::new(1, 1), PixelFormat::Yuv420p, planes, 5);
1817 }
1818
1819 #[test]
1820 fn video_frame_with_visible_rect_and_color_chain() {
1821 let planes: [Plane<()>; 4] = [Plane::new((), 0); 4];
1822 let vf = VideoFrame::new(Dimensions::new(8, 8), PixelFormat::Yuv420p, planes, 3)
1823 .with_visible_rect(Rect::new(0, 0, 6, 6));
1824 assert_eq!(vf.visible_rect(), Some(Rect::new(0, 0, 6, 6)));
1825 }
1826
1827 #[test]
1830 fn timestamped_frame_construction_defaults() {
1831 let tf: TimestampedFrame<&'static str> = TimestampedFrame::new("payload");
1832 assert!(tf.pts().is_none());
1833 assert!(tf.duration().is_none());
1834 assert_eq!(*tf.frame_ref(), "payload");
1835 }
1836
1837 #[test]
1838 fn timestamped_frame_into_frame_consumes() {
1839 let tf = TimestampedFrame::new(42u32);
1840 let raw = tf.into_frame();
1841 assert_eq!(raw, 42);
1842 }
1843
1844 #[test]
1845 fn timestamped_frame_pts_builder() {
1846 let tb = mediatime::Timebase::new(1, core::num::NonZeroU32::new(1000).unwrap());
1847 let ts = mediatime::Timestamp::new(1000, tb);
1848 let tf = TimestampedFrame::new(0u8).with_pts(ts).with_duration(ts);
1849 assert_eq!(tf.pts(), Some(ts));
1850 assert_eq!(tf.duration(), Some(ts));
1851 }
1852
1853 #[test]
1854 fn timestamped_frame_wraps_video_frame() {
1855 let planes: [Plane<()>; 4] = [Plane::new((), 0); 4];
1856 let vf = VideoFrame::new(Dimensions::new(4, 4), PixelFormat::Yuv420p, planes, 3);
1857 let tf = TimestampedFrame::new(vf);
1858 assert_eq!(tf.frame_ref().dimensions(), Dimensions::new(4, 4));
1859 }
1860
1861 #[test]
1864 fn rational_default_is_one_over_one() {
1865 let r = Rational::default();
1866 assert_eq!(r.num(), 1);
1867 assert_eq!(r.den().get(), 1);
1868 assert!(!r.is_zero());
1869 }
1870
1871 #[test]
1872 fn rational_construction_builders_and_is_zero() {
1873 let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
1874 let r = Rational::new(30000, nz(1001));
1875 assert_eq!(r.num(), 30000);
1876 assert_eq!(r.den().get(), 1001);
1877 assert!(!r.is_zero());
1878 let z = Rational::new(0, nz(1));
1879 assert!(z.is_zero());
1880 let r2 = Rational::default().with_num(24).with_den(nz(1));
1881 assert_eq!((r2.num(), r2.den().get()), (24, 1));
1882 let mut r3 = Rational::default();
1883 r3.set_num(16).set_den(nz(9));
1884 assert_eq!((r3.num(), r3.den().get()), (16, 9));
1885 }
1886
1887 #[cfg(feature = "std")]
1888 #[test]
1889 fn rational_display() {
1890 let nz = core::num::NonZeroU32::new(1001).unwrap();
1891 assert_eq!(std::format!("{}", Rational::new(30000, nz)), "30000/1001");
1892 }
1893
1894 #[test]
1897 fn sample_aspect_ratio_rational_interop() {
1898 let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
1899 let sar = SampleAspectRatio::new(40, nz(33));
1900 let via_method: Rational = sar.as_rational();
1901 let via_from: Rational = Rational::from(sar);
1902 let via_into: Rational = sar.into();
1903 assert_eq!(via_method, Rational::new(40, nz(33)));
1904 assert_eq!(via_method, via_from);
1905 assert_eq!(via_from, via_into);
1906 assert_eq!(
1908 SampleAspectRatio::default().as_rational(),
1909 Rational::default()
1910 );
1911 }
1912
1913 #[test]
1914 fn sample_aspect_ratio_rational_round_trip_both_ways() {
1915 let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
1916 let sar = SampleAspectRatio::new(40, nz(33));
1918 let r: Rational = sar.into();
1919 let back: SampleAspectRatio = r.into();
1920 assert_eq!(back, sar);
1921 assert_eq!(sar.rational(), r);
1922 assert_eq!(sar.rational(), sar.as_rational());
1923 let r2 = Rational::new(16, nz(9));
1925 let s2 = SampleAspectRatio::from(r2);
1926 assert_eq!((s2.num(), s2.den().get()), (16, 9));
1927 assert_eq!(Rational::from(s2), r2);
1928 }
1929
1930 #[test]
1931 fn sample_aspect_ratio_default_is_one_to_one() {
1932 let d = SampleAspectRatio::default();
1933 assert_eq!((d.num(), d.den().get()), (1, 1));
1934 assert!(d.is_square());
1935 assert_eq!(d, SampleAspectRatio::new(1, core::num::NonZeroU32::MIN));
1936 }
1937
1938 #[test]
1939 fn sample_aspect_ratio_eq_and_hash_parity() {
1940 use core::hash::{Hash, Hasher};
1941 let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
1942 let a = SampleAspectRatio::new(40, nz(33));
1943 let b = SampleAspectRatio::default().with_num(40).with_den(nz(33));
1944 assert_eq!(a, b);
1945
1946 fn h(s: &SampleAspectRatio) -> u64 {
1947 struct Fnv(u64);
1949 impl Hasher for Fnv {
1950 fn finish(&self) -> u64 {
1951 self.0
1952 }
1953 fn write(&mut self, bytes: &[u8]) {
1954 for &x in bytes {
1955 self.0 = (self.0 ^ x as u64).wrapping_mul(0x0100_0000_01b3);
1956 }
1957 }
1958 }
1959 let mut hasher = Fnv(0xcbf2_9ce4_8422_2325);
1960 s.hash(&mut hasher);
1961 hasher.finish()
1962 }
1963 assert_eq!(h(&a), h(&b));
1964 }
1965
1966 #[test]
1969 fn frame_rate_default_is_one_over_one_cfr() {
1970 let fr = FrameRate::default();
1971 assert_eq!(fr.rate(), Rational::default());
1972 assert!(!fr.is_vfr());
1973 }
1974
1975 #[test]
1976 fn frame_rate_construction_and_builders() {
1977 let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
1978 let ntsc = Rational::new(30000, nz(1001));
1979 let fr = FrameRate::new(ntsc, false);
1980 assert_eq!(fr.rate(), ntsc);
1981 assert!(!fr.is_vfr());
1982 let vfr = FrameRate::default().with_rate(ntsc).with_is_vfr();
1983 assert_eq!(vfr.rate(), ntsc);
1984 assert!(vfr.is_vfr());
1985 let mut fr3 = FrameRate::default();
1986 fr3.set_rate(Rational::new(25, nz(1))).set_is_vfr();
1987 assert_eq!(fr3.rate(), Rational::new(25, nz(1)));
1988 assert!(fr3.is_vfr());
1989 let fr4 = FrameRate::default().maybe_is_vfr(true);
1991 assert!(fr4.is_vfr());
1992 let mut fr5 = FrameRate::default();
1993 fr5.update_is_vfr(true);
1994 assert!(fr5.is_vfr());
1995 fr5.clear_is_vfr();
1996 assert!(!fr5.is_vfr());
1997 }
1998
1999 #[test]
2002 fn field_order_default_is_unknown_zero_and_as_str() {
2003 assert_eq!(FieldOrder::default(), FieldOrder::Unknown(0));
2004 assert_eq!(FieldOrder::Unknown(0).as_str(), "unknown");
2005 assert_eq!(FieldOrder::Progressive.as_str(), "progressive");
2006 assert_eq!(FieldOrder::Tt.as_str(), "tt");
2007 assert_eq!(FieldOrder::Bb.as_str(), "bb");
2008 assert_eq!(FieldOrder::Tb.as_str(), "tb");
2009 assert_eq!(FieldOrder::Bt.as_str(), "bt");
2010 assert!(FieldOrder::Progressive.is_progressive());
2011 }
2012
2013 #[test]
2014 fn field_order_u32_round_trip_and_unknown() {
2015 for f in [
2016 FieldOrder::Progressive,
2017 FieldOrder::Tt,
2018 FieldOrder::Bb,
2019 FieldOrder::Tb,
2020 FieldOrder::Bt,
2021 FieldOrder::Unknown(0),
2022 FieldOrder::Unknown(99),
2023 FieldOrder::Unknown(4242),
2024 ] {
2025 assert_eq!(FieldOrder::from_u32(f.to_u32()), f);
2026 }
2027 assert_eq!(FieldOrder::from_u32(1), FieldOrder::Progressive);
2028 assert_eq!(FieldOrder::from_u32(5), FieldOrder::Bt);
2029 assert_eq!(FieldOrder::from_u32(0), FieldOrder::Unknown(0));
2031 assert_eq!(FieldOrder::from_u32(99), FieldOrder::Unknown(99));
2032 assert_eq!(FieldOrder::from_u32(99).to_u32(), 99);
2033 }
2034
2035 #[test]
2038 fn stereo_mode_default_is_mono_and_as_str() {
2039 assert_eq!(StereoMode::default(), StereoMode::Mono);
2040 assert_eq!(StereoMode::Mono.as_str(), "mono");
2041 assert_eq!(StereoMode::SideBySide.as_str(), "side-by-side");
2042 assert_eq!(StereoMode::Columns.as_str(), "columns");
2043 assert_eq!(StereoMode::Unknown(0).as_str(), "unknown");
2044 assert!(StereoMode::Mono.is_mono());
2045 }
2046
2047 #[test]
2048 fn stereo_mode_u32_round_trip_and_unknown() {
2049 for s in [
2050 StereoMode::Mono,
2051 StereoMode::SideBySide,
2052 StereoMode::TopBottom,
2053 StereoMode::FrameSequence,
2054 StereoMode::Checkerboard,
2055 StereoMode::SideBySideQuincunx,
2056 StereoMode::Lines,
2057 StereoMode::Columns,
2058 StereoMode::Unknown(99),
2059 StereoMode::Unknown(4242),
2060 ] {
2061 assert_eq!(StereoMode::from_u32(s.to_u32()), s);
2062 }
2063 assert_eq!(StereoMode::from_u32(0), StereoMode::Mono);
2064 assert_eq!(StereoMode::from_u32(7), StereoMode::Columns);
2065 assert_eq!(StereoMode::from_u32(99), StereoMode::Unknown(99));
2067 assert_eq!(StereoMode::from_u32(99).to_u32(), 99);
2068 }
2069}
2070
2071#[cfg(all(test, any(feature = "std", feature = "alloc")))]
2074mod tests;