blob: fb2c84d2b1697fe674cb1dd88358e4fc25640473 [file] [log] [blame]
use super::{is_multi_planar, BufferFlags, PlaneData};
use crate::bindings;
use crate::QueueType;
use nix::{self, errno::Errno, Error};
use std::fmt::Debug;
use std::mem;
use std::os::unix::io::AsRawFd;
use thiserror::Error;
/// Implementors can receive the result from the `dqbuf` ioctl.
pub trait DQBuf: Sized {
/// 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 from_v4l2_buffer(v4l2_buf: &bindings::v4l2_buffer, v4l2_planes: Option<&PlaneData>) -> Self;
}
/// Allows to dequeue a buffer without caring for any of its data.
impl DQBuf for () {
fn from_v4l2_buffer(
_v4l2_buf: &bindings::v4l2_buffer,
_v4l2_planes: Option<&PlaneData>,
) -> Self {
}
}
/// Useful for the case where we are only interested in the index of a dequeued
/// buffer
impl DQBuf for u32 {
fn from_v4l2_buffer(
v4l2_buf: &bindings::v4l2_buffer,
_v4l2_planes: Option<&PlaneData>,
) -> Self {
v4l2_buf.index
}
}
#[derive(Debug)]
pub struct DQBufPlane {
pub length: u32,
pub bytesused: u32,
pub data_offset: u32,
}
/// Contains all the information from a dequeued buffer. Safe variant of
/// `struct v4l2_buffer`.
#[derive(Debug, Default)]
pub struct DQBuffer {
pub index: u32,
pub flags: BufferFlags,
pub field: u32,
pub sequence: u32,
pub planes: Vec<DQBufPlane>,
}
impl DQBuf for DQBuffer {
fn from_v4l2_buffer(v4l2_buf: &bindings::v4l2_buffer, v4l2_planes: Option<&PlaneData>) -> Self {
let planes = match v4l2_planes {
None => vec![DQBufPlane {
length: v4l2_buf.length,
bytesused: v4l2_buf.bytesused,
data_offset: 0,
}],
Some(v4l2_planes) => v4l2_planes
.iter()
.take(v4l2_buf.length as usize)
.map(|v4l2_plane| DQBufPlane {
length: v4l2_plane.length,
bytesused: v4l2_plane.bytesused,
data_offset: v4l2_plane.data_offset,
})
.collect(),
};
DQBuffer {
index: v4l2_buf.index as u32,
flags: BufferFlags::from_bits_truncate(v4l2_buf.flags),
field: v4l2_buf.field,
sequence: v4l2_buf.sequence,
planes,
}
}
}
#[doc(hidden)]
mod ioctl {
use crate::bindings::v4l2_buffer;
nix::ioctl_readwrite!(vidioc_dqbuf, b'V', 17, v4l2_buffer);
}
#[derive(Debug, Error)]
pub enum DQBufError<T: Debug> {
#[error("End-of-stream reached")]
EOS,
#[error("No buffer ready for dequeue")]
NotReady,
#[error("Buffer with ERROR flag dequeued")]
CorruptedBuffer(T),
#[error("Unexpected ioctl error: {0}")]
IoctlError(Error),
}
impl<T: Debug> From<Error> for DQBufError<T> {
fn from(error: Error) -> Self {
match error {
Error::Sys(Errno::EAGAIN) => Self::NotReady,
Error::Sys(Errno::EPIPE) => Self::EOS,
error => Self::IoctlError(error),
}
}
}
/// Safe wrapper around the `VIDIOC_DQBUF` ioctl.
pub fn dqbuf<T: DQBuf + Debug, F: AsRawFd>(fd: &F, queue: QueueType) -> Result<T, DQBufError<T>> {
let mut v4l2_buf = bindings::v4l2_buffer {
type_: queue as u32,
..unsafe { mem::zeroed() }
};
let dequeued_buffer = if is_multi_planar(queue) {
let mut plane_data: PlaneData = Default::default();
v4l2_buf.m.planes = plane_data.as_mut_ptr();
v4l2_buf.length = plane_data.len() as u32;
unsafe { ioctl::vidioc_dqbuf(fd.as_raw_fd(), &mut v4l2_buf) }?;
T::from_v4l2_buffer(&v4l2_buf, Some(&plane_data))
} else {
unsafe { ioctl::vidioc_dqbuf(fd.as_raw_fd(), &mut v4l2_buf) }?;
T::from_v4l2_buffer(&v4l2_buf, None)
};
Ok(dequeued_buffer)
}