blob: 704dbbcd0e93b984be1febbf69474a1fccf8fdaa [file] [log] [blame]
use super::{is_multi_planar, BufferFlags, V4l2BufferPlanes};
use crate::bindings;
use crate::ioctl::V4l2Buffer;
use crate::memory::MemoryType;
use crate::QueueType;
use nix::errno::Errno;
use thiserror::Error;
use std::convert::Infallible;
use std::mem;
use std::os::unix::io::AsRawFd;
/// Implementors can receive the result from the `querybuf` ioctl.
pub trait QueryBuf: Sized {
type Error: std::fmt::Display;
/// Try to retrieve the data from `v4l2_buf`. If `v4l2_planes` is `None`,
/// then the buffer is single-planar. If it has data, the buffer is
/// multi-planar and the array of `struct v4l2_plane` shall be used to
/// retrieve the plane data.
fn try_from_v4l2_buffer(
v4l2_buf: bindings::v4l2_buffer,
v4l2_planes: Option<V4l2BufferPlanes>,
) -> Result<Self, Self::Error>;
}
/// For cases where we are not interested in the result of `qbuf`
impl QueryBuf for () {
type Error = Infallible;
fn try_from_v4l2_buffer(
_v4l2_buf: bindings::v4l2_buffer,
_v4l2_planes: Option<V4l2BufferPlanes>,
) -> Result<Self, Self::Error> {
Ok(())
}
}
#[derive(Debug, Error)]
pub enum V4l2BufferFromError {
#[error("unknown queue type {0}")]
UnknownQueueType(u32),
#[error("unknown memory type {0}")]
UnknownMemoryType(u32),
}
impl QueryBuf for V4l2Buffer {
type Error = V4l2BufferFromError;
/// Do some consistency checks to ensure methods of `V4l2Buffer` that do an `unwrap` can never
/// fail.
fn try_from_v4l2_buffer(
v4l2_buf: bindings::v4l2_buffer,
v4l2_planes: Option<V4l2BufferPlanes>,
) -> Result<Self, Self::Error> {
if QueueType::n(v4l2_buf.type_).is_none() {
return Err(V4l2BufferFromError::UnknownMemoryType(v4l2_buf.type_));
}
if MemoryType::n(v4l2_buf.memory).is_none() {
return Err(V4l2BufferFromError::UnknownQueueType(v4l2_buf.memory));
}
Ok(Self {
buffer: v4l2_buf,
planes: v4l2_planes.unwrap_or_else(|| {
let mut pdata: V4l2BufferPlanes = Default::default();
// Duplicate information for the first plane so our methods can work.
pdata[0] = bindings::v4l2_plane {
bytesused: v4l2_buf.bytesused,
length: v4l2_buf.length,
data_offset: 0,
reserved: Default::default(),
// Safe because both unions have the same members and
// layout in single-plane mode.
m: unsafe { std::mem::transmute(v4l2_buf.m) },
};
pdata
}),
})
}
}
#[derive(Debug)]
pub struct QueryBufPlane {
/// Offset to pass to `mmap()` in order to obtain a mapping for this plane.
pub mem_offset: u32,
/// Length of this plane.
pub length: u32,
}
/// Contains all the information that makes sense when using `querybuf`.
#[derive(Debug)]
pub struct QueryBuffer {
pub index: usize,
pub flags: BufferFlags,
pub planes: Vec<QueryBufPlane>,
}
impl QueryBuf for QueryBuffer {
type Error = Infallible;
fn try_from_v4l2_buffer(
v4l2_buf: bindings::v4l2_buffer,
v4l2_planes: Option<V4l2BufferPlanes>,
) -> Result<Self, Self::Error> {
let planes = match v4l2_planes {
None => vec![QueryBufPlane {
mem_offset: unsafe { v4l2_buf.m.offset },
length: v4l2_buf.length,
}],
Some(v4l2_planes) => v4l2_planes
.iter()
.take(v4l2_buf.length as usize)
.map(|v4l2_plane| QueryBufPlane {
mem_offset: unsafe { v4l2_plane.m.mem_offset },
length: v4l2_plane.length,
})
.collect(),
};
Ok(QueryBuffer {
index: v4l2_buf.index as usize,
flags: BufferFlags::from_bits_truncate(v4l2_buf.flags),
planes,
})
}
}
#[doc(hidden)]
mod ioctl {
use crate::bindings::v4l2_buffer;
nix::ioctl_readwrite!(vidioc_querybuf, b'V', 9, v4l2_buffer);
}
#[derive(Debug, Error)]
pub enum QueryBufError<Q: QueryBuf> {
#[error("error while converting from v4l2_buffer: {0}")]
ConvertionError(Q::Error),
#[error("ioctl error: {0}")]
IoctlError(#[from] Errno),
}
impl<Q: QueryBuf> From<QueryBufError<Q>> for Errno {
fn from(err: QueryBufError<Q>) -> Self {
match err {
QueryBufError::ConvertionError(_) => Errno::EINVAL,
QueryBufError::IoctlError(e) => e,
}
}
}
/// Safe wrapper around the `VIDIOC_QUERYBUF` ioctl.
pub fn querybuf<T: QueryBuf>(
fd: &impl AsRawFd,
queue: QueueType,
index: usize,
) -> Result<T, QueryBufError<T>> {
let mut v4l2_buf = bindings::v4l2_buffer {
index: index as u32,
type_: queue as u32,
..unsafe { mem::zeroed() }
};
if is_multi_planar(queue) {
let mut plane_data: V4l2BufferPlanes = Default::default();
v4l2_buf.m.planes = plane_data.as_mut_ptr();
v4l2_buf.length = plane_data.len() as u32;
unsafe { ioctl::vidioc_querybuf(fd.as_raw_fd(), &mut v4l2_buf) }?;
Ok(T::try_from_v4l2_buffer(v4l2_buf, Some(plane_data))
.map_err(QueryBufError::ConvertionError)?)
} else {
unsafe { ioctl::vidioc_querybuf(fd.as_raw_fd(), &mut v4l2_buf) }?;
Ok(T::try_from_v4l2_buffer(v4l2_buf, None).map_err(QueryBufError::ConvertionError)?)
}
}