linuxvideo/
lib.rs

1//! Linux video device library.
2//!
3//! This library provides a (hopefully) convenient and high-level wrapper around the V4L2 ioctls,
4//! and allows accessing video devices (capture cards, webcams, etc.) on Linux systems.
5//!
6//! The main entry points to the library are [`list`], for enumerating all V4L2 devices (and opening
7//! one of them by name), and [`Device::open`], for opening a specific path.
8
9#[macro_use]
10mod macros;
11mod buf_type;
12pub mod controls;
13pub mod format;
14mod pixel_format;
15mod raw;
16mod shared;
17pub mod stream;
18pub mod uvc;
19
20use pixel_format::PixelFormat;
21use std::{
22    fmt,
23    fs::{self, File, OpenOptions},
24    io::{self, Read, Write},
25    mem::{self, MaybeUninit},
26    os::unix::prelude::*,
27    path::{Path, PathBuf},
28};
29
30use controls::{ControlDesc, ControlIter, TextMenuIter};
31use format::{Format, FormatDescIter, FrameIntervals, FrameSizes, MetaFormat, PixFormat};
32use raw::controls::Cid;
33use shared::{CaptureParamFlags, Memory, StreamParamCaps};
34use stream::{ReadStream, WriteStream, DEFAULT_BUFFER_COUNT};
35
36pub use buf_type::*;
37pub use shared::{
38    AnalogStd, CapabilityFlags, Fract, InputCapabilities, InputStatus, InputType,
39    OutputCapabilities, OutputType,
40};
41
42const DEVICE_PREFIXES: &[&str] = &[
43    "video",
44    "vbi",
45    "radio",
46    "swradio",
47    "v4l-touch",
48    "v4l-subdev",
49];
50
51/// Returns an iterator over all connected V4L2 devices.
52///
53/// This will enumerate all devices in `/dev` that match one of the V4L2 device file patterns:
54///
55/// - **`/dev/video*`**: webcams, capture cards, video output devices, codecs, video overlays, etc.
56/// - **`/dev/vbi*`**: devices for capturing and outputting raw **V**ertical **B**lanking **I**nterval data.
57/// - **`/dev/radio*`**: AM and FM radio transmitters and receivers.
58/// - **`/dev/swradio*`**: Software-defined radios.
59/// - **`/dev/v4l-touch*`**: Touch screens and other touch devices.
60/// - **`/dev/v4l-subdev*`**: A sub-device exported as part of a bigger device.
61pub fn list() -> io::Result<impl Iterator<Item = io::Result<Device>>> {
62    Ok(fs::read_dir("/dev")?.flat_map(|file| {
63        let file = match file {
64            Ok(file) => file,
65            Err(e) => return Some(Err(e.into())),
66        };
67
68        let name = file.file_name();
69        if !DEVICE_PREFIXES
70            .iter()
71            .any(|p| name.as_bytes().starts_with(p.as_bytes()))
72        {
73            // Doesn't match any V4L2 device patterns.
74            return None;
75        }
76
77        // Sanity check that it's a char device.
78        match file.file_type() {
79            Ok(ty) => {
80                if !ty.is_char_device() {
81                    log::warn!(
82                        "'{}' is not a character device: {:?}",
83                        name.to_string_lossy(),
84                        ty,
85                    );
86                    return None;
87                }
88            }
89            Err(e) => return Some(Err(e.into())),
90        }
91
92        Some(Device::open(&file.path()))
93    }))
94}
95
96/// A V4L2 device.
97#[derive(Debug)]
98pub struct Device {
99    file: File,
100    available_capabilities: CapabilityFlags,
101}
102
103impl Device {
104    /// Opens a V4L2 device file from the given path.
105    ///
106    /// If the path does not refer to a V4L2 device node, an error will be returned.
107    pub fn open<A: AsRef<Path>>(path: A) -> io::Result<Self> {
108        Self::open_impl(path.as_ref())
109    }
110
111    fn open_impl(path: &Path) -> io::Result<Self> {
112        let file = OpenOptions::new().read(true).write(true).open(path)?;
113        let mut this = Self {
114            file,
115            available_capabilities: CapabilityFlags::empty(),
116        };
117        let caps = this.capabilities()?;
118        this.available_capabilities = caps.device_capabilities();
119
120        Ok(this)
121    }
122
123    fn fd(&self) -> RawFd {
124        self.file.as_raw_fd()
125    }
126
127    /// Returns the path to the V4L2 device.
128    ///
129    /// This will invoke `readlink(2)` on `/proc/self/fd/N` to find the path.
130    pub fn path(&self) -> io::Result<PathBuf> {
131        fs::read_link(format!("/proc/self/fd/{}", self.fd()))
132    }
133
134    /// Queries the device's [`Capabilities`].
135    pub fn capabilities(&self) -> io::Result<Capabilities> {
136        unsafe {
137            let mut caps = MaybeUninit::uninit();
138            let res = raw::VIDIOC_QUERYCAP.ioctl(self, caps.as_mut_ptr())?;
139            assert_eq!(res, 0);
140            Ok(Capabilities(caps.assume_init()))
141        }
142    }
143
144    pub fn supported_buf_types(&self) -> BufTypes {
145        BufTypes::from_capabilities(self.available_capabilities)
146    }
147
148    /// Enumerates the supported pixel formats of a stream.
149    ///
150    /// `buf_type` must be one of `VIDEO_CAPTURE`, `VIDEO_CAPTURE_MPLANE`, `VIDEO_OUTPUT`,
151    /// `VIDEO_OUTPUT_MPLANE`, `VIDEO_OVERLAY`, `SDR_CAPTURE`, `SDR_OUTPUT`, `META_CAPTURE`, or
152    /// `META_OUTPUT`.
153    pub fn formats(&self, buf_type: BufType) -> FormatDescIter<'_> {
154        FormatDescIter::new(self, buf_type)
155    }
156
157    /// Returns the supported frame sizes for a given pixel format.
158    ///
159    /// # Errors
160    ///
161    /// An `ENOTTY` error will be returned if `pixel_format` specifies a format that does not
162    /// describe video data (for example, [`PixelFormat::UVC`] or other metadata formats).
163    pub fn frame_sizes(&self, pixel_format: PixelFormat) -> io::Result<FrameSizes> {
164        FrameSizes::new(self, pixel_format)
165    }
166
167    pub fn frame_intervals(
168        &self,
169        pixel_format: PixelFormat,
170        width: u32,
171        height: u32,
172    ) -> io::Result<FrameIntervals> {
173        FrameIntervals::new(self, pixel_format, width, height)
174    }
175
176    /// Returns an iterator over the [`Input`]s of the device.
177    ///
178    /// # Errors
179    ///
180    /// May return `ENOTTY` if the device is not an input device.
181    pub fn inputs(&self) -> InputIter<'_> {
182        InputIter {
183            device: self,
184            next_index: 0,
185            finished: false,
186        }
187    }
188
189    /// Returns an iterator over the [`Output`]s of the device.
190    ///
191    /// # Errors
192    ///
193    /// May return `ENOTTY` if the device is not an output device.
194    pub fn outputs(&self) -> OutputIter<'_> {
195        OutputIter {
196            device: self,
197            next_index: 0,
198            finished: false,
199        }
200    }
201
202    pub fn controls(&self) -> ControlIter<'_> {
203        ControlIter::new(self)
204    }
205
206    /// Returns an iterator over the valid values of a menu control.
207    pub fn enumerate_menu(&self, ctrl: &ControlDesc) -> TextMenuIter<'_> {
208        TextMenuIter::new(self, ctrl)
209    }
210
211    pub fn read_control_raw(&self, cid: Cid) -> io::Result<i32> {
212        let mut control = raw::controls::Control { id: cid, value: 0 };
213
214        unsafe {
215            raw::VIDIOC_G_CTRL.ioctl(self, &mut control)?;
216        }
217
218        Ok(control.value)
219    }
220
221    pub fn write_control_raw(&mut self, cid: Cid, value: i32) -> io::Result<()> {
222        let mut control = raw::controls::Control { id: cid, value };
223        unsafe {
224            raw::VIDIOC_S_CTRL.ioctl(self, &mut control)?;
225        }
226        Ok(())
227    }
228
229    /// Reads the stream format in use by `buf_type`.
230    ///
231    /// The returned [`Format`] variant will match `buf_type`.
232    ///
233    /// If no format is set, this returns `EINVAL`.
234    ///
235    /// # Panics
236    ///
237    /// This will panic if `buf_type` corresponds to a buffer type that hasn't yet been implemented
238    /// in [`Format`].
239    pub fn format(&self, buf_type: BufType) -> io::Result<Format> {
240        unsafe {
241            let mut format = raw::Format {
242                type_: buf_type,
243                ..mem::zeroed()
244            };
245            raw::VIDIOC_G_FMT.ioctl(self, &mut format)?;
246            let fmt = Format::from_raw(format)
247                .unwrap_or_else(|| todo!("unsupported buffer type {:?}", buf_type));
248            Ok(fmt)
249        }
250    }
251
252    /// Negotiates a stream's format.
253    ///
254    /// The driver will adjust the values in `format` to the closest values it supports (the variant
255    /// will not be changed). The modified `Format` is returned.
256    fn set_format_raw(&mut self, format: Format) -> io::Result<Format> {
257        unsafe {
258            let mut raw_format: raw::Format = mem::zeroed();
259            match format {
260                Format::VideoCapture(f) => {
261                    raw_format.type_ = BufType::VIDEO_CAPTURE;
262                    raw_format.fmt.pix = f.to_raw();
263                }
264                Format::VideoOutput(f) => {
265                    raw_format.type_ = BufType::VIDEO_OUTPUT;
266                    raw_format.fmt.pix = f.to_raw();
267                }
268                Format::VideoCaptureMplane(f) => {
269                    raw_format.type_ = BufType::VIDEO_CAPTURE_MPLANE;
270                    raw_format.fmt.pix_mp = f.to_raw();
271                }
272                Format::VideoOutputMplane(f) => {
273                    raw_format.type_ = BufType::VIDEO_OUTPUT_MPLANE;
274                    raw_format.fmt.pix_mp = f.to_raw();
275                }
276                Format::VideoOverlay(f) => {
277                    raw_format.type_ = BufType::VIDEO_OVERLAY;
278                    raw_format.fmt.win = f.to_raw();
279                }
280                Format::MetaCapture(f) => {
281                    raw_format.type_ = BufType::META_CAPTURE;
282                    raw_format.fmt.meta = f.to_raw();
283                }
284                Format::MetaOutput(f) => {
285                    raw_format.type_ = BufType::META_OUTPUT;
286                    raw_format.fmt.meta = f.to_raw();
287                }
288            }
289            raw::VIDIOC_S_FMT.ioctl(self, &mut raw_format)?;
290            let fmt = Format::from_raw(raw_format).unwrap();
291            Ok(fmt)
292        }
293    }
294
295    /// Puts the device into video capture mode and negotiates a pixel format.
296    ///
297    /// # Format Negotiation
298    ///
299    /// Generally, the driver is allowed to change most properties of the [`PixFormat`], including
300    /// the requested dimensions and the [`PixelFormat`], if the provided value is not supported.
301    /// However, it is not required to do so and may instead return `EINVAL` if the parameters are
302    /// not supported. One example where this happens is with `v4l2loopback`.
303    pub fn video_capture(mut self, format: PixFormat) -> io::Result<VideoCaptureDevice> {
304        let format = match self.set_format_raw(Format::VideoCapture(format))? {
305            Format::VideoCapture(fmt) => fmt,
306            _ => unreachable!(),
307        };
308
309        Ok(VideoCaptureDevice {
310            file: self.file,
311            format,
312        })
313    }
314
315    /// Puts the device into video output mode and negotiates a pixel format.
316    ///
317    /// # Format Negotiation
318    ///
319    /// Generally, the driver is allowed to change most properties of the [`PixFormat`], including
320    /// the requested dimensions and the [`PixelFormat`], if the provided value is not supported.
321    /// However, it is not required to do so and may instead return `EINVAL` if the parameters are
322    /// not supported. One example where this happens is with `v4l2loopback`.
323    pub fn video_output(mut self, format: PixFormat) -> io::Result<VideoOutputDevice> {
324        let format = match self.set_format_raw(Format::VideoOutput(format))? {
325            Format::VideoOutput(fmt) => fmt,
326            _ => unreachable!(),
327        };
328
329        Ok(VideoOutputDevice {
330            file: self.file,
331            format,
332        })
333    }
334
335    /// Puts the device into metadata capture mode and negotiates a data format.
336    pub fn meta_capture(mut self, format: MetaFormat) -> io::Result<MetaCaptureDevice> {
337        let format = match self.set_format_raw(Format::MetaCapture(format))? {
338            Format::MetaCapture(fmt) => fmt,
339            _ => unreachable!(),
340        };
341
342        Ok(MetaCaptureDevice {
343            file: self.file,
344            format,
345        })
346    }
347}
348
349impl AsRawFd for Device {
350    #[inline]
351    fn as_raw_fd(&self) -> RawFd {
352        self.file.as_raw_fd()
353    }
354}
355
356impl AsFd for Device {
357    #[inline]
358    fn as_fd(&self) -> BorrowedFd<'_> {
359        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
360    }
361}
362
363/// A video device configured for video capture.
364///
365/// Returned by [`Device::video_capture`].
366pub struct VideoCaptureDevice {
367    file: File,
368    format: PixFormat,
369}
370
371impl VideoCaptureDevice {
372    /// Returns the pixel format the driver chose for capturing.
373    ///
374    /// This may (and usually will) differ from the format passed to [`Device::video_capture`].
375    pub fn format(&self) -> &PixFormat {
376        &self.format
377    }
378
379    /// Requests a change to the frame interval.
380    ///
381    /// Returns the actual frame interval chosen by the driver.
382    ///
383    /// Supported frame intervals depend on the pixel format and video resolution and can be
384    /// enumerated with [`Device::frame_intervals`].
385    pub fn set_frame_interval(&self, interval: Fract) -> io::Result<Fract> {
386        unsafe {
387            let mut parm = raw::StreamParm {
388                type_: BufType::VIDEO_CAPTURE,
389                union: raw::StreamParmUnion {
390                    capture: raw::CaptureParm {
391                        timeperframe: interval,
392                        capability: StreamParamCaps::TIMEPERFRAME,
393                        capturemode: CaptureParamFlags::empty(),
394                        extendedmode: 0,
395                        readbuffers: 0,
396                        reserved: [0; 4],
397                    },
398                },
399            };
400            raw::VIDIOC_S_PARM.ioctl(self, &mut parm)?;
401            Ok(parm.union.capture.timeperframe)
402        }
403    }
404
405    /// Initializes streaming I/O mode.
406    pub fn into_stream(self) -> io::Result<ReadStream> {
407        Ok(ReadStream::new(
408            self.file,
409            BufType::VIDEO_CAPTURE,
410            Memory::MMAP,
411            DEFAULT_BUFFER_COUNT,
412        )?)
413    }
414}
415
416/// Performs a direct `read()` from the video device.
417///
418/// This will only succeed if the device advertises the `READWRITE` capability, otherwise an
419/// error will be returned and you have to use the streaming API instead.
420impl Read for VideoCaptureDevice {
421    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
422        self.file.read(buf)
423    }
424}
425
426impl AsRawFd for VideoCaptureDevice {
427    #[inline]
428    fn as_raw_fd(&self) -> RawFd {
429        self.file.as_raw_fd()
430    }
431}
432
433impl AsFd for VideoCaptureDevice {
434    #[inline]
435    fn as_fd(&self) -> BorrowedFd<'_> {
436        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
437    }
438}
439
440/// A video device configured for video output.
441///
442/// Returned by [`Device::video_output`].
443pub struct VideoOutputDevice {
444    file: File,
445    format: PixFormat,
446}
447
448impl VideoOutputDevice {
449    /// Returns the video format chosen by the driver.
450    pub fn format(&self) -> &PixFormat {
451        &self.format
452    }
453
454    /// Initializes streaming I/O mode.
455    pub fn into_stream(self) -> io::Result<WriteStream> {
456        Ok(WriteStream::new(
457            self.file,
458            BufType::VIDEO_CAPTURE,
459            Memory::MMAP,
460            DEFAULT_BUFFER_COUNT,
461        )?)
462    }
463}
464
465/// Performs a direct `write()` on the video device file, writing a video frame to it.
466///
467/// This will only succeed if the device advertises the `READWRITE` capability, otherwise an
468/// error will be returned and you have to use the streaming API instead.
469///
470/// Note that some applications, like guvcview, do not support the read/write methods, so using this
471/// on a v4l2loopback device will not work with such applications.
472impl Write for VideoOutputDevice {
473    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
474        self.file.write(buf)
475    }
476
477    fn flush(&mut self) -> io::Result<()> {
478        self.file.flush()
479    }
480}
481
482impl AsRawFd for VideoOutputDevice {
483    #[inline]
484    fn as_raw_fd(&self) -> RawFd {
485        self.file.as_raw_fd()
486    }
487}
488
489impl AsFd for VideoOutputDevice {
490    #[inline]
491    fn as_fd(&self) -> BorrowedFd<'_> {
492        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
493    }
494}
495
496/// A device configured for metadata capture.
497///
498/// Returned by [`Device::meta_capture`].
499pub struct MetaCaptureDevice {
500    file: File,
501    format: MetaFormat,
502}
503
504impl MetaCaptureDevice {
505    /// Returns the metadata format the driver chose.
506    pub fn format(&self) -> &MetaFormat {
507        &self.format
508    }
509
510    /// Initializes streaming I/O mode.
511    pub fn into_stream(self) -> io::Result<ReadStream> {
512        Ok(ReadStream::new(
513            self.file,
514            BufType::META_CAPTURE,
515            Memory::MMAP,
516            DEFAULT_BUFFER_COUNT,
517        )?)
518    }
519}
520
521/// Performs a direct `read()` from the video device.
522///
523/// This will only succeed if the device advertises the `READWRITE` capability, otherwise an
524/// error will be returned and you have to use the streaming API instead.
525impl Read for MetaCaptureDevice {
526    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
527        self.file.read(buf)
528    }
529}
530
531impl AsRawFd for MetaCaptureDevice {
532    #[inline]
533    fn as_raw_fd(&self) -> RawFd {
534        self.file.as_raw_fd()
535    }
536}
537
538impl AsFd for MetaCaptureDevice {
539    #[inline]
540    fn as_fd(&self) -> BorrowedFd<'_> {
541        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
542    }
543}
544
545/// Stores generic device information.
546///
547/// Returned by [`Device::capabilities`].
548pub struct Capabilities(raw::Capabilities);
549
550impl Capabilities {
551    /// Returns the identifier of the V4L2 driver that provides this device.
552    ///
553    /// Examples:
554    /// - `uvcvideo`
555    /// - `v4l2 loopback`
556    pub fn driver(&self) -> &str {
557        byte_array_to_str(&self.0.driver)
558    }
559
560    /// Returns the card or device name.
561    ///
562    /// For `v4l2loopback` devices, the reported card name can be configured by passing the
563    /// `card_label` parameter when loading the module (or via `modprobe.d`).
564    pub fn card(&self) -> &str {
565        byte_array_to_str(&self.0.card)
566    }
567
568    /// Returns a description of where on the system the device is attached.
569    ///
570    /// Examples:
571    /// - `usb-0000:0a:00.3-2.1`
572    /// - `platform:v4l2loopback-002`
573    pub fn bus_info(&self) -> &str {
574        byte_array_to_str(&self.0.bus_info)
575    }
576
577    /// Returns all capabilities the underlying hardware device exposes.
578    ///
579    /// Some capabilities might be inaccessible through the opened device node and require opening a
580    /// different one.
581    pub fn all_capabilities(&self) -> CapabilityFlags {
582        self.0.capabilities
583    }
584
585    /// Returns the capabilities available through the currently opened device node.
586    pub fn device_capabilities(&self) -> CapabilityFlags {
587        if self.0.capabilities.contains(CapabilityFlags::DEVICE_CAPS) {
588            self.0.device_caps
589        } else {
590            self.0.capabilities
591        }
592    }
593}
594
595impl fmt::Debug for Capabilities {
596    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597        f.debug_struct("Capabilities")
598            .field("driver", &self.driver())
599            .field("card", &self.card())
600            .field("bus_info", &self.bus_info())
601            .field("capabilities", &self.0.capabilities)
602            .field("device_caps", &self.0.device_caps)
603            .finish()
604    }
605}
606
607/// Iterator over the [`Output`]s of a [`Device`].
608pub struct OutputIter<'a> {
609    device: &'a Device,
610    next_index: u32,
611    finished: bool,
612}
613
614impl Iterator for OutputIter<'_> {
615    type Item = io::Result<Output>;
616
617    fn next(&mut self) -> Option<Self::Item> {
618        if self.finished {
619            return None;
620        }
621
622        unsafe {
623            let mut raw = raw::Output {
624                index: self.next_index,
625                ..mem::zeroed()
626            };
627            match raw::VIDIOC_ENUMOUTPUT.ioctl(self.device, &mut raw) {
628                Ok(_) => {}
629                Err(e) => {
630                    self.finished = true;
631                    if e.raw_os_error() == Some(libc::EINVAL as _) {
632                        // `EINVAL` indicates the end of the list.
633                        return None;
634                    } else {
635                        return Some(Err(e));
636                    }
637                }
638            }
639
640            self.next_index += 1;
641
642            Some(Ok(Output(raw)))
643        }
644    }
645}
646
647/// Iterator over the [`Input`]s of a [`Device`].
648pub struct InputIter<'a> {
649    device: &'a Device,
650    next_index: u32,
651    finished: bool,
652}
653
654impl Iterator for InputIter<'_> {
655    type Item = io::Result<Input>;
656
657    fn next(&mut self) -> Option<Self::Item> {
658        if self.finished {
659            return None;
660        }
661
662        unsafe {
663            let mut raw = raw::Input {
664                index: self.next_index,
665                ..mem::zeroed()
666            };
667            match raw::VIDIOC_ENUMINPUT.ioctl(self.device, &mut raw) {
668                Ok(_) => {}
669                Err(e) => {
670                    self.finished = true;
671                    if e.raw_os_error() == Some(libc::EINVAL as _) {
672                        // `EINVAL` indicates the end of the list.
673                        return None;
674                    } else {
675                        return Some(Err(e));
676                    }
677                }
678            }
679
680            self.next_index += 1;
681
682            Some(Ok(Input(raw)))
683        }
684    }
685}
686
687/// Information about a device output.
688pub struct Output(raw::Output);
689
690impl Output {
691    /// Returns the output's name.
692    ///
693    /// Examples:
694    /// - `loopback in`
695    pub fn name(&self) -> &str {
696        byte_array_to_str(&self.0.name)
697    }
698
699    /// Returns what kind of device this output is.
700    #[inline]
701    pub fn output_type(&self) -> OutputType {
702        self.0.type_
703    }
704
705    /// Returns the set of selectable audio sources when this output is active.
706    ///
707    /// This may return 0 even if the device supports audio inputs to indicate that the application
708    /// cannot choose an audio input.
709    #[inline]
710    pub fn audioset(&self) -> u32 {
711        self.0.audioset
712    }
713
714    /// Returns the modulator index if this input is of type [`OutputType::MODULATOR`].
715    ///
716    /// For non-modulator outputs, this value should be ignored.
717    #[inline]
718    pub fn modulator(&self) -> u32 {
719        self.0.modulator
720    }
721
722    /// Returns the set of supported analog video standards.
723    #[inline]
724    pub fn std(&self) -> AnalogStd {
725        self.0.std
726    }
727
728    /// Returns the capability flags of this output.
729    #[inline]
730    pub fn capabilities(&self) -> OutputCapabilities {
731        self.0.capabilities
732    }
733}
734
735impl fmt::Debug for Output {
736    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
737        f.debug_struct("Output")
738            .field("index", &self.0.index)
739            .field("name", &self.name())
740            .field("output_type", &self.output_type())
741            .field("audioset", &self.0.audioset)
742            .field("modulator", &self.0.modulator)
743            .field("std", &self.0.std)
744            .field("capabilities", &self.0.capabilities)
745            .finish()
746    }
747}
748
749/// Information about a device input.
750pub struct Input(raw::Input);
751
752impl Input {
753    /// Returns the name of the input.
754    ///
755    /// Examples:
756    /// - `Camera 1`
757    /// - `loopback`
758    pub fn name(&self) -> &str {
759        byte_array_to_str(&self.0.name)
760    }
761
762    /// Returns what kind of device this input is.
763    #[inline]
764    pub fn input_type(&self) -> InputType {
765        self.0.type_
766    }
767
768    /// Returns the set of selectable audio sources when this input is active.
769    ///
770    /// This may return 0 even if the device supports audio inputs to indicate that the application
771    /// cannot choose an audio input.
772    #[inline]
773    pub fn audioset(&self) -> u32 {
774        self.0.audioset
775    }
776
777    /// Returns the tuner index if this input is of type [`InputType::TUNER`].
778    ///
779    /// For non-tuner inputs, this value should be ignored.
780    #[inline]
781    pub fn tuner(&self) -> u32 {
782        self.0.tuner
783    }
784
785    /// Returns the set of supported analog video standards for this input.
786    #[inline]
787    pub fn std(&self) -> AnalogStd {
788        self.0.std
789    }
790
791    /// Returns the current status of the input.
792    ///
793    /// Note that the input needs to be selected as the active input for most fields in this value
794    /// to be valid.
795    #[inline]
796    pub fn status(&self) -> InputStatus {
797        self.0.status
798    }
799
800    /// Returns the capability flags of this input.
801    #[inline]
802    pub fn capabilities(&self) -> InputCapabilities {
803        self.0.capabilities
804    }
805}
806
807impl fmt::Debug for Input {
808    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
809        f.debug_struct("Input")
810            .field("index", &self.0.index)
811            .field("name", &self.name())
812            .field("input_type", &self.input_type())
813            .field("audioset", &self.0.audioset)
814            .field("tuner", &self.0.tuner)
815            .field("std", &self.0.std)
816            .field("status", &self.0.status)
817            .field("capabilities", &self.0.capabilities)
818            .finish()
819    }
820}
821
822/// Turns a zero-padded byte array containing UTF-8 or ASCII data into a `&str`.
823fn byte_array_to_str(bytes: &[u8]) -> &str {
824    let len = bytes
825        .iter()
826        .position(|b| *b == 0)
827        .expect("missing NUL terminator");
828    std::str::from_utf8(&bytes[..len]).unwrap()
829}