ioctl: add safe v4l2_format representation
diff --git a/lib/src/ioctl.rs b/lib/src/ioctl.rs
index f535901..3ba2e70 100644
--- a/lib/src/ioctl.rs
+++ b/lib/src/ioctl.rs
@@ -112,7 +112,13 @@
use crate::memory::MemoryType;
use crate::memory::Mmap;
use crate::memory::UserPtr;
+use crate::Colorspace;
+use crate::PixelFormat;
+use crate::Quantization;
+use crate::QueueDirection;
use crate::QueueType;
+use crate::XferFunc;
+use crate::YCbCrEncoding;
/// Utility function for sub-modules.
/// Constructs an owned String instance from a slice containing a nul-terminated
@@ -297,9 +303,10 @@
}
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, N)]
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, N)]
#[repr(u32)]
pub enum BufferField {
+ #[default]
Any = bindings::v4l2_field_V4L2_FIELD_ANY,
None = bindings::v4l2_field_V4L2_FIELD_NONE,
Top = bindings::v4l2_field_V4L2_FIELD_TOP,
@@ -929,6 +936,147 @@
}
}
+/// Representation of a validated multi-planar `struct v4l2_format`. It provides accessors returning proper
+/// types instead of `u32`s.
+#[derive(Clone)]
+#[repr(transparent)]
+pub struct V4l2MplaneFormat(bindings::v4l2_format);
+
+impl AsRef<bindings::v4l2_format> for V4l2MplaneFormat {
+ fn as_ref(&self) -> &bindings::v4l2_format {
+ &self.0
+ }
+}
+
+impl AsRef<bindings::v4l2_pix_format_mplane> for V4l2MplaneFormat {
+ fn as_ref(&self) -> &bindings::v4l2_pix_format_mplane {
+ // SAFETY: safe because we verify that the format is pixel multiplanar at construction
+ // time.
+ unsafe { &self.0.fmt.pix_mp }
+ }
+}
+
+#[derive(Debug, Error)]
+pub enum V4l2MplaneFormatFromError {
+ #[error("format is not multi-planar")]
+ NotMultiPlanar,
+ #[error("invalid field type {0}")]
+ InvalidField(u32),
+ #[error("invalid colorspace {0}")]
+ InvalidColorSpace(u32),
+ #[error("invalid number of planes {0}")]
+ InvalidPlanesNumber(u8),
+ #[error("invalid YCbCr encoding {0}")]
+ InvalidYCbCr(u8),
+ #[error("invalid quantization {0}")]
+ InvalidQuantization(u8),
+ #[error("invalid Xfer func {0}")]
+ InvalidXferFunc(u8),
+}
+
+/// Turn a `struct v4l2_format` into its validated version, returning an error if any of the fields
+/// cannot be validated.
+impl TryFrom<bindings::v4l2_format> for V4l2MplaneFormat {
+ type Error = V4l2MplaneFormatFromError;
+
+ fn try_from(format: bindings::v4l2_format) -> Result<Self, Self::Error> {
+ if !matches!(
+ QueueType::n(format.type_),
+ Some(QueueType::VideoCaptureMplane) | Some(QueueType::VideoOutputMplane)
+ ) {
+ return Err(V4l2MplaneFormatFromError::NotMultiPlanar);
+ }
+ let pix_mp = unsafe { &format.fmt.pix_mp };
+
+ if pix_mp.num_planes == 0 || pix_mp.num_planes > bindings::VIDEO_MAX_PLANES as u8 {
+ return Err(V4l2MplaneFormatFromError::InvalidPlanesNumber(
+ pix_mp.num_planes,
+ ));
+ }
+
+ let _ = BufferField::n(pix_mp.field)
+ .ok_or(V4l2MplaneFormatFromError::InvalidField(pix_mp.field))?;
+ let _ = Colorspace::n(pix_mp.colorspace).ok_or(
+ V4l2MplaneFormatFromError::InvalidColorSpace(pix_mp.colorspace),
+ )?;
+ let ycbcr_enc = unsafe { pix_mp.__bindgen_anon_1.ycbcr_enc };
+ let _ = YCbCrEncoding::n(ycbcr_enc as u32)
+ .ok_or(V4l2MplaneFormatFromError::InvalidYCbCr(ycbcr_enc));
+
+ let _ = Quantization::n(pix_mp.quantization as u32).ok_or(
+ V4l2MplaneFormatFromError::InvalidQuantization(pix_mp.quantization),
+ )?;
+ let _ = XferFunc::n(pix_mp.xfer_func as u32)
+ .ok_or(V4l2MplaneFormatFromError::InvalidXferFunc(pix_mp.xfer_func))?;
+
+ Ok(Self(format))
+ }
+}
+
+/// Turn a `struct v4l2_pix_format_mplane` into its validated version, turning any field that can
+/// not be validated into its default value.
+impl From<(QueueDirection, bindings::v4l2_pix_format_mplane)> for V4l2MplaneFormat {
+ fn from((direction, mut pix_mp): (QueueDirection, bindings::v4l2_pix_format_mplane)) -> Self {
+ pix_mp.field = BufferField::n(pix_mp.field).unwrap_or_default() as u32;
+ pix_mp.colorspace = Colorspace::n(pix_mp.colorspace).unwrap_or_default() as u32;
+ let ycbcr_enc = unsafe { pix_mp.__bindgen_anon_1.ycbcr_enc };
+ pix_mp.__bindgen_anon_1.ycbcr_enc =
+ YCbCrEncoding::n(ycbcr_enc as u32).unwrap_or_default() as u8;
+ pix_mp.quantization = Quantization::n(pix_mp.quantization as u32).unwrap_or_default() as u8;
+ pix_mp.xfer_func = XferFunc::n(pix_mp.xfer_func as u32).unwrap_or_default() as u8;
+
+ Self(bindings::v4l2_format {
+ type_: QueueType::from_dir_and_class(direction, crate::QueueClass::VideoMplane) as u32,
+ fmt: bindings::v4l2_format__bindgen_ty_1 { pix_mp },
+ })
+ }
+}
+
+impl V4l2MplaneFormat {
+ /// Returns the direction of the MPLANE queue this format applies to.
+ pub fn direction(&self) -> QueueDirection {
+ QueueType::n(self.0.type_).unwrap().direction()
+ }
+
+ pub fn size(&self) -> (u32, u32) {
+ let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
+ (pix_mp.width, pix_mp.height)
+ }
+
+ pub fn pixelformat(&self) -> PixelFormat {
+ let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
+ PixelFormat::from_u32(pix_mp.pixelformat)
+ }
+
+ pub fn field(&self) -> BufferField {
+ let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
+ // Safe because we checked the boundaries at construction time.
+ BufferField::n(pix_mp.field).unwrap()
+ }
+
+ pub fn colorspace(&self) -> Colorspace {
+ let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
+ // Safe because we checked the boundaries at construction time.
+ Colorspace::n(pix_mp.colorspace).unwrap()
+ }
+
+ pub fn ycbcr_enc(&self) -> YCbCrEncoding {
+ let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
+ // Safe because we checked the boundaries at construction time.
+ YCbCrEncoding::n(unsafe { pix_mp.__bindgen_anon_1.ycbcr_enc as u32 }).unwrap()
+ }
+
+ pub fn quantization(&self) -> Quantization {
+ let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
+ Quantization::n(pix_mp.quantization as u32).unwrap()
+ }
+
+ pub fn xfer_func(&self) -> XferFunc {
+ let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
+ XferFunc::n(pix_mp.xfer_func as u32).unwrap()
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::{bindings, QueueType};
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index f86028e..44e5d3f 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -442,8 +442,9 @@
/// Equivalent of `enum v4l2_colorspace`.
#[repr(u32)]
-#[derive(Debug, Copy, Clone, PartialEq, Eq, N)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, N)]
pub enum Colorspace {
+ #[default]
Default = bindings::v4l2_colorspace_V4L2_COLORSPACE_DEFAULT,
Smpte170M = bindings::v4l2_colorspace_V4L2_COLORSPACE_SMPTE170M,
Smpte240M = bindings::v4l2_colorspace_V4L2_COLORSPACE_SMPTE240M,
@@ -461,8 +462,9 @@
/// Equivalent of `enum v4l2_xfer_func`.
#[repr(u32)]
-#[derive(Debug, Copy, Clone, PartialEq, Eq, N)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, N)]
pub enum XferFunc {
+ #[default]
Default = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_DEFAULT,
F709 = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_709,
Srgb = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_SRGB,
@@ -475,8 +477,9 @@
/// Equivalent of `enum v4l2_ycbcr_encoding`.
#[repr(u32)]
-#[derive(Debug, Copy, Clone, PartialEq, Eq, N)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, N)]
pub enum YCbCrEncoding {
+ #[default]
Default = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_DEFAULT,
E601 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_601,
E709 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_709,
@@ -490,8 +493,9 @@
/// Equivalent of `enum v4l2_quantization`.
#[repr(u32)]
-#[derive(Debug, Copy, Clone, PartialEq, Eq, N)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, N)]
pub enum Quantization {
+ #[default]
Default = bindings::v4l2_quantization_V4L2_QUANTIZATION_DEFAULT,
FullRange = bindings::v4l2_quantization_V4L2_QUANTIZATION_FULL_RANGE,
LimRange = bindings::v4l2_quantization_V4L2_QUANTIZATION_LIM_RANGE,