1 //! Safe wrapper for the `VIDIOC_ENUM_FMT` ioctl.
2 use super::string_from_cstr;
3 use crate::bindings;
4 use crate::bindings::v4l2_fmtdesc;
5 use crate::{PixelFormat, QueueType};
6 use bitflags::bitflags;
7 use log::error;
8 use nix::errno::Errno;
9 use std::fmt;
10 use std::os::unix::io::AsRawFd;
11 use thiserror::Error;
12
13 bitflags! {
14 /// Flags returned by the `VIDIOC_ENUM_FMT` ioctl into the `flags` field of
15 /// `struct v4l2_fmtdesc`.
16 #[derive(Clone, Copy, Debug)]
17 pub struct FormatFlags: u32 {
18 const COMPRESSED = bindings::V4L2_FMT_FLAG_COMPRESSED;
19 const EMULATED = bindings::V4L2_FMT_FLAG_EMULATED;
20 }
21 }
22 /// Quickly get the Fourcc code of a format.
23 impl From<v4l2_fmtdesc> for PixelFormat {
from(fmtdesc: v4l2_fmtdesc) -> Self24 fn from(fmtdesc: v4l2_fmtdesc) -> Self {
25 fmtdesc.pixelformat.into()
26 }
27 }
28
29 /// Safe variant of the `v4l2_fmtdesc` struct, to be used with `enum_fmt`.
30 #[derive(Debug)]
31 pub struct FmtDesc {
32 pub flags: FormatFlags,
33 pub description: String,
34 pub pixelformat: PixelFormat,
35 }
36
37 impl fmt::Display for FmtDesc {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 write!(
40 f,
41 "{}: {} {}",
42 self.pixelformat,
43 self.description,
44 if self.flags.is_empty() {
45 "".into()
46 } else {
47 format!("({:?})", self.flags)
48 }
49 )
50 }
51 }
52
53 impl From<v4l2_fmtdesc> for FmtDesc {
from(fmtdesc: v4l2_fmtdesc) -> Self54 fn from(fmtdesc: v4l2_fmtdesc) -> Self {
55 FmtDesc {
56 flags: FormatFlags::from_bits_truncate(fmtdesc.flags),
57 description: string_from_cstr(&fmtdesc.description).unwrap_or_else(|_| "".into()),
58 pixelformat: fmtdesc.pixelformat.into(),
59 }
60 }
61 }
62
63 #[doc(hidden)]
64 mod ioctl {
65 use crate::bindings::v4l2_fmtdesc;
66 nix::ioctl_readwrite!(vidioc_enum_fmt, b'V', 2, v4l2_fmtdesc);
67 }
68
69 #[derive(Debug, Error)]
70 pub enum EnumFmtError {
71 #[error("ioctl error: {0}")]
72 IoctlError(#[from] nix::Error),
73 }
74
75 impl From<EnumFmtError> for Errno {
from(err: EnumFmtError) -> Self76 fn from(err: EnumFmtError) -> Self {
77 match err {
78 EnumFmtError::IoctlError(e) => e,
79 }
80 }
81 }
82
83 /// Safe wrapper around the `VIDIOC_ENUM_FMT` ioctl.
enum_fmt<T: From<v4l2_fmtdesc>>( fd: &impl AsRawFd, queue: QueueType, index: u32, ) -> Result<T, EnumFmtError>84 pub fn enum_fmt<T: From<v4l2_fmtdesc>>(
85 fd: &impl AsRawFd,
86 queue: QueueType,
87 index: u32,
88 ) -> Result<T, EnumFmtError> {
89 let mut fmtdesc = v4l2_fmtdesc {
90 type_: queue as u32,
91 index,
92 ..Default::default()
93 };
94 unsafe { ioctl::vidioc_enum_fmt(fd.as_raw_fd(), &mut fmtdesc) }?;
95
96 Ok(T::from(fmtdesc))
97 }
98
99 /// Iterator over the formats of the given queue. This takes a reference to the
100 /// device's file descriptor so no operation that could affect the format
101 /// enumeration can take place while the iterator exists.
102 pub struct FormatIterator<'a, F: AsRawFd> {
103 fd: &'a F,
104 queue: QueueType,
105 index: u32,
106 }
107
108 impl<'a, F: AsRawFd> FormatIterator<'a, F> {
109 /// Create a new iterator listing all the currently valid formats on
110 /// `queue`.
new(fd: &'a F, queue: QueueType) -> Self111 pub fn new(fd: &'a F, queue: QueueType) -> Self {
112 FormatIterator {
113 fd,
114 queue,
115 index: 0,
116 }
117 }
118 }
119
120 impl<'a, F: AsRawFd> Iterator for FormatIterator<'a, F> {
121 type Item = FmtDesc;
122
next(&mut self) -> Option<Self::Item>123 fn next(&mut self) -> Option<Self::Item> {
124 match enum_fmt(self.fd, self.queue, self.index) {
125 Ok(fmtdesc) => {
126 self.index += 1;
127 Some(fmtdesc)
128 }
129 // EINVAL means we have reached the last format.
130 Err(EnumFmtError::IoctlError(Errno::EINVAL)) => None,
131 _ => {
132 error!("Unexpected return value for VIDIOC_ENUM_FMT!");
133 None
134 }
135 }
136 }
137 }
138