blob: 008febfd087725f332db3f586211cce84c9c7bd3 [file] [log] [blame]
//! Provides types related to queuing buffers on a `Queue` object.
use super::{buffer::BufferInfo, Capture, Direction, Output};
use super::{BufferState, BufferStateFuse, BuffersAllocated, Queue};
use crate::ioctl;
use crate::memory::*;
use std::{
fmt::{self, Debug},
sync::Arc,
};
use nix::sys::time::{TimeVal, TimeValLike};
use thiserror::Error;
pub mod get_free;
pub mod get_indexed;
/// Error that can occur when queuing a buffer. It wraps a regular error and also
/// returns the plane handles back to the user.
#[derive(Error)]
#[error("{}", self.error)]
pub struct QueueError<P: BufferHandles> {
pub error: ioctl::QBufError<()>,
pub plane_handles: P,
}
impl<P: BufferHandles> Debug for QueueError<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Debug::fmt(&self.error, f)
}
}
#[allow(type_alias_bounds)]
pub type QueueResult<R, P: BufferHandles> = std::result::Result<R, QueueError<P>>;
/// A free buffer that has just been obtained from `Queue::get_buffer()` and
/// which is being prepared to the queued.
///
/// The necessary setup depends on the kind of direction of the buffer:
///
/// * Capture buffers are to be filled by the driver, so we just need to attach
/// one memory handle per plane before submitting them (MMAP buffers don't
/// need this step).
/// * Output buffers on the other hand are filled by us ; so on top of one valid
/// memory handle per plane, we also need to specify how much data we have
/// written in each of them, and possibly set a few flags on the buffer.
///
/// This struct is specialized on both the direction and type of memory so
/// mandatory data is always specified, and irrelevant data is inaccessible.
///
/// Once a buffer is ready, it can be queued using the queue() method. Failures
/// occur if the QBUF ioctl failed, or if the number of specified planes does
/// not match the number of planes in the format. A queued buffer remains
/// inaccessible for further queuing until it has been dequeued and dropped.
///
/// If a QBuffer object is destroyed before being queued, its buffer returns
/// to the pool of available buffers and can be requested again with
/// `Queue::get_buffer()`.
///
/// A QBuffer holds a strong reference to its queue, therefore the state of the
/// queue or device cannot be changed while it is being used. Contrary to
/// DQBuffer which can be freely duplicated and passed around, instances of this
/// struct are supposed to be short-lived.
pub struct QBuffer<'a, D: Direction, P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> {
queue: &'a Queue<D, BuffersAllocated<Q>>,
index: usize,
num_planes: usize,
timestamp: TimeVal,
fuse: BufferStateFuse<Q>,
_p: std::marker::PhantomData<P>,
}
impl<'a, D: Direction, P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> QBuffer<'a, D, P, Q> {
pub(super) fn new(
queue: &'a Queue<D, BuffersAllocated<Q>>,
buffer_info: &Arc<BufferInfo<Q>>,
) -> Self {
let buffer = &buffer_info.features;
let fuse = BufferStateFuse::new(Arc::downgrade(buffer_info));
QBuffer {
queue,
index: buffer.index,
num_planes: buffer.planes.len(),
timestamp: TimeVal::zero(),
fuse,
_p: std::marker::PhantomData,
}
}
/// Returns the V4L2 index of this buffer.
pub fn index(&self) -> usize {
self.index
}
/// Returns the number of handles/plane data expected to be specified for
/// this buffer.
pub fn num_expected_planes(&self) -> usize {
self.num_planes
}
pub fn set_timestamp(mut self, timestamp: TimeVal) -> Self {
self.timestamp = timestamp;
self
}
// R is meant to mean "either P or Q".
// Caller is responsible for making sure that the number of planes and
// plane_handles is the same as the number of expected planes for this
// buffer.
fn queue_bound_planes<R: BufferHandles + Into<Q>>(
mut self,
planes: Vec<ioctl::QBufPlane>,
plane_handles: R,
) -> QueueResult<(), R> {
let qbuffer = ioctl::QBuffer::<P::HandleType> {
planes,
timestamp: self.timestamp,
..Default::default()
};
match ioctl::qbuf(
&self.queue.inner,
self.queue.inner.type_,
self.index,
qbuffer,
) {
Ok(()) => (),
Err(error) => {
return Err(QueueError {
error,
plane_handles,
})
}
};
// We got this now.
self.fuse.disarm();
self.queue
.state
.buffer_info
.get(self.index)
.expect("Inconsistent buffer state!")
.update_state(|state| {
*state = BufferState::Queued(plane_handles.into());
});
Ok(())
}
}
impl<'a, P, Q> QBuffer<'a, Output, P, Q>
where
P: PrimitiveBufferHandles,
P::HandleType: Mappable,
Q: BufferHandles + From<P>,
{
pub fn get_plane_mapping(&self, plane: usize) -> Option<ioctl::PlaneMapping> {
let buffer_info = self.queue.state.buffer_info.get(self.index)?;
let plane_info = buffer_info.features.planes.get(plane)?;
P::HandleType::map(self.queue.inner.device.as_ref(), plane_info)
}
}
/// Trait for queueable CAPTURE buffers. These buffers only require handles to
/// be queued.
pub trait CaptureQueueable<Q: BufferHandles> {
/// Queue the buffer after binding `handles`, consuming the object.
/// The number of handles must match the buffer's expected number of planes.
fn queue_with_handles(self, handles: Q) -> QueueResult<(), Q>;
}
/// Trait for queueable OUTPUT buffers. The number of bytes used must be
/// specified for each plane.
pub trait OutputQueueable<Q: BufferHandles> {
/// Queue the buffer after binding `handles`, consuming the object.
/// The number of handles must match the buffer's expected number of planes.
/// `bytes_used` must be a slice with as many slices as there are handles,
/// describing the amount of useful data in each of them.
fn queue_with_handles(self, handles: Q, bytes_used: &[usize]) -> QueueResult<(), Q>;
}
/// Trait for all objects that are capable of providing objects that can be
/// queued to the CAPTURE queue.
pub trait CaptureQueueableProvider<'a, Q: BufferHandles> {
type Queueable: 'a + CaptureQueueable<Q>;
}
/// Trait for all objects that are capable of providing objects that can be
/// queued to the CAPTURE queue.
pub trait OutputQueueableProvider<'a, Q: BufferHandles> {
type Queueable: 'a + OutputQueueable<Q>;
}
/// Any CAPTURE QBuffer implements CaptureQueueable.
impl<P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> CaptureQueueable<Q>
for QBuffer<'_, Capture, P, Q>
{
fn queue_with_handles(self, handles: Q) -> QueueResult<(), Q> {
if handles.len() != self.num_expected_planes() {
return Err(QueueError {
error: ioctl::QBufError::NumPlanesMismatch(
handles.len(),
self.num_expected_planes(),
),
plane_handles: handles,
});
}
// TODO BufferHandles should have a method returning the actual MEMORY_TYPE implemented? So we can check
// that it matches with P.
let mut planes: Vec<_> = (0..self.num_expected_planes())
.map(|_| ioctl::QBufPlane::new(0))
.collect();
for (index, plane) in planes.iter_mut().enumerate() {
// TODO take the QBufPlane as argument if possible?
handles.fill_v4l2_plane(index, &mut plane.0);
}
self.queue_bound_planes(planes, handles)
}
}
/// Any OUTPUT QBuffer implements OutputQueueable.
impl<P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> OutputQueueable<Q>
for QBuffer<'_, Output, P, Q>
{
fn queue_with_handles(self, handles: Q, bytes_used: &[usize]) -> QueueResult<(), Q> {
if handles.len() != self.num_expected_planes() {
return Err(QueueError {
error: ioctl::QBufError::NumPlanesMismatch(
handles.len(),
self.num_expected_planes(),
),
plane_handles: handles,
});
}
// TODO make specific error for bytes_used?
if bytes_used.len() != self.num_expected_planes() {
return Err(QueueError {
error: ioctl::QBufError::NumPlanesMismatch(
bytes_used.len(),
self.num_expected_planes(),
),
plane_handles: handles,
});
}
// TODO BufferHandles should have a method returning the actual MEMORY_TYPE implemented? So we can check
// that it matches with P.
let mut planes: Vec<_> = bytes_used
.iter()
.map(|size| ioctl::QBufPlane::new(*size))
.collect();
for (index, plane) in planes.iter_mut().enumerate() {
// TODO take the QBufPlane as argument if possible?
handles.fill_v4l2_plane(index, &mut plane.0);
}
self.queue_bound_planes(planes, handles)
}
}
/// Shortcut to quickly queue self-backed CAPTURE buffers without specifying
/// empty handles.
/// Since we don't receive plane handles, we also don't need to return any, so
/// the returned error can be simplified.
impl<P: PrimitiveBufferHandles + Default, Q: BufferHandles + From<P>> QBuffer<'_, Capture, P, Q>
where
<P::HandleType as PlaneHandle>::Memory: SelfBacked,
{
pub fn queue(self) -> Result<(), ioctl::QBufError<()>> {
let planes: Vec<_> = (0..self.num_expected_planes())
.map(|_| ioctl::QBufPlane::new(0))
.collect();
self.queue_bound_planes::<P>(planes, Default::default())
.map_err(|e| e.error)
}
}
/// Shortcut to quickly queue self-backed OUTPUT buffers without specifying
/// empty handles.
/// Since we don't receive plane handles, we also don't need to return any, so
/// the returned error can be simplified.
impl<P: PrimitiveBufferHandles + Default, Q: BufferHandles + From<P>> QBuffer<'_, Output, P, Q>
where
<P::HandleType as PlaneHandle>::Memory: SelfBacked,
{
pub fn queue(self, bytes_used: &[usize]) -> Result<(), ioctl::QBufError<()>> {
// TODO make specific error for bytes_used?
if bytes_used.len() != self.num_expected_planes() {
return Err(ioctl::QBufError::NumPlanesMismatch(
bytes_used.len(),
self.num_expected_planes(),
));
}
let planes: Vec<_> = bytes_used
.iter()
.map(|size| ioctl::QBufPlane::new(*size))
.collect();
self.queue_bound_planes::<P>(planes, Default::default())
.map_err(|e| e.error)
}
}