//! This library provides two levels of abstraction over V4L2: //! //! * The `ioctl` module provides direct, thin wrappers over the V4L2 ioctls //! with added safety. Note that "safety" here is in terms of memory safety: //! this layer won't guard against passing invalid data that the ioctls will //! reject - it just makes sure that data passed from and to the kernel can //! be accessed safely. Since this is a 1:1 mapping over the V4L2 ioctls, //! working at this level is a bit laborious, although more comfortable than //! doing the same in C. //! //! * The `device` module (still WIP) provides a higher-level abstraction over //! the V4L2 entities, like device and queue. Strong typing will ensure that //! most inconsistencies while using the V4L2 API can be caught at //! compile-time. //! //! These two layers should provide the foundations for higher-level libraries //! to provide safe, specialized APIs that support various V4L2 usage scenarios //! (camera, decoder/encoder, etc). //! #[doc(hidden)] pub mod bindings; pub mod controls; pub mod decoder; pub mod device; pub mod encoder; pub mod ioctl; pub mod memory; // This can be needed to match nix errors that we expose. pub use nix; use std::convert::TryFrom; use std::fmt; use std::fmt::{Debug, Display}; use enumn::N; use thiserror::Error; // The goal of this library is to provide two layers of abstraction: // ioctl: direct, safe counterparts of the V4L2 ioctls. // device/queue/buffer: higher abstraction, still mapping to core V4L2 mechanics. /// Possible directions for the queue #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum QueueDirection { Output, Capture, } /// Possible classes for this queue. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum QueueClass { Video, Vbi, SlicedVbi, VideoOverlay, VideoMplane, Sdr, Meta, } /// Types of queues currently supported by this library. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, N)] #[repr(u32)] pub enum QueueType { VideoCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE, VideoOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT, VideoOverlay = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OVERLAY, VbiCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VBI_CAPTURE, VbiOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VBI_OUTPUT, SlicedVbiCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_SLICED_VBI_CAPTURE, SlicedVbiOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_SLICED_VBI_OUTPUT, VideoOutputOverlay = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, VideoCaptureMplane = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, VideoOutputMplane = bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, SdrCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_SDR_CAPTURE, SdrOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_SDR_OUTPUT, MetaCapture = bindings::v4l2_buf_type_V4L2_BUF_TYPE_META_CAPTURE, MetaOutput = bindings::v4l2_buf_type_V4L2_BUF_TYPE_META_OUTPUT, } impl QueueType { /// Returns the queue corresponding to the passed `direction` and `class`. pub fn from_dir_and_class(direction: QueueDirection, class: QueueClass) -> Self { match (direction, class) { (QueueDirection::Capture, QueueClass::Video) => Self::VideoCapture, (QueueDirection::Output, QueueClass::Video) => Self::VideoOutput, (QueueDirection::Capture, QueueClass::VideoOverlay) => Self::VideoOverlay, (QueueDirection::Output, QueueClass::VideoOverlay) => Self::VideoOutputOverlay, (QueueDirection::Capture, QueueClass::Vbi) => Self::VbiCapture, (QueueDirection::Output, QueueClass::Vbi) => Self::VbiOutput, (QueueDirection::Capture, QueueClass::SlicedVbi) => Self::SlicedVbiCapture, (QueueDirection::Output, QueueClass::SlicedVbi) => Self::SlicedVbiOutput, (QueueDirection::Capture, QueueClass::VideoMplane) => Self::VideoCaptureMplane, (QueueDirection::Output, QueueClass::VideoMplane) => Self::VideoOutputMplane, (QueueDirection::Capture, QueueClass::Sdr) => Self::SdrCapture, (QueueDirection::Output, QueueClass::Sdr) => Self::SdrOutput, (QueueDirection::Capture, QueueClass::Meta) => Self::MetaCapture, (QueueDirection::Output, QueueClass::Meta) => Self::MetaOutput, } } /// Returns whether the queue type is multiplanar. pub fn is_multiplanar(&self) -> bool { matches!( self, QueueType::VideoCaptureMplane | QueueType::VideoOutputMplane ) } /// Returns the direction of the queue type (Output or Capture). pub fn direction(&self) -> QueueDirection { match self { QueueType::VideoOutput | QueueType::VideoOutputMplane | QueueType::VideoOverlay | QueueType::VideoOutputOverlay | QueueType::VbiOutput | QueueType::SlicedVbiOutput | QueueType::SdrOutput | QueueType::MetaOutput => QueueDirection::Output, QueueType::VideoCapture | QueueType::VbiCapture | QueueType::SlicedVbiCapture | QueueType::VideoCaptureMplane | QueueType::SdrCapture | QueueType::MetaCapture => QueueDirection::Capture, } } pub fn class(&self) -> QueueClass { match self { QueueType::VideoCapture | QueueType::VideoOutput => QueueClass::Video, QueueType::VideoOverlay | QueueType::VideoOutputOverlay => QueueClass::VideoOverlay, QueueType::VbiCapture | QueueType::VbiOutput => QueueClass::Vbi, QueueType::SlicedVbiCapture | QueueType::SlicedVbiOutput => QueueClass::SlicedVbi, QueueType::VideoCaptureMplane | QueueType::VideoOutputMplane => QueueClass::VideoMplane, QueueType::SdrCapture | QueueType::SdrOutput => QueueClass::Sdr, QueueType::MetaCapture | QueueType::MetaOutput => QueueClass::Meta, } } } impl Display for QueueType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(self, f) } } /// A Fourcc pixel format, used to pass formats to V4L2. It can be converted /// back and forth from a 32-bit integer, or a 4-bytes string. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct PixelFormat(u32); impl PixelFormat { pub const fn from_u32(v: u32) -> Self { Self(v) } pub const fn to_u32(self) -> u32 { self.0 } pub const fn from_fourcc(n: &[u8; 4]) -> Self { Self(n[0] as u32 | (n[1] as u32) << 8 | (n[2] as u32) << 16 | (n[3] as u32) << 24) } pub const fn to_fourcc(self) -> [u8; 4] { self.0.to_le_bytes() } } /// Converts a Fourcc in 32-bit integer format (like the ones passed in V4L2 /// structures) into the matching pixel format. /// /// # Examples /// /// ``` /// # use v4l2r::PixelFormat; /// // Fourcc representation of NV12. /// let nv12 = u32::from_le(0x3231564e); /// let f = PixelFormat::from(nv12); /// assert_eq!(u32::from(f), nv12); /// ``` impl From for PixelFormat { fn from(i: u32) -> Self { Self::from_u32(i) } } /// Converts a pixel format back to its 32-bit representation. /// /// # Examples /// /// ``` /// # use v4l2r::PixelFormat; /// // Fourcc representation of NV12. /// let nv12 = u32::from_le(0x3231564e); /// let f = PixelFormat::from(nv12); /// assert_eq!(u32::from(f), nv12); /// ``` impl From for u32 { fn from(format: PixelFormat) -> Self { format.to_u32() } } /// Simple way to convert a string litteral (e.g. b"NV12") into a pixel /// format that can be passed to V4L2. /// /// # Examples /// /// ``` /// # use v4l2r::PixelFormat; /// let nv12 = b"NV12"; /// let f = PixelFormat::from(nv12); /// assert_eq!(&<[u8; 4]>::from(f), nv12); /// ``` impl From<&[u8; 4]> for PixelFormat { fn from(n: &[u8; 4]) -> Self { Self::from_fourcc(n) } } /// Convert a pixel format back to its 4-character representation. /// /// # Examples /// /// ``` /// # use v4l2r::PixelFormat; /// let nv12 = b"NV12"; /// let f = PixelFormat::from(nv12); /// assert_eq!(&<[u8; 4]>::from(f), nv12); /// ``` impl From for [u8; 4] { fn from(format: PixelFormat) -> Self { format.to_fourcc() } } /// Produces a debug string for this PixelFormat, including its hexadecimal /// and string representation. /// /// # Examples /// /// ``` /// # use v4l2r::PixelFormat; /// // Fourcc representation of NV12. /// let nv12 = u32::from_le(0x3231564e); /// let f = PixelFormat::from(nv12); /// assert_eq!(format!("{:?}", f), "0x3231564e (NV12)"); /// ``` impl fmt::Debug for PixelFormat { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_fmt(format_args!("0x{:08x} ({})", self.0, self)) } } /// Produces a displayable form of this PixelFormat. /// /// # Examples /// /// ``` /// # use v4l2r::PixelFormat; /// // Fourcc representation of NV12. /// let nv12 = u32::from_le(0x3231564e); /// let f = PixelFormat::from(nv12); /// assert_eq!(f.to_string(), "NV12"); /// ``` impl fmt::Display for PixelFormat { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let fourcc = self .0 .to_le_bytes() .iter() .map(|&x| x as char) .collect::(); f.write_str(fourcc.as_str()) } } /// Description of a single plane in a format. #[derive(Debug, PartialEq, Clone, Default)] pub struct PlaneLayout { /// Useful size of the plane ; the backing memory must be at least that large. pub sizeimage: u32, /// Bytes per line of data. Only meaningful for image formats. pub bytesperline: u32, } /// Unified representation of a V4L2 format capable of handling both single /// and multi-planar formats. When the single-planar API is used, only /// one plane shall be used - attempts to have more will be rejected by the /// ioctl wrappers. #[derive(Debug, PartialEq, Clone, Default)] pub struct Format { /// Width of the image in pixels. pub width: u32, /// Height of the image in pixels. pub height: u32, /// Format each pixel is encoded in. pub pixelformat: PixelFormat, /// Individual layout of each plane in this format. The exact number of planes /// is defined by `pixelformat`. pub plane_fmt: Vec, } #[derive(Debug, Error, PartialEq)] pub enum FormatConversionError { #[error("too many planes ({0}) specified,")] TooManyPlanes(usize), #[error("invalid buffer type requested")] InvalidBufferType(u32), } impl TryFrom for Format { type Error = FormatConversionError; fn try_from(fmt: bindings::v4l2_format) -> std::result::Result { match fmt.type_ { bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE | bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT => { let pix = unsafe { &fmt.fmt.pix }; Ok(Format { width: pix.width, height: pix.height, pixelformat: PixelFormat::from(pix.pixelformat), plane_fmt: vec![PlaneLayout { bytesperline: pix.bytesperline, sizeimage: pix.sizeimage, }], }) } bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE | bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE => { let pix_mp = unsafe { &fmt.fmt.pix_mp }; // Can only happen if we passed a malformed v4l2_format. if pix_mp.num_planes as usize > pix_mp.plane_fmt.len() { return Err(Self::Error::TooManyPlanes(pix_mp.num_planes as usize)); } let mut plane_fmt = Vec::new(); for i in 0..pix_mp.num_planes as usize { let plane = &pix_mp.plane_fmt[i]; plane_fmt.push(PlaneLayout { sizeimage: plane.sizeimage, bytesperline: plane.bytesperline, }); } Ok(Format { width: pix_mp.width, height: pix_mp.height, pixelformat: PixelFormat::from(pix_mp.pixelformat), plane_fmt, }) } t => Err(Self::Error::InvalidBufferType(t)), } } } /// Quickly build a usable `Format` from a pixel format and resolution. /// /// # Examples /// /// ``` /// # use v4l2r::Format; /// let f = Format::from((b"NV12", (640, 480))); /// assert_eq!(f.width, 640); /// assert_eq!(f.height, 480); /// assert_eq!(f.pixelformat.to_string(), "NV12"); /// assert_eq!(f.plane_fmt.len(), 0); /// ``` impl> From<(T, (usize, usize))> for Format { fn from((pixel_format, (width, height)): (T, (usize, usize))) -> Self { Format { width: width as u32, height: height as u32, pixelformat: pixel_format.into(), ..Default::default() } } } /// A more elegant representation for `v4l2_rect`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Rect { pub left: i32, pub top: i32, pub width: u32, pub height: u32, } impl Rect { pub fn new(left: i32, top: i32, width: u32, height: u32) -> Rect { Rect { left, top, width, height, } } } impl From for Rect { fn from(rect: bindings::v4l2_rect) -> Self { Rect { left: rect.left, top: rect.top, width: rect.width, height: rect.height, } } } impl From for Rect { fn from(selection: bindings::v4l2_selection) -> Self { Self::from(selection.r) } } impl From for bindings::v4l2_rect { fn from(rect: Rect) -> Self { bindings::v4l2_rect { left: rect.left, top: rect.top, width: rect.width, height: rect.height, } } } impl Display for Rect { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "({}, {}), {}x{}", self.left, self.top, self.width, self.height ) } } /// Equivalent of `enum v4l2_colorspace`. #[repr(u32)] #[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, Rec709 = bindings::v4l2_colorspace_V4L2_COLORSPACE_REC709, Bt878 = bindings::v4l2_colorspace_V4L2_COLORSPACE_BT878, SystemM470 = bindings::v4l2_colorspace_V4L2_COLORSPACE_470_SYSTEM_M, SystemBG470 = bindings::v4l2_colorspace_V4L2_COLORSPACE_470_SYSTEM_BG, Jpeg = bindings::v4l2_colorspace_V4L2_COLORSPACE_JPEG, Srgb = bindings::v4l2_colorspace_V4L2_COLORSPACE_SRGB, OpRgb = bindings::v4l2_colorspace_V4L2_COLORSPACE_OPRGB, Bt2020 = bindings::v4l2_colorspace_V4L2_COLORSPACE_BT2020, Raw = bindings::v4l2_colorspace_V4L2_COLORSPACE_RAW, DciP3 = bindings::v4l2_colorspace_V4L2_COLORSPACE_DCI_P3, } /// Equivalent of `enum v4l2_xfer_func`. #[repr(u32)] #[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, OpRgb = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_OPRGB, Smpte240M = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_SMPTE240M, None = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_NONE, DciP3 = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_DCI_P3, Smpte2084 = bindings::v4l2_xfer_func_V4L2_XFER_FUNC_SMPTE2084, } /// Equivalent of `enum v4l2_ycbcr_encoding`. #[repr(u32)] #[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, Xv601 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_XV601, Xv709 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_XV709, Sycc = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_SYCC, Bt2020 = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_BT2020, Bt2020ConstLum = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_BT2020_CONST_LUM, Smpte240M = bindings::v4l2_ycbcr_encoding_V4L2_YCBCR_ENC_SMPTE240M, } /// Equivalent of `enum v4l2_quantization`. #[repr(u32)] #[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, }