#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("width ({width}) or height ({height}) is zero")]
pub struct ZeroDimension {
width: u32,
height: u32,
}
impl ZeroDimension {
#[inline]
pub const fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
#[inline]
pub const fn width(&self) -> u32 {
self.width
}
#[inline]
pub const fn height(&self) -> u32 {
self.height
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("dimensions {width} × {height} overflow")]
pub struct DimensionOverflow {
width: u32,
height: u32,
}
impl DimensionOverflow {
#[inline]
pub const fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
#[inline]
pub const fn width(&self) -> u32 {
self.width
}
#[inline]
pub const fn height(&self) -> u32 {
self.height
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("stride ({stride}) is smaller than minimum ({min})")]
pub struct InsufficientStride {
stride: u32,
min: u32,
}
impl InsufficientStride {
#[inline]
pub const fn new(stride: u32, min: u32) -> Self {
Self { stride, min }
}
#[inline]
pub const fn stride(&self) -> u32 {
self.stride
}
#[inline]
pub const fn min(&self) -> u32 {
self.min
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("plane has {actual} bytes/samples but at least {expected} are required")]
pub struct InsufficientPlane {
expected: usize,
actual: usize,
}
impl InsufficientPlane {
#[inline]
pub const fn new(expected: usize, actual: usize) -> Self {
Self { expected, actual }
}
#[inline]
pub const fn expected(&self) -> usize {
self.expected
}
#[inline]
pub const fn actual(&self) -> usize {
self.actual
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("declared geometry overflows usize: stride={stride} * rows={rows}")]
pub struct GeometryOverflow {
stride: u32,
rows: u32,
}
impl GeometryOverflow {
#[inline]
pub const fn new(stride: u32, rows: u32) -> Self {
Self { stride, rows }
}
#[inline]
pub const fn stride(&self) -> u32 {
self.stride
}
#[inline]
pub const fn rows(&self) -> u32 {
self.rows
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("width ({width}) {required}")]
pub struct WidthAlignment {
width: usize,
required: WidthAlignmentRequirement,
}
impl WidthAlignment {
#[inline]
const fn new(width: usize, required: WidthAlignmentRequirement) -> Self {
Self { width, required }
}
#[inline]
pub const fn odd(width: usize) -> Self {
Self::new(width, WidthAlignmentRequirement::Even)
}
#[inline]
pub const fn multiple_of_four(width: usize) -> Self {
Self::new(width, WidthAlignmentRequirement::MultipleOfFour)
}
#[inline]
pub const fn width(&self) -> usize {
self.width
}
#[inline]
pub const fn required(&self) -> WidthAlignmentRequirement {
self.required
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant, Display)]
#[non_exhaustive]
pub enum WidthAlignmentRequirement {
#[display("is odd")]
Even,
#[display("is not a multiple of 4")]
MultipleOfFour,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("width ({width}) overflow")]
pub struct WidthOverflow {
width: u32,
}
impl WidthOverflow {
#[inline]
pub const fn new(width: u32) -> Self {
Self { width }
}
#[inline]
pub const fn width(&self) -> u32 {
self.width
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("unsupported BITS ({bits})")]
pub struct UnsupportedBits {
bits: u32,
}
impl UnsupportedBits {
#[inline]
pub const fn new(bits: u32) -> Self {
Self { bits }
}
#[inline]
pub const fn bits(&self) -> u32 {
self.bits
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::coded::dimensions")
)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Dimensions {
width: u32,
height: u32,
}
impl Dimensions {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn width(&self) -> u32 {
self.width
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn height(&self) -> u32 {
self.height
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_width(mut self, width: u32) -> Self {
self.width = width;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_width(&mut self, width: u32) -> &mut Self {
self.width = width;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_height(mut self, height: u32) -> Self {
self.height = height;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_height(&mut self, height: u32) -> &mut Self {
self.height = height;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_zero(&self) -> bool {
self.width == 0 && self.height == 0
}
}
impl core::fmt::Display for Dimensions {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}x{}", self.width, self.height)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::coded::rect")
)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rect {
x: u32,
y: u32,
width: u32,
height: u32,
}
impl Rect {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
Self {
x,
y,
width,
height,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn x(&self) -> u32 {
self.x
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn y(&self) -> u32 {
self.y
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn width(&self) -> u32 {
self.width
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn height(&self) -> u32 {
self.height
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_x(mut self, x: u32) -> Self {
self.x = x;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_y(mut self, y: u32) -> Self {
self.y = y;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_width(mut self, w: u32) -> Self {
self.width = w;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_height(mut self, h: u32) -> Self {
self.height = h;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_x(&mut self, x: u32) -> &mut Self {
self.x = x;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_y(&mut self, y: u32) -> &mut Self {
self.y = y;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_width(&mut self, w: u32) -> &mut Self {
self.width = w;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_height(&mut self, h: u32) -> &mut Self {
self.height = h;
self
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::coded::rotation")
)]
pub enum Rotation {
Unknown(u32),
#[default]
D0,
D90,
D180,
D270,
}
impl Rotation {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Unknown(_) => "unknown",
Self::D0 => "0",
Self::D90 => "90",
Self::D180 => "180",
Self::D270 => "270",
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn to_u32(&self) -> u32 {
match self {
Self::Unknown(v) => *v,
Self::D0 => 0,
Self::D90 => 1,
Self::D180 => 2,
Self::D270 => 3,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn from_u32(v: u32) -> Self {
match v {
0 => Self::D0,
1 => Self::D90,
2 => Self::D180,
3 => Self::D270,
_ => Self::Unknown(v),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::coded::sample_aspect_ratio")
)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SampleAspectRatio(Rational);
impl Default for SampleAspectRatio {
#[cfg_attr(not(tarpaulin), inline(always))]
fn default() -> Self {
Self(Rational::default())
}
}
impl SampleAspectRatio {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(num: u32, den: core::num::NonZeroU32) -> Self {
Self(Rational::new(num, den))
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn num(&self) -> u32 {
self.0.num()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn den(&self) -> core::num::NonZeroU32 {
self.0.den()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_square(&self) -> bool {
self.0.num() == self.0.den().get()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn rational(&self) -> Rational {
self.0
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_rational(&self) -> Rational {
self.rational()
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_num(mut self, num: u32) -> Self {
self.0 = self.0.with_num(num);
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_den(mut self, den: core::num::NonZeroU32) -> Self {
self.0 = self.0.with_den(den);
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_num(&mut self, num: u32) -> &mut Self {
self.0.set_num(num);
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_den(&mut self, den: core::num::NonZeroU32) -> &mut Self {
self.0.set_den(den);
self
}
}
impl core::fmt::Display for SampleAspectRatio {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}:{}", self.0.num(), self.0.den())
}
}
impl From<SampleAspectRatio> for Rational {
#[cfg_attr(not(tarpaulin), inline(always))]
fn from(sar: SampleAspectRatio) -> Self {
sar.0
}
}
impl From<Rational> for SampleAspectRatio {
#[cfg_attr(not(tarpaulin), inline(always))]
fn from(rate: Rational) -> Self {
Self(rate)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::coded::rational")
)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rational {
num: u32,
den: core::num::NonZeroU32,
}
impl Default for Rational {
#[cfg_attr(not(tarpaulin), inline(always))]
fn default() -> Self {
Self {
num: 1,
den: core::num::NonZeroU32::MIN,
}
}
}
impl Rational {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(num: u32, den: core::num::NonZeroU32) -> Self {
Self { num, den }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn num(&self) -> u32 {
self.num
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn den(&self) -> core::num::NonZeroU32 {
self.den
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_zero(&self) -> bool {
self.num == 0
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_num(mut self, num: u32) -> Self {
self.num = num;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_den(mut self, den: core::num::NonZeroU32) -> Self {
self.den = den;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_num(&mut self, num: u32) -> &mut Self {
self.num = num;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_den(&mut self, den: core::num::NonZeroU32) -> &mut Self {
self.den = den;
self
}
}
impl core::fmt::Display for Rational {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}/{}", self.num, self.den)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::coded::frame_rate")
)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FrameRate {
rate: Rational,
is_vfr: bool,
}
impl FrameRate {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(rate: Rational, is_vfr: bool) -> Self {
Self { rate, is_vfr }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn rate(&self) -> Rational {
self.rate
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_vfr(&self) -> bool {
self.is_vfr
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_rate(mut self, rate: Rational) -> Self {
self.rate = rate;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_is_vfr(mut self) -> Self {
self.is_vfr = true;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn maybe_is_vfr(mut self, is_vfr: bool) -> Self {
self.is_vfr = is_vfr;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_rate(&mut self, rate: Rational) -> &mut Self {
self.rate = rate;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_is_vfr(&mut self) -> &mut Self {
self.is_vfr = true;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn update_is_vfr(&mut self, is_vfr: bool) -> &mut Self {
self.is_vfr = is_vfr;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn clear_is_vfr(&mut self) -> &mut Self {
self.is_vfr = false;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::coded::field_order")
)]
pub enum FieldOrder {
Unknown(u32),
Progressive,
Tt,
Bb,
Tb,
Bt,
}
impl Default for FieldOrder {
#[cfg_attr(not(tarpaulin), inline(always))]
fn default() -> Self {
Self::Unknown(0)
}
}
impl FieldOrder {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Unknown(_) => "unknown",
Self::Progressive => "progressive",
Self::Tt => "tt",
Self::Bb => "bb",
Self::Tb => "tb",
Self::Bt => "bt",
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn to_u32(&self) -> u32 {
match self {
Self::Unknown(v) => *v,
Self::Progressive => 1,
Self::Tt => 2,
Self::Bb => 3,
Self::Tb => 4,
Self::Bt => 5,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn from_u32(v: u32) -> Self {
match v {
1 => Self::Progressive,
2 => Self::Tt,
3 => Self::Bb,
4 => Self::Tb,
5 => Self::Bt,
_ => Self::Unknown(v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::coded::stereo_mode")
)]
pub enum StereoMode {
Unknown(u32),
Mono,
SideBySide,
TopBottom,
FrameSequence,
Checkerboard,
SideBySideQuincunx,
Lines,
Columns,
}
impl Default for StereoMode {
#[cfg_attr(not(tarpaulin), inline(always))]
fn default() -> Self {
Self::Mono
}
}
impl StereoMode {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Unknown(_) => "unknown",
Self::Mono => "mono",
Self::SideBySide => "side-by-side",
Self::TopBottom => "top-bottom",
Self::FrameSequence => "frame-sequence",
Self::Checkerboard => "checkerboard",
Self::SideBySideQuincunx => "side-by-side-quincunx",
Self::Lines => "lines",
Self::Columns => "columns",
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn to_u32(&self) -> u32 {
match self {
Self::Unknown(v) => *v,
Self::Mono => 0,
Self::SideBySide => 1,
Self::TopBottom => 2,
Self::FrameSequence => 3,
Self::Checkerboard => 4,
Self::SideBySideQuincunx => 5,
Self::Lines => 6,
Self::Columns => 7,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn from_u32(v: u32) -> Self {
match v {
0 => Self::Mono,
1 => Self::SideBySide,
2 => Self::TopBottom,
3 => Self::FrameSequence,
4 => Self::Checkerboard,
5 => Self::SideBySideQuincunx,
6 => Self::Lines,
7 => Self::Columns,
_ => Self::Unknown(v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Plane<B> {
data: B,
stride: u32,
}
impl<B> Plane<B> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(data: B, stride: u32) -> Self {
Self { data, stride }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn stride(&self) -> u32 {
self.stride
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn data_ref(&self) -> &B {
&self.data
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn data_mut(&mut self) -> &mut B {
&mut self.data
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn into_data(self) -> B {
self.data
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_stride(mut self, stride: u32) -> Self {
self.stride = stride;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_stride(&mut self, stride: u32) -> &mut Self {
self.stride = stride;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VideoFrame<P, B> {
dimensions: Dimensions,
visible_rect: Option<Rect>,
pixel_format: P,
plane_count: u8,
planes: [Plane<B>; 4],
color: crate::color::Info,
}
impl<P, B> VideoFrame<P, B> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(
dimensions: Dimensions,
pixel_format: P,
planes: [Plane<B>; 4],
plane_count: u8,
) -> Self {
assert!(
plane_count as usize <= 4,
"VideoFrame::new: plane_count exceeds the fixed 4-plane array",
);
Self {
dimensions,
visible_rect: None,
pixel_format,
plane_count,
planes,
color: crate::color::Info::UNSPECIFIED,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn dimensions(&self) -> Dimensions {
self.dimensions
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn width(&self) -> u32 {
self.dimensions.width()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn height(&self) -> u32 {
self.dimensions.height()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn visible_rect(&self) -> Option<Rect> {
self.visible_rect
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn pixel_format_ref(&self) -> &P {
&self.pixel_format
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn plane_count(&self) -> u8 {
self.plane_count
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn planes(&self) -> &[Plane<B>] {
&self.planes[..self.plane_count as usize]
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn plane(&self, i: usize) -> Option<&Plane<B>> {
if i < self.plane_count as usize {
self.planes.get(i)
} else {
None
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn color(&self) -> crate::color::Info {
self.color
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_visible_rect(mut self, v: Rect) -> Self {
self.visible_rect = Some(v);
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn maybe_visible_rect(mut self, v: Option<Rect>) -> Self {
self.visible_rect = v;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_color(mut self, v: crate::color::Info) -> Self {
self.color = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_visible_rect(&mut self, v: Rect) -> &mut Self {
self.visible_rect = Some(v);
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn update_visible_rect(&mut self, v: Option<Rect>) -> &mut Self {
self.visible_rect = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn clear_visible_rect(&mut self) -> &mut Self {
self.visible_rect = None;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_color(&mut self, v: crate::color::Info) -> &mut Self {
self.color = v;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TimestampedFrame<F> {
pts: Option<mediatime::Timestamp>,
duration: Option<mediatime::Timestamp>,
frame: F,
}
impl<F> TimestampedFrame<F> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(frame: F) -> Self {
Self {
pts: None,
duration: None,
frame,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn pts(&self) -> Option<mediatime::Timestamp> {
self.pts
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn duration(&self) -> Option<mediatime::Timestamp> {
self.duration
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn frame_ref(&self) -> &F {
&self.frame
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn frame_mut(&mut self) -> &mut F {
&mut self.frame
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn into_frame(self) -> F {
self.frame
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_pts(mut self, v: mediatime::Timestamp) -> Self {
self.pts = Some(v);
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn maybe_pts(mut self, v: Option<mediatime::Timestamp>) -> Self {
self.pts = v;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_duration(mut self, v: mediatime::Timestamp) -> Self {
self.duration = Some(v);
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn maybe_duration(mut self, v: Option<mediatime::Timestamp>) -> Self {
self.duration = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_pts(&mut self, v: mediatime::Timestamp) -> &mut Self {
self.pts = Some(v);
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn update_pts(&mut self, v: Option<mediatime::Timestamp>) -> &mut Self {
self.pts = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn clear_pts(&mut self) -> &mut Self {
self.pts = None;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_duration(&mut self, v: mediatime::Timestamp) -> &mut Self {
self.duration = Some(v);
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn update_duration(&mut self, v: Option<mediatime::Timestamp>) -> &mut Self {
self.duration = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn clear_duration(&mut self) -> &mut Self {
self.duration = None;
self
}
}
#[cfg(feature = "yuv-planar")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-planar")))]
mod planar_8bit;
#[cfg(feature = "yuv-planar")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-planar")))]
mod subsampled_high_bit_planar;
use derive_more::{Display, IsVariant};
#[cfg(feature = "yuv-planar")]
pub use planar_8bit::*;
#[cfg(feature = "yuv-planar")]
pub use subsampled_high_bit_planar::*;
#[cfg(feature = "yuv-semi-planar")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-semi-planar")))]
mod semi_planar_8bit;
#[cfg(feature = "yuv-semi-planar")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-semi-planar")))]
mod subsampled_high_bit_pn;
#[cfg(feature = "yuv-semi-planar")]
pub use semi_planar_8bit::*;
#[cfg(feature = "yuv-semi-planar")]
pub use subsampled_high_bit_pn::*;
#[cfg(feature = "yuva")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuva")))]
mod yuva;
#[cfg(feature = "yuva")]
pub use yuva::*;
#[cfg(feature = "yuv-packed")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-packed")))]
mod packed_yuv_4_1_1;
#[cfg(feature = "yuv-packed")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-packed")))]
mod packed_yuv_8bit;
#[cfg(feature = "yuv-packed")]
pub use packed_yuv_4_1_1::*;
#[cfg(feature = "yuv-packed")]
pub use packed_yuv_8bit::*;
#[cfg(feature = "yuv-444-packed")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-444-packed")))]
mod packed_yuv_4_4_4;
#[cfg(feature = "yuv-444-packed")]
pub use packed_yuv_4_4_4::*;
#[cfg(feature = "y2xx")]
#[cfg_attr(docsrs, doc(cfg(feature = "y2xx")))]
mod y2xx;
#[cfg(feature = "y2xx")]
pub use y2xx::*;
#[cfg(feature = "v210")]
#[cfg_attr(docsrs, doc(cfg(feature = "v210")))]
mod v210;
#[cfg(feature = "v210")]
pub use v210::*;
#[cfg(feature = "rgb")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb")))]
mod packed_rgb_10bit;
#[cfg(feature = "rgb")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb")))]
mod packed_rgb_16bit;
#[cfg(feature = "rgb")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb")))]
mod packed_rgb_8bit;
#[cfg(feature = "rgb")]
pub use packed_rgb_8bit::*;
#[cfg(feature = "rgb")]
pub use packed_rgb_10bit::*;
#[cfg(feature = "rgb")]
pub use packed_rgb_16bit::*;
#[cfg(feature = "rgb-float")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb-float")))]
mod packed_rgb_f16;
#[cfg(feature = "rgb-float")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb-float")))]
mod packed_rgb_float;
#[cfg(feature = "rgb-float")]
pub use packed_rgb_f16::*;
#[cfg(feature = "rgb-float")]
pub use packed_rgb_float::*;
#[cfg(feature = "rgb-legacy")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb-legacy")))]
mod legacy_rgb;
#[cfg(feature = "rgb-legacy")]
pub use legacy_rgb::*;
#[cfg(feature = "gbr")]
#[cfg_attr(docsrs, doc(cfg(feature = "gbr")))]
mod planar_gbr_8bit;
#[cfg(feature = "gbr")]
#[cfg_attr(docsrs, doc(cfg(feature = "gbr")))]
mod planar_gbr_float;
#[cfg(feature = "gbr")]
#[cfg_attr(docsrs, doc(cfg(feature = "gbr")))]
mod planar_gbr_high_bit;
#[cfg(feature = "gbr")]
pub use planar_gbr_8bit::*;
#[cfg(feature = "gbr")]
pub use planar_gbr_float::*;
#[cfg(feature = "gbr")]
pub use planar_gbr_high_bit::*;
#[cfg(feature = "gray")]
#[cfg_attr(docsrs, doc(cfg(feature = "gray")))]
mod gray;
#[cfg(feature = "gray")]
pub use gray::*;
#[cfg(feature = "bayer")]
#[cfg_attr(docsrs, doc(cfg(feature = "bayer")))]
mod bayer;
#[cfg(feature = "bayer")]
pub use bayer::*;
#[cfg(feature = "xyz")]
#[cfg_attr(docsrs, doc(cfg(feature = "xyz")))]
mod xyz12;
#[cfg(feature = "xyz")]
pub use xyz12::*;
#[cfg(feature = "mono")]
#[cfg_attr(docsrs, doc(cfg(feature = "mono")))]
mod mono1bit;
#[cfg(feature = "mono")]
#[cfg_attr(docsrs, doc(cfg(feature = "mono")))]
mod pal8;
#[cfg(feature = "mono")]
pub use mono1bit::*;
#[cfg(feature = "mono")]
pub use pal8::*;
#[cfg(test)]
mod tests_primitives {
use super::*;
#[test]
fn dimensions_construction_and_accessors() {
let d = Dimensions::new(1920, 1080);
assert_eq!(d.width(), 1920);
assert_eq!(d.height(), 1080);
assert!(!d.is_zero());
assert!(Dimensions::default().is_zero());
}
#[test]
fn dimensions_builder() {
let d = Dimensions::new(0, 0).with_width(640).with_height(480);
assert_eq!(d.width(), 640);
assert_eq!(d.height(), 480);
}
#[cfg(feature = "std")]
#[test]
fn dimensions_display() {
assert_eq!(std::format!("{}", Dimensions::new(1920, 1080)), "1920x1080");
}
#[test]
fn rect_construction_and_accessors() {
let r = Rect::new(10, 20, 1280, 720);
assert_eq!(r.x(), 10);
assert_eq!(r.y(), 20);
assert_eq!(r.width(), 1280);
assert_eq!(r.height(), 720);
}
#[test]
fn rect_builder_chains() {
let r = Rect::default()
.with_x(8)
.with_y(8)
.with_width(640)
.with_height(360);
assert_eq!((r.x(), r.y(), r.width(), r.height()), (8, 8, 640, 360));
}
#[test]
fn rotation_defaults_and_as_str() {
assert!(matches!(Rotation::default(), Rotation::D0));
assert_eq!(Rotation::D0.as_str(), "0");
assert_eq!(Rotation::D90.as_str(), "90");
assert_eq!(Rotation::D180.as_str(), "180");
assert_eq!(Rotation::D270.as_str(), "270");
assert!(Rotation::D90.is_d_90());
}
#[test]
fn rotation_u32_round_trip_and_unknown() {
for r in [
Rotation::D0,
Rotation::D90,
Rotation::D180,
Rotation::D270,
Rotation::Unknown(99),
Rotation::Unknown(4242),
] {
assert_eq!(Rotation::from_u32(r.to_u32()), r);
}
assert_eq!(Rotation::from_u32(0), Rotation::D0);
assert_eq!(Rotation::from_u32(3), Rotation::D270);
assert_eq!(Rotation::from_u32(99), Rotation::Unknown(99));
assert_eq!(Rotation::from_u32(99).to_u32(), 99);
}
#[test]
fn sample_aspect_ratio_default_is_square() {
let s = SampleAspectRatio::default();
assert_eq!(s.num(), 1);
assert_eq!(s.den().get(), 1);
assert!(s.is_square());
}
#[test]
fn sample_aspect_ratio_construction_and_builders() {
let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
let s = SampleAspectRatio::new(40, nz(33));
assert_eq!(s.num(), 40);
assert_eq!(s.den().get(), 33);
assert!(!s.is_square());
let s2 = SampleAspectRatio::default().with_num(16).with_den(nz(9));
assert_eq!((s2.num(), s2.den().get()), (16, 9));
let mut s3 = SampleAspectRatio::default();
s3.set_num(4).set_den(nz(3));
assert_eq!((s3.num(), s3.den().get()), (4, 3));
}
#[cfg(feature = "std")]
#[test]
fn sample_aspect_ratio_display() {
let nz = core::num::NonZeroU32::new(11).unwrap();
assert_eq!(std::format!("{}", SampleAspectRatio::new(10, nz)), "10:11");
}
#[test]
fn plane_holds_owned_buffer() {
let p: Plane<[u8; 4]> = Plane::new([1, 2, 3, 4], 4);
assert_eq!(p.stride(), 4);
assert_eq!(p.data_ref(), &[1, 2, 3, 4]);
let raw = p.into_data();
assert_eq!(raw, [1, 2, 3, 4]);
}
#[test]
fn plane_holds_borrowed_buffer() {
let backing = [10u8, 20, 30, 40];
let p: Plane<&[u8]> = Plane::new(&backing[..], 2);
assert_eq!(p.stride(), 2);
assert_eq!(*p.data_ref(), &[10, 20, 30, 40][..]);
}
#[test]
fn plane_with_stride_builder() {
let p = Plane::new([0u8; 2], 0).with_stride(64);
assert_eq!(p.stride(), 64);
}
use crate::{color::Info, pixel_format::PixelFormat};
#[test]
fn video_frame_construction_defaults() {
let planes: [Plane<&[u8]>; 4] = [
Plane::new(&[][..], 16),
Plane::new(&[][..], 8),
Plane::new(&[][..], 8),
Plane::new(&[][..], 0),
];
let vf = VideoFrame::new(Dimensions::new(16, 16), PixelFormat::Yuv420p, planes, 3);
assert_eq!(vf.dimensions(), Dimensions::new(16, 16));
assert_eq!(vf.width(), 16);
assert_eq!(vf.height(), 16);
assert_eq!(*vf.pixel_format_ref(), PixelFormat::Yuv420p);
assert_eq!(vf.plane_count(), 3);
assert!(vf.visible_rect().is_none());
assert_eq!(vf.color(), Info::UNSPECIFIED);
}
#[test]
fn video_frame_planes_slice_uses_plane_count() {
let planes: [Plane<u32>; 4] = [
Plane::new(1, 0),
Plane::new(2, 0),
Plane::new(3, 0),
Plane::new(4, 0),
];
let vf = VideoFrame::new(Dimensions::new(2, 2), PixelFormat::Yuv420p, planes, 2);
assert_eq!(vf.planes().len(), 2);
assert_eq!(*vf.plane(0).unwrap().data_ref(), 1);
assert_eq!(*vf.plane(1).unwrap().data_ref(), 2);
assert!(vf.plane(2).is_none());
assert!(vf.plane(7).is_none());
}
#[test]
#[should_panic(expected = "plane_count exceeds the fixed 4-plane array")]
fn video_frame_new_panics_on_plane_count_over_4() {
let planes: [Plane<()>; 4] = [Plane::new((), 0); 4];
let _ = VideoFrame::new(Dimensions::new(1, 1), PixelFormat::Yuv420p, planes, 5);
}
#[test]
fn video_frame_with_visible_rect_and_color_chain() {
let planes: [Plane<()>; 4] = [Plane::new((), 0); 4];
let vf = VideoFrame::new(Dimensions::new(8, 8), PixelFormat::Yuv420p, planes, 3)
.with_visible_rect(Rect::new(0, 0, 6, 6));
assert_eq!(vf.visible_rect(), Some(Rect::new(0, 0, 6, 6)));
}
#[test]
fn timestamped_frame_construction_defaults() {
let tf: TimestampedFrame<&'static str> = TimestampedFrame::new("payload");
assert!(tf.pts().is_none());
assert!(tf.duration().is_none());
assert_eq!(*tf.frame_ref(), "payload");
}
#[test]
fn timestamped_frame_into_frame_consumes() {
let tf = TimestampedFrame::new(42u32);
let raw = tf.into_frame();
assert_eq!(raw, 42);
}
#[test]
fn timestamped_frame_pts_builder() {
let tb = mediatime::Timebase::new(1, core::num::NonZeroU32::new(1000).unwrap());
let ts = mediatime::Timestamp::new(1000, tb);
let tf = TimestampedFrame::new(0u8).with_pts(ts).with_duration(ts);
assert_eq!(tf.pts(), Some(ts));
assert_eq!(tf.duration(), Some(ts));
}
#[test]
fn timestamped_frame_wraps_video_frame() {
let planes: [Plane<()>; 4] = [Plane::new((), 0); 4];
let vf = VideoFrame::new(Dimensions::new(4, 4), PixelFormat::Yuv420p, planes, 3);
let tf = TimestampedFrame::new(vf);
assert_eq!(tf.frame_ref().dimensions(), Dimensions::new(4, 4));
}
#[test]
fn rational_default_is_one_over_one() {
let r = Rational::default();
assert_eq!(r.num(), 1);
assert_eq!(r.den().get(), 1);
assert!(!r.is_zero());
}
#[test]
fn rational_construction_builders_and_is_zero() {
let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
let r = Rational::new(30000, nz(1001));
assert_eq!(r.num(), 30000);
assert_eq!(r.den().get(), 1001);
assert!(!r.is_zero());
let z = Rational::new(0, nz(1));
assert!(z.is_zero());
let r2 = Rational::default().with_num(24).with_den(nz(1));
assert_eq!((r2.num(), r2.den().get()), (24, 1));
let mut r3 = Rational::default();
r3.set_num(16).set_den(nz(9));
assert_eq!((r3.num(), r3.den().get()), (16, 9));
}
#[cfg(feature = "std")]
#[test]
fn rational_display() {
let nz = core::num::NonZeroU32::new(1001).unwrap();
assert_eq!(std::format!("{}", Rational::new(30000, nz)), "30000/1001");
}
#[test]
fn sample_aspect_ratio_rational_interop() {
let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
let sar = SampleAspectRatio::new(40, nz(33));
let via_method: Rational = sar.as_rational();
let via_from: Rational = Rational::from(sar);
let via_into: Rational = sar.into();
assert_eq!(via_method, Rational::new(40, nz(33)));
assert_eq!(via_method, via_from);
assert_eq!(via_from, via_into);
assert_eq!(
SampleAspectRatio::default().as_rational(),
Rational::default()
);
}
#[test]
fn sample_aspect_ratio_rational_round_trip_both_ways() {
let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
let sar = SampleAspectRatio::new(40, nz(33));
let r: Rational = sar.into();
let back: SampleAspectRatio = r.into();
assert_eq!(back, sar);
assert_eq!(sar.rational(), r);
assert_eq!(sar.rational(), sar.as_rational());
let r2 = Rational::new(16, nz(9));
let s2 = SampleAspectRatio::from(r2);
assert_eq!((s2.num(), s2.den().get()), (16, 9));
assert_eq!(Rational::from(s2), r2);
}
#[test]
fn sample_aspect_ratio_default_is_one_to_one() {
let d = SampleAspectRatio::default();
assert_eq!((d.num(), d.den().get()), (1, 1));
assert!(d.is_square());
assert_eq!(d, SampleAspectRatio::new(1, core::num::NonZeroU32::MIN));
}
#[test]
fn sample_aspect_ratio_eq_and_hash_parity() {
use core::hash::{Hash, Hasher};
let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
let a = SampleAspectRatio::new(40, nz(33));
let b = SampleAspectRatio::default().with_num(40).with_den(nz(33));
assert_eq!(a, b);
fn h(s: &SampleAspectRatio) -> u64 {
struct Fnv(u64);
impl Hasher for Fnv {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for &x in bytes {
self.0 = (self.0 ^ x as u64).wrapping_mul(0x0100_0000_01b3);
}
}
}
let mut hasher = Fnv(0xcbf2_9ce4_8422_2325);
s.hash(&mut hasher);
hasher.finish()
}
assert_eq!(h(&a), h(&b));
}
#[test]
fn frame_rate_default_is_one_over_one_cfr() {
let fr = FrameRate::default();
assert_eq!(fr.rate(), Rational::default());
assert!(!fr.is_vfr());
}
#[test]
fn frame_rate_construction_and_builders() {
let nz = |n: u32| core::num::NonZeroU32::new(n).unwrap();
let ntsc = Rational::new(30000, nz(1001));
let fr = FrameRate::new(ntsc, false);
assert_eq!(fr.rate(), ntsc);
assert!(!fr.is_vfr());
let vfr = FrameRate::default().with_rate(ntsc).with_is_vfr();
assert_eq!(vfr.rate(), ntsc);
assert!(vfr.is_vfr());
let mut fr3 = FrameRate::default();
fr3.set_rate(Rational::new(25, nz(1))).set_is_vfr();
assert_eq!(fr3.rate(), Rational::new(25, nz(1)));
assert!(fr3.is_vfr());
let fr4 = FrameRate::default().maybe_is_vfr(true);
assert!(fr4.is_vfr());
let mut fr5 = FrameRate::default();
fr5.update_is_vfr(true);
assert!(fr5.is_vfr());
fr5.clear_is_vfr();
assert!(!fr5.is_vfr());
}
#[test]
fn field_order_default_is_unknown_zero_and_as_str() {
assert_eq!(FieldOrder::default(), FieldOrder::Unknown(0));
assert_eq!(FieldOrder::Unknown(0).as_str(), "unknown");
assert_eq!(FieldOrder::Progressive.as_str(), "progressive");
assert_eq!(FieldOrder::Tt.as_str(), "tt");
assert_eq!(FieldOrder::Bb.as_str(), "bb");
assert_eq!(FieldOrder::Tb.as_str(), "tb");
assert_eq!(FieldOrder::Bt.as_str(), "bt");
assert!(FieldOrder::Progressive.is_progressive());
}
#[test]
fn field_order_u32_round_trip_and_unknown() {
for f in [
FieldOrder::Progressive,
FieldOrder::Tt,
FieldOrder::Bb,
FieldOrder::Tb,
FieldOrder::Bt,
FieldOrder::Unknown(0),
FieldOrder::Unknown(99),
FieldOrder::Unknown(4242),
] {
assert_eq!(FieldOrder::from_u32(f.to_u32()), f);
}
assert_eq!(FieldOrder::from_u32(1), FieldOrder::Progressive);
assert_eq!(FieldOrder::from_u32(5), FieldOrder::Bt);
assert_eq!(FieldOrder::from_u32(0), FieldOrder::Unknown(0));
assert_eq!(FieldOrder::from_u32(99), FieldOrder::Unknown(99));
assert_eq!(FieldOrder::from_u32(99).to_u32(), 99);
}
#[test]
fn stereo_mode_default_is_mono_and_as_str() {
assert_eq!(StereoMode::default(), StereoMode::Mono);
assert_eq!(StereoMode::Mono.as_str(), "mono");
assert_eq!(StereoMode::SideBySide.as_str(), "side-by-side");
assert_eq!(StereoMode::Columns.as_str(), "columns");
assert_eq!(StereoMode::Unknown(0).as_str(), "unknown");
assert!(StereoMode::Mono.is_mono());
}
#[test]
fn stereo_mode_u32_round_trip_and_unknown() {
for s in [
StereoMode::Mono,
StereoMode::SideBySide,
StereoMode::TopBottom,
StereoMode::FrameSequence,
StereoMode::Checkerboard,
StereoMode::SideBySideQuincunx,
StereoMode::Lines,
StereoMode::Columns,
StereoMode::Unknown(99),
StereoMode::Unknown(4242),
] {
assert_eq!(StereoMode::from_u32(s.to_u32()), s);
}
assert_eq!(StereoMode::from_u32(0), StereoMode::Mono);
assert_eq!(StereoMode::from_u32(7), StereoMode::Columns);
assert_eq!(StereoMode::from_u32(99), StereoMode::Unknown(99));
assert_eq!(StereoMode::from_u32(99).to_u32(), 99);
}
}
#[cfg(all(test, any(feature = "std", feature = "alloc")))]
mod tests;