use std::{io, mem, ptr, slice, sync::Arc};
use crate::buffer;
use crate::device::Handle;
use crate::memory::Memory;
use crate::v4l2;
use crate::v4l_sys::*;
pub struct Arena<'a> {
handle: Arc<Handle>,
pub bufs: Vec<&'a mut [u8]>,
pub buf_type: buffer::Type,
}
impl<'a> Arena<'a> {
pub fn new(handle: Arc<Handle>, buf_type: buffer::Type) -> Self {
Arena {
handle,
bufs: Vec::new(),
buf_type,
}
}
fn buffer_desc(&self) -> v4l2_buffer {
v4l2_buffer {
type_: self.buf_type as u32,
memory: Memory::Mmap as u32,
..unsafe { mem::zeroed() }
}
}
fn requestbuffers_desc(&self) -> v4l2_requestbuffers {
v4l2_requestbuffers {
type_: self.buf_type as u32,
memory: Memory::Mmap as u32,
..unsafe { mem::zeroed() }
}
}
pub fn allocate(&mut self, count: u32) -> io::Result<u32> {
let mut v4l2_reqbufs = v4l2_requestbuffers {
count,
..self.requestbuffers_desc()
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
)?;
}
for index in 0..v4l2_reqbufs.count {
let mut v4l2_buf = v4l2_buffer {
index,
..self.buffer_desc()
};
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(slice);
}
}
Ok(v4l2_reqbufs.count)
}
pub fn release(&mut self) -> io::Result<()> {
for buf in &self.bufs {
unsafe {
v4l2::munmap(buf.as_ptr() as *mut core::ffi::c_void, buf.len())?;
}
}
let mut v4l2_reqbufs = v4l2_requestbuffers {
count: 0,
..self.requestbuffers_desc()
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
)?;
}
self.bufs.clear();
Ok(())
}
}
impl<'a> Drop for Arena<'a> {
fn drop(&mut self) {
if self.bufs.is_empty() {
return;
}
if let Err(e) = self.release() {
if let Some(code) = e.raw_os_error() {
if code == 19 {
return;
}
}
panic!("{:?}", e)
}
}
}