//! Safe wrapper for the `VIDIOC_ENUM_FMT` ioctl. use super::string_from_cstr; use crate::bindings; use crate::bindings::v4l2_fmtdesc; use crate::{PixelFormat, QueueType}; use bitflags::bitflags; use log::error; use nix::errno::Errno; use std::fmt; use std::os::unix::io::AsRawFd; use thiserror::Error; bitflags! { /// Flags returned by the `VIDIOC_ENUM_FMT` ioctl into the `flags` field of /// `struct v4l2_fmtdesc`. #[derive(Clone, Copy, Debug)] pub struct FormatFlags: u32 { const COMPRESSED = bindings::V4L2_FMT_FLAG_COMPRESSED; const EMULATED = bindings::V4L2_FMT_FLAG_EMULATED; } } /// Quickly get the Fourcc code of a format. impl From for PixelFormat { fn from(fmtdesc: v4l2_fmtdesc) -> Self { fmtdesc.pixelformat.into() } } /// Safe variant of the `v4l2_fmtdesc` struct, to be used with `enum_fmt`. #[derive(Debug)] pub struct FmtDesc { pub flags: FormatFlags, pub description: String, pub pixelformat: PixelFormat, } impl fmt::Display for FmtDesc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}: {} {}", self.pixelformat, self.description, if self.flags.is_empty() { "".into() } else { format!("({:?})", self.flags) } ) } } impl From for FmtDesc { fn from(fmtdesc: v4l2_fmtdesc) -> Self { FmtDesc { flags: FormatFlags::from_bits_truncate(fmtdesc.flags), description: string_from_cstr(&fmtdesc.description).unwrap_or_else(|_| "".into()), pixelformat: fmtdesc.pixelformat.into(), } } } #[doc(hidden)] mod ioctl { use crate::bindings::v4l2_fmtdesc; nix::ioctl_readwrite!(vidioc_enum_fmt, b'V', 2, v4l2_fmtdesc); } #[derive(Debug, Error)] pub enum EnumFmtError { #[error("ioctl error: {0}")] IoctlError(#[from] nix::Error), } impl From for Errno { fn from(err: EnumFmtError) -> Self { match err { EnumFmtError::IoctlError(e) => e, } } } /// Safe wrapper around the `VIDIOC_ENUM_FMT` ioctl. pub fn enum_fmt>( fd: &impl AsRawFd, queue: QueueType, index: u32, ) -> Result { let mut fmtdesc = v4l2_fmtdesc { type_: queue as u32, index, ..Default::default() }; unsafe { ioctl::vidioc_enum_fmt(fd.as_raw_fd(), &mut fmtdesc) }?; Ok(T::from(fmtdesc)) } /// Iterator over the formats of the given queue. This takes a reference to the /// device's file descriptor so no operation that could affect the format /// enumeration can take place while the iterator exists. pub struct FormatIterator<'a, F: AsRawFd> { fd: &'a F, queue: QueueType, index: u32, } impl<'a, F: AsRawFd> FormatIterator<'a, F> { /// Create a new iterator listing all the currently valid formats on /// `queue`. pub fn new(fd: &'a F, queue: QueueType) -> Self { FormatIterator { fd, queue, index: 0, } } } impl<'a, F: AsRawFd> Iterator for FormatIterator<'a, F> { type Item = FmtDesc; fn next(&mut self) -> Option { match enum_fmt(self.fd, self.queue, self.index) { Ok(fmtdesc) => { self.index += 1; Some(fmtdesc) } // EINVAL means we have reached the last format. Err(EnumFmtError::IoctlError(Errno::EINVAL)) => None, _ => { error!("Unexpected return value for VIDIOC_ENUM_FMT!"); None } } } }