v4l2r 0.0.7

Safe and flexible abstraction over V4L2
Documentation
//! Provides types related to dequeuing buffers from a `Queue` object.
use super::{
    buffer::BufferInfo,
    direction::{Capture, Direction},
    BufferStateFuse, BuffersAllocated, Queue,
};
use crate::ioctl::{self, PlaneMapping};
use crate::{
    device::Device,
    memory::{BufferHandles, Mappable, PrimitiveBufferHandles},
};
use std::{
    fmt::Debug,
    sync::{Arc, Weak},
};

pub type DropCallback<D, P> = Box<dyn FnOnce(&mut DqBuffer<D, P>) + Send>;

/// Represents the information of a dequeued buffer. This is basically the same
/// information as what the `ioctl` interface provides, but it also includes
/// the plane handles that have been provided when the buffer was queued to
/// return their ownership to the user.
pub struct DqBuffer<D: Direction, P: BufferHandles> {
    /// Dequeued buffer information as reported by V4L2.
    pub data: ioctl::V4l2Buffer,
    /// The backing memory that has been provided for this buffer.
    plane_handles: Option<P>,

    device: Weak<Device>,
    buffer_info: Weak<BufferInfo<P>>,
    /// Callbacks to be run when the object is dropped.
    drop_callbacks: Vec<DropCallback<D, P>>,
    /// Fuse that will put the buffer back into the `Free` state when this
    /// object is destroyed.
    fuse: BufferStateFuse<P>,
    _d: std::marker::PhantomData<D>,
}

impl<D: Direction, P: BufferHandles> Debug for DqBuffer<D, P> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.data.fmt(f)
    }
}

impl<D: Direction, P: BufferHandles> DqBuffer<D, P> {
    pub(super) fn new(
        queue: &Queue<D, BuffersAllocated<P>>,
        buffer: &Arc<BufferInfo<P>>,
        plane_handles: P,
        data: ioctl::V4l2Buffer,
        fuse: BufferStateFuse<P>,
    ) -> Self {
        DqBuffer {
            plane_handles: Some(plane_handles),
            data,
            device: Arc::downgrade(&queue.inner.device),
            buffer_info: Arc::downgrade(buffer),
            fuse,
            drop_callbacks: Default::default(),
            _d: std::marker::PhantomData,
        }
    }

    /// Attach a callback that will be called when the DQBuffer is destroyed,
    /// and after the buffer has been returned to the free list.
    /// This method can be called several times, the callback will be run in
    /// the inverse order that they were added.
    pub fn add_drop_callback<F: FnOnce(&mut Self) + Send + 'static>(&mut self, callback: F) {
        self.drop_callbacks.push(Box::new(callback));
    }

    /// Return the plane handles of the buffer. This method is guaranteed to
    /// return Some() the first time it is called, and None any subsequent times.
    pub fn take_handles(&mut self) -> Option<P> {
        self.plane_handles.take()
    }
}

impl<P> DqBuffer<Capture, P>
where
    P: PrimitiveBufferHandles,
    P::HandleType: Mappable,
{
    // TODO returned mapping should be read-only!
    pub fn get_plane_mapping(&self, plane_index: usize) -> Option<PlaneMapping> {
        // We can only obtain a mapping if this buffer has not been deleted.
        let buffer_info = self.buffer_info.upgrade()?;
        let plane = buffer_info.features.planes.get(plane_index)?;
        let plane_data = self.data.planes_iter().nth(plane_index)?;
        // If the buffer info was alive, then the device must also be.
        let device = self.device.upgrade()?;

        let start = *plane_data.data_offset.unwrap_or(&0) as usize;
        let end = *plane_data.bytesused as usize;

        Some(P::HandleType::map(device.as_ref(), plane)?.restrict(start, end))
    }
}

impl<D: Direction, P: BufferHandles> Drop for DqBuffer<D, P> {
    fn drop(&mut self) {
        // Make sure the buffer is returned to the free state before we call
        // the callbacks.
        self.fuse.trigger();
        while let Some(callback) = self.drop_callbacks.pop() {
            callback(self);
        }
    }
}