#[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
}
}
#[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)
}
}
#[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, 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(&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::ColorInfo,
}
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::ColorInfo::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(&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::ColorInfo {
self.color
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_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::ColorInfo) -> Self {
self.color = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_visible_rect(&mut self, v: Option<Rect>) -> &mut Self {
self.visible_rect = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_color(&mut self, v: crate::color::ColorInfo) -> &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(&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: Option<mediatime::Timestamp>) -> Self {
self.pts = v;
self
}
#[must_use]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_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: Option<mediatime::Timestamp>) -> &mut Self {
self.pts = v;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_duration(&mut self, v: Option<mediatime::Timestamp>) -> &mut Self {
self.duration = v;
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 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(), &[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(), &[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::ColorInfo, 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(), PixelFormat::Yuv420p);
assert_eq!(vf.plane_count(), 3);
assert!(vf.visible_rect().is_none());
assert_eq!(vf.color(), ColorInfo::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(), 1);
assert_eq!(*vf.plane(1).unwrap().data(), 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(Some(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(), "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(Some(ts))
.with_duration(Some(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().dimensions(), Dimensions::new(4, 4));
}
}
#[cfg(all(test, any(feature = "std", feature = "alloc")))]
mod tests;