v4l/
device.rs

1use std::convert::TryFrom;
2use std::path::Path;
3use std::sync::Arc;
4use std::{io, mem};
5
6use libc;
7
8use crate::control;
9use crate::v4l2;
10use crate::v4l2::videodev::v4l2_ext_controls;
11use crate::v4l_sys::*;
12use crate::{capability::Capabilities, control::Control};
13
14/// Linux capture device abstraction
15pub struct Device {
16    /// Raw handle
17    handle: Arc<Handle>,
18}
19
20impl Device {
21    /// Returns a capture device by index
22    ///
23    /// Devices are usually enumerated by the system.
24    /// An index of zero thus represents the first device the system got to know about.
25    ///
26    /// # Arguments
27    ///
28    /// * `index` - Index (0: first, 1: second, ..)
29    ///
30    /// # Example
31    ///
32    /// ```
33    /// use v4l::device::Device;
34    /// let dev = Device::new(0);
35    /// ```
36    pub fn new(index: usize) -> io::Result<Self> {
37        let path = format!("{}{}", "/dev/video", index);
38        let fd = v4l2::open(path, libc::O_RDWR | libc::O_NONBLOCK)?;
39
40        if fd == -1 {
41            return Err(io::Error::last_os_error());
42        }
43
44        Ok(Device {
45            handle: Arc::new(Handle::new(fd)),
46        })
47    }
48
49    /// Returns a capture device by path
50    ///
51    /// Linux device nodes are usually found in /dev/videoX or /sys/class/video4linux/videoX.
52    ///
53    /// # Arguments
54    ///
55    /// * `path` - Path (e.g. "/dev/video0")
56    ///
57    /// # Example
58    ///
59    /// ```
60    /// use v4l::device::Device;
61    /// let dev = Device::with_path("/dev/video0");
62    /// ```
63    pub fn with_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
64        let fd = v4l2::open(&path, libc::O_RDWR | libc::O_NONBLOCK)?;
65
66        if fd == -1 {
67            return Err(io::Error::last_os_error());
68        }
69
70        Ok(Device {
71            handle: Arc::new(Handle::new(fd)),
72        })
73    }
74
75    /// Returns the raw device handle
76    pub fn handle(&self) -> Arc<Handle> {
77        self.handle.clone()
78    }
79
80    /// Returns video4linux framework defined information such as card, driver, etc.
81    pub fn query_caps(&self) -> io::Result<Capabilities> {
82        unsafe {
83            let mut v4l2_caps: v4l2_capability = mem::zeroed();
84            v4l2::ioctl(
85                self.handle().fd(),
86                v4l2::vidioc::VIDIOC_QUERYCAP,
87                &mut v4l2_caps as *mut _ as *mut std::os::raw::c_void,
88            )?;
89
90            Ok(Capabilities::from(v4l2_caps))
91        }
92    }
93
94    /// Returns the supported controls for a device such as gain, focus, white balance, etc.
95    pub fn query_controls(&self) -> io::Result<Vec<control::Description>> {
96        let mut controls = Vec::new();
97        unsafe {
98            let mut v4l2_ctrl: v4l2_query_ext_ctrl = mem::zeroed();
99
100            loop {
101                v4l2_ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
102                v4l2_ctrl.id |= V4L2_CTRL_FLAG_NEXT_COMPOUND;
103                match v4l2::ioctl(
104                    self.handle().fd(),
105                    v4l2::vidioc::VIDIOC_QUERY_EXT_CTRL,
106                    &mut v4l2_ctrl as *mut _ as *mut std::os::raw::c_void,
107                ) {
108                    Ok(_) => {
109                        // get the basic control information
110                        let mut control = control::Description::from(v4l2_ctrl);
111
112                        // if this is a menu control, enumerate its items
113                        if control.typ == control::Type::Menu
114                            || control.typ == control::Type::IntegerMenu
115                        {
116                            let mut items = Vec::new();
117
118                            for i in (v4l2_ctrl.minimum..=v4l2_ctrl.maximum)
119                                .step_by(v4l2_ctrl.step as usize)
120                            {
121                                let mut v4l2_menu = v4l2_querymenu {
122                                    id: v4l2_ctrl.id,
123                                    index: i as u32,
124                                    ..mem::zeroed()
125                                };
126                                let res = v4l2::ioctl(
127                                    self.handle().fd(),
128                                    v4l2::vidioc::VIDIOC_QUERYMENU,
129                                    &mut v4l2_menu as *mut _ as *mut std::os::raw::c_void,
130                                );
131
132                                // BEWARE OF DRAGONS!
133                                // The API docs [1] state VIDIOC_QUERYMENU should may return EINVAL
134                                // for some indices between minimum and maximum when an item is not
135                                // supported by a driver.
136                                //
137                                // I have no idea why it is advertised in the first place then, but
138                                // have seen this happen with a Logitech C920 HD Pro webcam.
139                                // In case of errors, let's just skip the offending index.
140                                //
141                                // [1] https://github.com/torvalds/linux/blob/master/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst#description
142                                if res.is_err() {
143                                    continue;
144                                }
145
146                                let item =
147                                    control::MenuItem::try_from((control.typ, v4l2_menu)).unwrap();
148                                items.push((v4l2_menu.index, item));
149                            }
150
151                            control.items = Some(items);
152                        }
153
154                        controls.push(control);
155                    }
156                    Err(e) => {
157                        if controls.is_empty() || e.kind() != io::ErrorKind::InvalidInput {
158                            return Err(e);
159                        } else {
160                            break;
161                        }
162                    }
163                }
164            }
165        }
166
167        Ok(controls)
168    }
169
170    /// Returns the control value for an ID
171    ///
172    /// # Arguments
173    ///
174    /// * `id` - Control identifier
175    pub fn control(&self, id: u32) -> io::Result<Control> {
176        unsafe {
177            let mut queryctrl = v4l2_query_ext_ctrl {
178                id,
179                ..mem::zeroed()
180            };
181            v4l2::ioctl(
182                self.handle().fd(),
183                v4l2::vidioc::VIDIOC_QUERY_EXT_CTRL,
184                &mut queryctrl as *mut _ as *mut std::os::raw::c_void,
185            )?;
186
187            // determine the control type
188            let description = control::Description::from(queryctrl);
189
190            // query the actual control value
191            let mut v4l2_ctrl = v4l2_ext_control {
192                id,
193                ..mem::zeroed()
194            };
195            let mut v4l2_ctrls = v4l2_ext_controls {
196                count: 1,
197                controls: &mut v4l2_ctrl,
198                ..mem::zeroed()
199            };
200            v4l2::ioctl(
201                self.handle().fd(),
202                v4l2::vidioc::VIDIOC_G_EXT_CTRLS,
203                &mut v4l2_ctrls as *mut _ as *mut std::os::raw::c_void,
204            )?;
205
206            let value = match description.typ {
207                control::Type::Integer64 => {
208                    control::Value::Integer(v4l2_ctrl.__bindgen_anon_1.value64)
209                }
210                control::Type::Integer | control::Type::Menu => {
211                    control::Value::Integer(v4l2_ctrl.__bindgen_anon_1.value as i64)
212                }
213                control::Type::Boolean => {
214                    control::Value::Boolean(v4l2_ctrl.__bindgen_anon_1.value == 1)
215                }
216                _ => {
217                    return Err(io::Error::new(
218                        io::ErrorKind::Other,
219                        "cannot handle control type",
220                    ))
221                }
222            };
223
224            Ok(Control { id, value })
225        }
226    }
227
228    /// Modifies the control value
229    ///
230    /// # Arguments
231    ///
232    /// * `ctrl` - Control to be set
233    pub fn set_control(&self, ctrl: Control) -> io::Result<()> {
234        self.set_controls(vec![ctrl])
235    }
236
237    /// Modifies the control values atomically
238    ///
239    /// # Arguments
240    ///
241    /// * `ctrls` - Vec of the controls to be set
242    pub fn set_controls(&self, ctrls: Vec<Control>) -> io::Result<()> {
243        unsafe {
244            let mut control_list: Vec<v4l2_ext_control> = vec![];
245            let mut class: Option<u32> = None;
246
247            if ctrls.is_empty() {
248                return Err(io::Error::new(
249                    io::ErrorKind::InvalidInput,
250                    "ctrls cannot be empty",
251                ));
252            }
253
254            for ref ctrl in ctrls {
255                let mut control = v4l2_ext_control {
256                    id: ctrl.id,
257                    ..mem::zeroed()
258                };
259                class = match class {
260                    Some(c) => {
261                        if c != (control.id & 0xFFFF0000) {
262                            return Err(io::Error::new(
263                                io::ErrorKind::InvalidInput,
264                                "All controls must be in the same class",
265                            ));
266                        } else {
267                            Some(c)
268                        }
269                    }
270                    None => Some(control.id & 0xFFFF0000),
271                };
272
273                match ctrl.value {
274                    control::Value::None => {}
275                    control::Value::Integer(val) => {
276                        control.__bindgen_anon_1.value64 = val;
277                        control.size = std::mem::size_of::<i64>() as u32;
278                    }
279                    control::Value::Boolean(val) => {
280                        control.__bindgen_anon_1.value64 = val as i64;
281                        control.size = std::mem::size_of::<i64>() as u32;
282                    }
283                    control::Value::String(ref val) => {
284                        control.__bindgen_anon_1.string = val.as_ptr() as *mut std::os::raw::c_char;
285                        control.size = val.len() as u32;
286                    }
287                    control::Value::CompoundU8(ref val) => {
288                        control.__bindgen_anon_1.p_u8 = val.as_ptr() as *mut u8;
289                        control.size = (val.len() * std::mem::size_of::<u8>()) as u32;
290                    }
291                    control::Value::CompoundU16(ref val) => {
292                        control.__bindgen_anon_1.p_u16 = val.as_ptr() as *mut u16;
293                        control.size = (val.len() * std::mem::size_of::<u16>()) as u32;
294                    }
295                    control::Value::CompoundU32(ref val) => {
296                        control.__bindgen_anon_1.p_u32 = val.as_ptr() as *mut u32;
297                        control.size = (val.len() * std::mem::size_of::<u32>()) as u32;
298                    }
299                    control::Value::CompoundPtr(ref val) => {
300                        control.__bindgen_anon_1.ptr = val.as_ptr() as *mut std::os::raw::c_void;
301                        control.size = (val.len() * std::mem::size_of::<u8>()) as u32;
302                    }
303                };
304
305                control_list.push(control);
306            }
307
308            let class = class.ok_or_else(|| {
309                io::Error::new(
310                    io::ErrorKind::InvalidInput,
311                    "failed to determine control class",
312                )
313            })?;
314
315            let mut controls = v4l2_ext_controls {
316                count: control_list.len() as u32,
317                controls: control_list.as_mut_ptr(),
318
319                which: class,
320                ..mem::zeroed()
321            };
322
323            v4l2::ioctl(
324                self.handle().fd(),
325                v4l2::vidioc::VIDIOC_S_EXT_CTRLS,
326                &mut controls as *mut _ as *mut std::os::raw::c_void,
327            )
328        }
329    }
330}
331
332impl io::Read for Device {
333    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
334        unsafe {
335            let ret = libc::read(
336                self.handle().fd(),
337                buf.as_mut_ptr() as *mut std::os::raw::c_void,
338                buf.len(),
339            );
340            match ret {
341                -1 => Err(io::Error::last_os_error()),
342                ret => Ok(ret as usize),
343            }
344        }
345    }
346}
347
348impl io::Write for Device {
349    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
350        unsafe {
351            let ret = libc::write(
352                self.handle().fd(),
353                buf.as_ptr() as *const std::os::raw::c_void,
354                buf.len(),
355            );
356
357            match ret {
358                -1 => Err(io::Error::last_os_error()),
359                ret => Ok(ret as usize),
360            }
361        }
362    }
363
364    fn flush(&mut self) -> io::Result<()> {
365        // write doesn't use a buffer, so it effectively flushes with each call
366        // therefore, we don't have anything to flush later
367        Ok(())
368    }
369}
370
371/// Device handle for low-level access.
372///
373/// Acquiring a handle facilitates (possibly mutating) interactions with the device.
374pub struct Handle {
375    fd: std::os::raw::c_int,
376}
377
378impl Handle {
379    fn new(fd: std::os::raw::c_int) -> Self {
380        Self { fd }
381    }
382
383    /// Returns the raw file descriptor
384    pub fn fd(&self) -> std::os::raw::c_int {
385        self.fd
386    }
387
388    /// Polls the file descriptor for I/O events
389    ///
390    /// # Arguments
391    ///
392    /// * `events`  - The events you are interested in (e.g. POLLIN)
393    ///
394    /// * `timeout` - Timeout in milliseconds
395    ///               A value of zero returns immedately, even if the fd is not ready.
396    ///               A negative value means infinite timeout (blocking).
397    pub fn poll(&self, events: i16, timeout: i32) -> io::Result<i32> {
398        match unsafe {
399            libc::poll(
400                [libc::pollfd {
401                    fd: self.fd,
402                    events,
403                    revents: 0,
404                }]
405                .as_mut_ptr(),
406                1,
407                timeout,
408            )
409        } {
410            -1 => Err(io::Error::last_os_error()),
411            ret => {
412                // A return value of zero means that we timed out. A positive value signifies the
413                // number of fds with non-zero revents fields (aka I/O activity).
414                assert!(ret == 0 || ret == 1);
415                Ok(ret)
416            }
417        }
418    }
419}
420
421impl Drop for Handle {
422    fn drop(&mut self) {
423        v4l2::close(self.fd).unwrap();
424    }
425}