use std::{io, mem, ptr::NonNull, sync::Arc};
use v4l::{
buffer::{Metadata, Type},
device::{Device, Handle},
io::traits::Stream as StreamTrait,
memory::Memory,
v4l2,
v4l_sys::*,
};
#[derive(Clone)]
pub struct MmapBuffer {
ptr: NonNull<u8>,
length: usize,
_mmap_info: Arc<MmapInfo>,
}
unsafe impl Send for MmapBuffer {}
unsafe impl Sync for MmapBuffer {}
impl MmapBuffer {
pub(crate) unsafe fn new(ptr: *const u8, length: usize, mmap_info: Arc<MmapInfo>) -> Self {
Self {
ptr: NonNull::new_unchecked(ptr as *mut u8),
length,
_mmap_info: mmap_info,
}
}
pub(crate) fn with_length(&self, used_bytes: usize) -> Self {
let len = used_bytes.min(self.length);
Self {
ptr: self.ptr,
length: len,
_mmap_info: self._mmap_info.clone(),
}
}
pub fn len(&self) -> usize {
self.length
}
pub fn is_empty(&self) -> bool {
self.length == 0
}
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.length) }
}
pub fn into_vec(self) -> Vec<u8> {
self.as_slice().to_vec()
}
}
impl std::ops::Deref for MmapBuffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
pub(crate) struct MmapInfo {
pub(crate) ptr: *mut u8, pub(crate) length: usize,
#[allow(dead_code)]
pub(crate) offset: u32,
}
unsafe impl Send for MmapInfo {}
unsafe impl Sync for MmapInfo {}
impl Drop for MmapInfo {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe {
let result = libc::munmap(self.ptr as *mut libc::c_void, self.length);
if result == -1 {
eprintln!(
"Error: munmap failed with errno {}",
*libc::__errno_location()
);
}
}
}
}
}
pub struct MmapStream {
handle: Arc<Handle>,
buffers: Vec<MmapBuffer>,
buf_type: Type,
buf_meta: Vec<Metadata>,
active: bool,
timeout: Option<i32>,
queued: Vec<bool>,
}
impl MmapStream {
pub fn with_buffers(device: &Device, buf_type: Type, buf_count: u32) -> io::Result<Self> {
let handle = device.handle();
let mut v4l2_reqbufs = v4l2_requestbuffers {
count: buf_count,
type_: buf_type as u32,
memory: Memory::Mmap as u32,
..unsafe { mem::zeroed() }
};
unsafe {
v4l2::ioctl(
handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
)?;
}
let actual_count = v4l2_reqbufs.count as usize;
let mut buffers = Vec::with_capacity(actual_count);
let mut buf_meta = Vec::with_capacity(actual_count);
for i in 0..actual_count {
let mut v4l2_buf = v4l2_buffer {
index: i as u32,
type_: buf_type as u32,
memory: Memory::Mmap as u32,
..unsafe { mem::zeroed() }
};
unsafe {
v4l2::ioctl(
handle.fd(),
v4l2::vidioc::VIDIOC_QUERYBUF,
&mut v4l2_buf as *mut _ as *mut std::os::raw::c_void,
)?;
}
let length = v4l2_buf.length as usize;
let offset = unsafe { v4l2_buf.m.offset };
let ptr = unsafe {
libc::mmap(
std::ptr::null_mut(),
length,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
handle.fd(),
offset as libc::off_t,
)
};
if ptr == libc::MAP_FAILED {
return Err(io::Error::last_os_error());
}
let mmap_info = Arc::new(MmapInfo {
ptr: ptr as *mut u8,
length,
offset,
});
let buffer = unsafe { MmapBuffer::new(ptr as *const u8, length, mmap_info) };
buffers.push(buffer);
buf_meta.push(Metadata::default());
}
Ok(Self {
handle,
buffers,
buf_type,
buf_meta,
active: false,
timeout: None,
queued: vec![false; actual_count],
})
}
fn queue_buffer(&mut self, index: usize) -> io::Result<()> {
let mut v4l2_buf = v4l2_buffer {
index: index as u32,
type_: self.buf_type as u32,
memory: Memory::Mmap as u32,
..unsafe { mem::zeroed() }
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_QBUF,
&mut v4l2_buf as *mut _ as *mut std::os::raw::c_void,
)?;
}
Ok(())
}
fn dequeue_buffer(&mut self) -> io::Result<usize> {
let mut v4l2_buf = v4l2_buffer {
type_: self.buf_type as u32,
memory: Memory::Mmap as u32,
..unsafe { mem::zeroed() }
};
if self.handle.poll(libc::POLLIN, self.timeout.unwrap_or(-1))? == 0 {
return Err(io::Error::new(io::ErrorKind::TimedOut, "Buffer timeout"));
}
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_DQBUF,
&mut v4l2_buf as *mut _ as *mut std::os::raw::c_void,
)?;
}
let index = v4l2_buf.index as usize;
if index >= self.buf_meta.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"driver returned an invalid buffer index",
));
}
self.buf_meta[index] = Metadata {
bytesused: v4l2_buf.bytesused,
flags: v4l2_buf.flags.into(),
field: v4l2_buf.field,
timestamp: v4l2_buf.timestamp.into(),
sequence: v4l2_buf.sequence,
};
Ok(index)
}
pub fn next_frame(&mut self) -> io::Result<(MmapBuffer, Metadata)> {
for i in 0..self.buffers.len() {
if !self.queued[i] && Arc::strong_count(&self.buffers[i]._mmap_info) == 1 {
self.queue_buffer(i)?;
self.queued[i] = true;
}
}
if !self.queued.iter().any(|&q| q) {
return Err(io::Error::new(
io::ErrorKind::WouldBlock,
"all capture buffers are still referenced by previous frames",
));
}
if !self.active {
self.start()?;
}
let index = self.dequeue_buffer()?;
let buffer = &self.buffers[index];
self.queued[index] = false;
let metadata = self.buf_meta[index];
let used_bytes = if metadata.bytesused > 0 {
metadata.bytesused as usize
} else {
buffer.len()
};
let frame_buffer = buffer.with_length(used_bytes);
Ok((frame_buffer, metadata))
}
}
impl StreamTrait for MmapStream {
type Item = MmapBuffer;
fn start(&mut self) -> io::Result<()> {
if self.active {
return Ok(());
}
let mut buf_type = self.buf_type as u32;
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_STREAMON,
&mut buf_type as *mut _ as *mut std::os::raw::c_void,
)?;
}
self.active = true;
Ok(())
}
fn stop(&mut self) -> io::Result<()> {
if !self.active {
return Ok(());
}
let mut buf_type = self.buf_type as u32;
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_STREAMOFF,
&mut buf_type as *mut _ as *mut std::os::raw::c_void,
)?;
}
self.queued.fill(false);
self.active = false;
Ok(())
}
}
impl Drop for MmapStream {
fn drop(&mut self) {
if let Err(e) = self.stop() {
if let Some(code) = e.raw_os_error() {
if code == 19 {
return;
}
}
eprintln!("Error stopping stream: {e}");
}
let mut v4l2_reqbufs = v4l2_requestbuffers {
count: 0,
type_: self.buf_type as u32,
memory: Memory::Mmap as u32,
..unsafe { mem::zeroed() }
};
unsafe {
let _ = v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mmap_buffer_clone_preserves_data() {
let test_data = b"Hello, World!";
let mmap_info = Arc::new(MmapInfo {
ptr: std::ptr::null_mut(),
length: test_data.len(),
offset: 0,
});
let buffer =
unsafe { MmapBuffer::new(test_data.as_ptr(), test_data.len(), mmap_info.clone()) };
let cloned = buffer.clone();
assert_eq!(buffer.as_slice(), test_data);
assert_eq!(cloned.as_slice(), test_data);
assert_eq!(buffer.as_slice(), cloned.as_slice());
assert_eq!(buffer.len(), test_data.len());
assert_eq!(cloned.len(), test_data.len());
}
#[test]
fn test_mmap_buffer_with_length() {
let test_data = b"Hello, World!";
let mmap_info = Arc::new(MmapInfo {
ptr: std::ptr::null_mut(),
length: test_data.len(),
offset: 0,
});
let buffer =
unsafe { MmapBuffer::new(test_data.as_ptr(), test_data.len(), mmap_info.clone()) };
let shorter = buffer.with_length(5);
assert_eq!(shorter.len(), 5);
assert_eq!(shorter.as_slice(), b"Hello");
let longer = buffer.with_length(100);
assert_eq!(longer.len(), test_data.len());
assert_eq!(longer.as_slice(), test_data);
let empty = buffer.with_length(0);
assert_eq!(empty.len(), 0);
assert!(empty.is_empty());
}
#[test]
fn test_mmap_buffer_as_slice() {
let test_data = b"Test Data";
let mmap_info = Arc::new(MmapInfo {
ptr: std::ptr::null_mut(),
length: test_data.len(),
offset: 0,
});
let buffer =
unsafe { MmapBuffer::new(test_data.as_ptr(), test_data.len(), mmap_info.clone()) };
let slice = buffer.as_slice();
assert_eq!(slice, test_data);
assert_eq!(slice.len(), test_data.len());
}
#[test]
fn test_mmap_buffer_len_and_is_empty() {
let test_data = b"Data";
let mmap_info = Arc::new(MmapInfo {
ptr: std::ptr::null_mut(),
length: test_data.len(),
offset: 0,
});
let buffer =
unsafe { MmapBuffer::new(test_data.as_ptr(), test_data.len(), mmap_info.clone()) };
assert_eq!(buffer.len(), 4);
assert!(!buffer.is_empty());
let empty = buffer.with_length(0);
assert_eq!(empty.len(), 0);
assert!(empty.is_empty());
}
#[test]
fn test_mmap_buffer_deref() {
let test_data = b"Hello";
let mmap_info = Arc::new(MmapInfo {
ptr: std::ptr::null_mut(),
length: test_data.len(),
offset: 0,
});
let buffer =
unsafe { MmapBuffer::new(test_data.as_ptr(), test_data.len(), mmap_info.clone()) };
let slice: &[u8] = &buffer;
assert_eq!(slice, test_data);
}
#[test]
fn test_mmap_buffer_into_vec() {
let test_data = b"Copy Test";
let mmap_info = Arc::new(MmapInfo {
ptr: std::ptr::null_mut(),
length: test_data.len(),
offset: 0,
});
let buffer =
unsafe { MmapBuffer::new(test_data.as_ptr(), test_data.len(), mmap_info.clone()) };
let vec = buffer.into_vec();
assert_eq!(vec.as_slice(), test_data);
assert_eq!(vec.len(), test_data.len());
}
#[test]
fn test_mmap_buffer_cleanup() {
let test_data = b"Cleanup Test Data";
let mmap_info = Arc::new(MmapInfo {
ptr: std::ptr::null_mut(),
length: test_data.len(),
offset: 0,
});
let buffer1 =
unsafe { MmapBuffer::new(test_data.as_ptr(), test_data.len(), mmap_info.clone()) };
let buffer2 = buffer1.clone();
let buffer3 = buffer1.clone();
assert_eq!(buffer1.as_slice(), test_data);
assert_eq!(buffer2.as_slice(), test_data);
assert_eq!(buffer3.as_slice(), test_data);
assert_eq!(Arc::strong_count(&mmap_info), 4);
drop(buffer1);
assert_eq!(Arc::strong_count(&mmap_info), 3);
assert_eq!(buffer2.as_slice(), test_data); assert_eq!(buffer3.as_slice(), test_data);
drop(buffer2);
assert_eq!(Arc::strong_count(&mmap_info), 2);
assert_eq!(buffer3.as_slice(), test_data);
drop(buffer3);
assert_eq!(Arc::strong_count(&mmap_info), 1);
drop(mmap_info);
}
#[test]
fn test_mmap_buffer_multiple_lengths() {
let test_data = b"Multiple Lengths Test";
let mmap_info = Arc::new(MmapInfo {
ptr: std::ptr::null_mut(),
length: test_data.len(),
offset: 0,
});
let full_buffer =
unsafe { MmapBuffer::new(test_data.as_ptr(), test_data.len(), mmap_info.clone()) };
let half = full_buffer.with_length(test_data.len() / 2);
let quarter = full_buffer.with_length(test_data.len() / 4);
let empty = full_buffer.with_length(0);
assert_eq!(full_buffer.len(), test_data.len());
assert_eq!(full_buffer.as_slice(), test_data);
assert_eq!(half.len(), test_data.len() / 2);
assert_eq!(half.as_slice(), &test_data[..test_data.len() / 2]);
assert_eq!(quarter.len(), test_data.len() / 4);
assert_eq!(quarter.as_slice(), &test_data[..test_data.len() / 4]);
assert_eq!(empty.len(), 0);
assert!(empty.is_empty());
assert_eq!(Arc::strong_count(&mmap_info), 5); }
}