Skip to content

Commit

Permalink
io: Consolidate Arena types
Browse files Browse the repository at this point in the history
Less maintenance burden, more visibility due to the implementations
residing in a single file again.

Signed-off-by: Christopher N. Hesse <raymanfx@gmail.com>
  • Loading branch information
raymanfx committed May 5, 2024
1 parent 0b83491 commit 1d135fa
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 274 deletions.
137 changes: 0 additions & 137 deletions src/io/mmap/arena.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/io/mmap/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
pub(crate) mod arena;

pub mod stream;
pub use stream::Stream;
8 changes: 4 additions & 4 deletions src/io/mmap/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ use v4l2_sys::v4l2_buffer;

use crate::buffer::{Metadata, Type};
use crate::device::{Device, Handle};
use crate::io::mmap::arena::Arena;
use crate::io::traits::{CaptureStream, OutputStream, Stream as StreamTrait};
use crate::memory::Memory;
use crate::io::Arena;
use crate::memory::{Memory, Mmap};
use crate::v4l2;

/// Stream of mapped buffers
///
/// An arena instance is used internally for buffer handling.
pub struct Stream<'a> {
handle: Arc<Handle>,
arena: Arena<'a>,
arena: Arena<Mmap<'a>>,
arena_index: usize,
buf_type: Type,
buf_meta: Vec<Metadata>,
Expand Down Expand Up @@ -50,7 +50,7 @@ impl<'a> Stream<'a> {
}

pub fn with_buffers(dev: &Device, buf_type: Type, buf_count: u32) -> io::Result<Self> {
let mut arena = Arena::new(dev.handle(), buf_type);
let mut arena = Arena::<Mmap<'a>>::new(dev.handle(), buf_type);
let count = arena.allocate(buf_count)?;
let mut buf_meta = Vec::new();
buf_meta.resize(count as usize, Metadata::default());
Expand Down
171 changes: 171 additions & 0 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,174 @@ pub mod traits;

pub mod mmap;
pub mod userptr;

use std::{io, mem, ptr, slice, sync::Arc};

use v4l2_sys::{v4l2_buffer, v4l2_format, v4l2_requestbuffers};

use crate::buffer;
use crate::device::Handle;
use crate::memory::{Memory, Mmap, UserPtr};
use crate::v4l2;

/// Manage mapped buffers
///
/// All buffers are unmapped in the Drop impl.
/// In case of errors during unmapping, we panic because there is memory corruption going on.
pub(crate) struct Arena<T> {
handle: Arc<Handle>,
pub bufs: Vec<T>,
pub buf_mem: Memory,
pub buf_type: buffer::Type,
}

impl<T> Arena<T> {
fn request(&mut self, count: u32) -> io::Result<u32> {
// free all buffers by requesting 0
let mut v4l2_reqbufs = v4l2_requestbuffers {
count,
type_: self.buf_type as u32,
memory: self.buf_mem as u32,
..unsafe { mem::zeroed() }
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
)?;
}

Ok(v4l2_reqbufs.count)
}
}

impl<T> Drop for Arena<T> {
fn drop(&mut self) {
if self.bufs.is_empty() {
// nothing to do
return;
}

// free all buffers by requesting 0
if let Err(e) = self.request(0) {
if let Some(code) = e.raw_os_error() {
// ENODEV means the file descriptor wrapped in the handle became invalid, most
// likely because the device was unplugged or the connection (USB, PCI, ..)
// broke down. Handle this case gracefully by ignoring it.
if code == 19 {
/* ignore */
return;
}
}

panic!("{:?}", e)
}
}
}

impl<'a> Arena<Mmap<'a>> {
/// Returns a new buffer manager instance
///
/// You usually do not need to use this directly.
/// A MappedBufferStream creates its own manager instance by default.
///
/// # Arguments
///
/// * `handle` - Device handle to get its file descriptor
/// * `buf_type` - Type of the buffers
pub fn new(handle: Arc<Handle>, buf_type: buffer::Type) -> Self {
Arena {
handle,
bufs: Vec::new(),
buf_mem: Memory::Mmap,
buf_type,
}
}

pub fn allocate(&mut self, count: u32) -> io::Result<u32> {
let count = self.request(count)?;
for index in 0..count {
let mut v4l2_buf = v4l2_buffer {
index,
type_: self.buf_type as u32,
memory: self.buf_mem as u32,
..unsafe { mem::zeroed() }
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_QUERYBUF,
&mut v4l2_buf as *mut _ as *mut std::os::raw::c_void,
)?;

let ptr = v4l2::mmap(
ptr::null_mut(),
v4l2_buf.length as usize,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
self.handle.fd(),
v4l2_buf.m.offset as libc::off_t,
)?;

let slice =
slice::from_raw_parts_mut::<u8>(ptr as *mut u8, v4l2_buf.length as usize);
self.bufs.push(Mmap(slice));
}
}

Ok(count)
}
}

impl Arena<UserPtr> {
/// Returns a new buffer manager instance
///
/// You usually do not need to use this directly.
/// A MappedBufferStream creates its own manager instance by default.
///
/// # Arguments
///
/// * `handle` - Device handle to get its file descriptor
/// * `buf_type` - Type of the buffers
pub fn new(handle: Arc<Handle>, buf_type: buffer::Type) -> Self {
Arena {
handle,
bufs: Vec::new(),
buf_mem: Memory::UserPtr,
buf_type,
}
}

pub fn allocate(&mut self, count: u32) -> io::Result<u32> {
// we need to get the maximum buffer size from the format first
let mut v4l2_fmt = v4l2_format {
type_: self.buf_type as u32,
..unsafe { mem::zeroed() }
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_G_FMT,
&mut v4l2_fmt as *mut _ as *mut std::os::raw::c_void,
)?;
}

#[cfg(feature = "v4l-sys")]
eprintln!(
"\n### WARNING ###\n\
As of early 2020, libv4l2 still does not support USERPTR buffers!\n\
You may want to use this crate with the raw v4l2 FFI bindings instead!\n"
);

// allocate the new user buffers
let count = self.request(count)?;
for _ in 0..count {
let size = unsafe { v4l2_fmt.fmt.pix.sizeimage };
let buf = vec![0u8; size as usize];
self.bufs.push(UserPtr(buf));
}

Ok(count)
}
}
Loading

0 comments on commit 1d135fa

Please sign in to comment.