| //! 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<v4l2_fmtdesc> 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<v4l2_fmtdesc> 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<EnumFmtError> 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<T: From<v4l2_fmtdesc>>( | |
| fd: &impl AsRawFd, | |
| queue: QueueType, | |
| index: u32, | |
| ) -> Result<T, EnumFmtError> { | |
| 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<Self::Item> { | |
| 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 | |
| } | |
| } | |
| } | |
| } |