Skip to content

Commit

Permalink
ioctl: introduce and use V4l2Buffer structure
Browse files Browse the repository at this point in the history
Introduce a simple V4l2Buffer (inspired from DqBuffer) that makes it
easy to queue/dequeue/query buffers from a single structure. This allows
further simplifications, like the removal of the DqBuf trait which is
now equivalent to QueryBuf.
  • Loading branch information
Gnurou committed Mar 6, 2023
1 parent 9ac435d commit e263fe2
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 233 deletions.
2 changes: 1 addition & 1 deletion lib/examples/vicodec_test/ioctl_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ pub fn run<F: FnMut(&[u8])>(
dqbuf::<()>(&fd, output_queue).expect("Failed to dequeue output buffer");

// The CAPTURE buffer, on the other hand, we want to examine more closely.
let cap_dqbuf: DqBuffer =
let cap_dqbuf: V4l2Buffer =
dqbuf(&fd, capture_queue).expect("Failed to dequeue capture buffer");
let bytes_used = cap_dqbuf.get_first_plane().bytesused() as usize;

Expand Down
2 changes: 1 addition & 1 deletion lib/src/device/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ impl<D: Direction, P: BufferHandles> TryDequeue for Queue<D, BuffersAllocated<P>
type Dequeued = DqBuffer<D, P>;

fn try_dequeue(&self) -> DqBufResult<Self::Dequeued> {
let dqbuf: ioctl::DqBuffer = match ioctl::dqbuf(&self.inner, self.inner.type_) {
let dqbuf: ioctl::V4l2Buffer = match ioctl::dqbuf(&self.inner, self.inner.type_) {
Ok(dqbuf) => dqbuf,
Err(DqBufError::Eos) => return Err(DqBufError::Eos),
Err(DqBufError::NotReady) => return Err(DqBufError::NotReady),
Expand Down
4 changes: 2 additions & 2 deletions lib/src/device/queue/dqbuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub type DropCallback<D, P> = Box<dyn FnOnce(&mut DqBuffer<D, P>) + Send>;
/// return their ownership to the user.
pub struct DqBuffer<D: Direction, P: BufferHandles> {
/// Dequeued buffer information as reported by V4L2.
pub data: ioctl::DqBuffer,
pub data: ioctl::V4l2Buffer,
/// The backing memory that has been provided for this buffer.
plane_handles: Option<P>,

Expand All @@ -47,7 +47,7 @@ impl<D: Direction, P: BufferHandles> DqBuffer<D, P> {
queue: &Queue<D, BuffersAllocated<P>>,
buffer: &Arc<BufferInfo<P>>,
plane_handles: P,
data: ioctl::DqBuffer,
data: ioctl::V4l2Buffer,
fuse: BufferStateFuse<P>,
) -> Self {
DqBuffer {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/device/queue/qbuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl<'a, D: Direction, P: PrimitiveBufferHandles, Q: BufferHandles + From<P>> QB
self.index,
qbuffer,
) {
Ok(_) => (),
Ok(()) => (),
Err(error) => {
return Err(QueueError {
error,
Expand Down
147 changes: 136 additions & 11 deletions lib/src/ioctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub use request::*;
pub use streamon::*;
pub use subscribe_event::*;

use std::fmt::Debug;

use crate::bindings;
use crate::QueueType;
use std::{
Expand Down Expand Up @@ -102,17 +104,6 @@ mod tests {
}
}

/// A memory area we can pass to ioctls in order to get/set plane information
/// with the multi-planar API.
type PlaneData = [bindings::v4l2_plane; bindings::VIDEO_MAX_PLANES as usize];

/// For simple initialization of `PlaneData`.
impl Default for bindings::v4l2_plane {
fn default() -> Self {
unsafe { mem::zeroed() }
}
}

/// Returns whether the given queue type can handle multi-planar formats.
fn is_multi_planar(queue: QueueType) -> bool {
matches!(
Expand All @@ -134,3 +125,137 @@ where
self.into() as i32
}
}

/// A memory area we can pass to ioctls in order to get/set plane information
/// with the multi-planar API.
type V4l2BufferPlanes = [bindings::v4l2_plane; bindings::VIDEO_MAX_PLANES as usize];

/// For simple initialization of `V4l2BufferPlanes`.
impl Default for bindings::v4l2_plane {
fn default() -> Self {
unsafe { mem::zeroed() }
}
}

/// Information about a single plane of a V4L2 buffer.
pub struct V4l2BufferPlane<'a> {
plane: &'a bindings::v4l2_plane,
}

impl<'a> Debug for V4l2BufferPlane<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("V4l2BufferPlane")
.field("length", &self.length())
.field("bytesused", &self.bytesused())
.field("data_offset", &self.data_offset())
.finish()
}
}

impl<'a> V4l2BufferPlane<'a> {
pub fn length(&self) -> u32 {
self.plane.length
}

pub fn bytesused(&self) -> u32 {
self.plane.bytesused
}

pub fn data_offset(&self) -> u32 {
self.plane.data_offset
}
}

/// A completely owned v4l2_buffer, where the pointer to planes is meaningless and fixed up when
/// needed.
///
/// If the buffer is single-planar, it is modified to use `planes` anyway for the information of
/// its unique plane.
#[derive(Clone)]
#[repr(C)]
pub struct V4l2Buffer {
buffer: bindings::v4l2_buffer,
planes: V4l2BufferPlanes,
}

/// V4l2Buffer is safe to send across threads. `v4l2_buffer` is !Send because it
/// contains a pointer, but we are making sure to use it safely here.
unsafe impl Send for V4l2Buffer {}

impl Debug for V4l2Buffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("V4l2Buffer")
.field("index", &self.index())
.field("flags", &self.flags())
.field("sequence", &self.sequence())
.finish()
}
}

impl V4l2Buffer {
pub fn index(&self) -> u32 {
self.buffer.index
}

pub fn queue_type(&self) -> u32 {
self.buffer.type_
}

pub fn flags(&self) -> BufferFlags {
BufferFlags::from_bits_truncate(self.buffer.flags)
}

pub fn is_last(&self) -> bool {
self.flags().contains(BufferFlags::LAST)
}

pub fn timestamp(&self) -> bindings::timeval {
self.buffer.timestamp
}

pub fn sequence(&self) -> u32 {
self.buffer.sequence
}

pub fn is_multi_planar(&self) -> bool {
matches!(
self.buffer.type_,
bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
| bindings::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
)
}

pub fn num_planes(&self) -> usize {
if self.is_multi_planar() {
self.buffer.length as usize
} else {
1
}
}

/// Returns the first plane of the buffer. This method is guaranteed to
/// succeed because every buffer has at least one plane.
pub fn get_first_plane(&self) -> V4l2BufferPlane {
V4l2BufferPlane {
plane: &self.planes[0],
}
}

/// Returns plane `index` of the buffer, or `None` if `index` is larger than
/// the number of planes in this buffer.
pub fn get_plane(&self, index: usize) -> Option<V4l2BufferPlane> {
if index < self.num_planes() {
Some(V4l2BufferPlane {
plane: &self.planes[index],
})
} else {
None
}
}

/// Returns the raw v4l2_buffer as a pointer. Useful to pass to unsafe
/// non-Rust code.
pub fn as_raw_v4l2_buffer(&self) -> *const bindings::v4l2_buffer {
&self.buffer
}
}
Loading

0 comments on commit e263fe2

Please sign in to comment.