| //! Safe wrapper for the `VIDIOC_QUERYCAP` ioctl. |
| use super::string_from_cstr; |
| use crate::bindings; |
| use bitflags::bitflags; |
| use nix::Error; |
| use std::fmt; |
| use std::mem; |
| use std::os::unix::io::AsRawFd; |
| use thiserror::Error; |
| |
| /// Implementors can receive the result from the `querycap` ioctl. |
| pub trait QueryCap { |
| fn from(capability: bindings::v4l2_capability) -> Self; |
| } |
| |
| bitflags! { |
| /// Flags returned by the `VIDIOC_QUERYCAP` ioctl into the `capabilities` |
| /// or `device_capabilities` field of `v4l2_capability`. |
| pub struct Capabilities: u32 { |
| const VIDEO_CAPTURE = bindings::V4L2_CAP_VIDEO_CAPTURE; |
| const VIDEO_OUTPUT = bindings::V4L2_CAP_VIDEO_OUTPUT; |
| const VIDEO_OVERLAY = bindings::V4L2_CAP_VIDEO_OVERLAY; |
| const VBI_CAPTURE = bindings::V4L2_CAP_VBI_CAPTURE; |
| const VBI_OUTPUT = bindings::V4L2_CAP_VBI_OUTPUT; |
| const SLICED_VBI_CAPTURE = bindings::V4L2_CAP_SLICED_VBI_CAPTURE; |
| const SLICED_VBI_OUTPUT = bindings::V4L2_CAP_SLICED_VBI_OUTPUT; |
| const RDS_CAPTURE = bindings::V4L2_CAP_RDS_CAPTURE; |
| const VIDEO_OUTPUT_OVERLAY = bindings::V4L2_CAP_VIDEO_OUTPUT_OVERLAY; |
| const HW_FREQ_SEEK = bindings::V4L2_CAP_HW_FREQ_SEEK; |
| const RDS_OUTPUT = bindings::V4L2_CAP_RDS_OUTPUT; |
| |
| const VIDEO_CAPTURE_MPLANE = bindings::V4L2_CAP_VIDEO_CAPTURE_MPLANE; |
| const VIDEO_OUTPUT_MPLANE = bindings::V4L2_CAP_VIDEO_OUTPUT_MPLANE; |
| const VIDEO_M2M_MPLANE = bindings::V4L2_CAP_VIDEO_M2M_MPLANE; |
| const VIDEO_M2M = bindings::V4L2_CAP_VIDEO_M2M; |
| |
| const TUNER = bindings::V4L2_CAP_TUNER; |
| const AUDIO = bindings::V4L2_CAP_AUDIO; |
| const RADIO = bindings::V4L2_CAP_RADIO; |
| const MODULATOR = bindings::V4L2_CAP_MODULATOR; |
| |
| const SDR_CAPTURE = bindings::V4L2_CAP_SDR_CAPTURE; |
| const EXT_PIX_FORMAT = bindings::V4L2_CAP_EXT_PIX_FORMAT; |
| const SDR_OUTPUT = bindings::V4L2_CAP_SDR_OUTPUT; |
| const META_CAPTURE = bindings::V4L2_CAP_META_CAPTURE; |
| |
| const READWRITE = bindings::V4L2_CAP_READWRITE; |
| const ASYNCIO = bindings::V4L2_CAP_ASYNCIO; |
| const STREAMING = bindings::V4L2_CAP_STREAMING; |
| const META_OUTPUT = bindings::V4L2_CAP_META_OUTPUT; |
| |
| const TOUCH = bindings::V4L2_CAP_TOUCH; |
| |
| const DEVICE_CAPS = bindings::V4L2_CAP_DEVICE_CAPS; |
| } |
| } |
| |
| impl fmt::Display for Capabilities { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| fmt::Debug::fmt(self, f) |
| } |
| } |
| |
| /// Used to get the capability flags from a `VIDIOC_QUERYCAP` ioctl. |
| impl QueryCap for Capabilities { |
| fn from(qcap: bindings::v4l2_capability) -> Self { |
| Capabilities::from_bits_truncate(qcap.capabilities) |
| } |
| } |
| |
| /// Safe variant of the `v4l2_capability` struct, to be used with `querycap`. |
| #[derive(Debug)] |
| pub struct Capability { |
| pub driver: String, |
| pub card: String, |
| pub bus_info: String, |
| pub version: u32, |
| pub capabilities: Capabilities, |
| pub device_caps: Option<Capabilities>, |
| } |
| |
| impl QueryCap for Capability { |
| fn from(qcap: bindings::v4l2_capability) -> Self { |
| Capability { |
| driver: string_from_cstr(&qcap.driver).unwrap_or_else(|_| "".into()), |
| card: string_from_cstr(&qcap.card).unwrap_or_else(|_| "".into()), |
| bus_info: string_from_cstr(&qcap.bus_info).unwrap_or_else(|_| "".into()), |
| version: qcap.version, |
| capabilities: Capabilities::from_bits_truncate(qcap.capabilities), |
| device_caps: if qcap.capabilities & bindings::V4L2_CAP_DEVICE_CAPS != 0 { |
| Some(Capabilities::from_bits_truncate(qcap.device_caps)) |
| } else { |
| None |
| }, |
| } |
| } |
| } |
| |
| #[doc(hidden)] |
| mod ioctl { |
| use crate::bindings::v4l2_capability; |
| nix::ioctl_read!(vidioc_querycap, b'V', 0, v4l2_capability); |
| } |
| |
| #[derive(Debug, Error)] |
| pub enum QueryCapError { |
| #[error("Unexpected ioctl error: {0}")] |
| IoctlError(Error), |
| } |
| |
| /// Safe wrapper around the `VIDIOC_QUERYCAP` ioctl. |
| pub fn querycap<T: QueryCap>(fd: &impl AsRawFd) -> Result<T, QueryCapError> { |
| let mut qcap: bindings::v4l2_capability = unsafe { mem::zeroed() }; |
| |
| match unsafe { ioctl::vidioc_querycap(fd.as_raw_fd(), &mut qcap) } { |
| Ok(_) => Ok(T::from(qcap)), |
| Err(e) => Err(QueryCapError::IoctlError(e)), |
| } |
| } |