blob: 848fcbbd0863dd4f67e1e8d4c245c416d60f7757 [file] [log] [blame]
//! Safe wrapper for the `VIDIOC_ENUM_FMT` ioctl.
use super::string_from_cstr;
use crate::bindings;
use crate::{Error, Result};
use crate::{PixelFormat, QueueType};
use bitflags::bitflags;
use nix::errno::Errno;
use std::fmt;
use std::mem;
use std::os::unix::io::AsRawFd;
/// Implementors can receive the result from the `enum_fmt` ioctl.
pub trait EnumFmt {
fn from(fmtdesc: bindings::v4l2_fmtdesc) -> Self;
}
bitflags! {
/// Flags returned by the `VIDIOC_ENUM_FMT` ioctl into the `flags` field of
/// `struct v4l2_fmtdesc`.
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 EnumFmt for PixelFormat {
fn from(fmtdesc: bindings::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 EnumFmt for FmtDesc {
fn from(fmtdesc: bindings::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);
}
/// Safe wrapper around the `VIDIOC_ENUM_FMT` ioctl.
pub fn enum_fmt<T: EnumFmt, F: AsRawFd>(fd: &F, queue: QueueType, index: u32) -> Result<T> {
let mut fmtdesc = bindings::v4l2_fmtdesc {
type_: queue as u32,
index,
..unsafe { mem::zeroed() }
};
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(Error::Nix(nix::Error::Sys(Errno::EINVAL))) => None,
_ => {
eprintln!("Unexpected return value for VIDIOC_ENUM_FMT!");
None
}
}
}
}