Skip to content

Commit

Permalink
Merge branch 'dmabuf'
Browse files Browse the repository at this point in the history
  • Loading branch information
dcz-self committed Oct 26, 2024
2 parents d50a57a + 258322a commit d0911e5
Show file tree
Hide file tree
Showing 7 changed files with 432 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ v4l2-sys = { path = "v4l2-sys", version = "0.3.0", package="v4l2-sys-mit", optio
[dev-dependencies]
glium = "0.35"
jpeg-decoder = "0.3.0"
memmap2 = "0.9"
winit = "0.30"

[features]
Expand Down
67 changes: 67 additions & 0 deletions examples/stream_capture_dmabuf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::io;
use std::time::Instant;

use v4l::buffer::Type;
use v4l::io::traits::CaptureStream;
use v4l::prelude::*;
use v4l::video::Capture;

fn main() -> io::Result<()> {
let path = "/dev/video0";
println!("Using device: {}\n", path);

// Capture 4 frames by default
let count = 4;

// Allocate 4 buffers by default
let buffer_count = 4;

let dev = Device::with_path(path)?;
let format = dev.format()?;
let params = dev.params()?;
println!("Active format:\n{}", format);
println!("Active parameters:\n{}", params);

// Setup a buffer stream and grab a frame, then print its data
let mut stream = DmabufStream::with_buffers(&dev, Type::VideoCapture, buffer_count)?;

// warmup
stream.next()?;

let start = Instant::now();
let mut megabytes_ps: f64 = 0.0;
for i in 0..count {
let t0 = Instant::now();
let (buf, meta) = stream.next()?;
let duration_us = t0.elapsed().as_micros();

println!("Buffer");
println!(" sequence : {}", meta.sequence);
println!(" timestamp : {}", meta.timestamp);
println!(" flags : {}", meta.flags);
use std::fs::File;
use std::os::fd::{FromRawFd, AsRawFd, IntoRawFd};
use memmap2;
let outf = unsafe { File::from_raw_fd(buf.as_raw_fd()) };
let outfmap = unsafe { memmap2::Mmap::map(&outf) }?;
println!(" length : {}", outfmap.len());

let cur = outfmap.len() as f64 / 1_048_576.0 * 1_000_000.0 / duration_us as f64;
if i == 0 {
megabytes_ps = cur;
} else {
// ignore the first measurement
let prev = megabytes_ps * (i as f64 / (i + 1) as f64);
let now = cur * (1.0 / (i + 1) as f64);
megabytes_ps = prev + now;
}
// Prevent File from dropping and closing the fd, because fd is borrowed from texture and not ours to close.
let _ = File::into_raw_fd(outf);
}

println!();
println!("FPS: {}", count as f64 / start.elapsed().as_secs_f64());
println!("MB/s: {}", megabytes_ps);

Ok(())
}
143 changes: 143 additions & 0 deletions src/io/dmabuf/arena.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use std::{io, mem, sync::Arc};
use std::os::fd::{FromRawFd, OwnedFd};

use crate::buffer;
use crate::device::Handle;
use crate::memory::Memory;
use crate::v4l2;
use crate::v4l_sys::*;

/// Manage dmabuf buffers
///
/// All buffers are released in the Drop impl.
pub struct Arena {
handle: Arc<Handle>,
pub bufs: Vec<OwnedFd>,
pub buf_type: buffer::Type,
}

impl Arena {
/// Returns a new buffer manager instance
///
/// You usually do not need to use this directly.
/// A UserBufferStream 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_type,
}
}

fn requestbuffers_desc(&self) -> v4l2_requestbuffers {
v4l2_requestbuffers {
type_: self.buf_type as u32,
..unsafe { mem::zeroed() }
}
}

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,
)?;
}

let mut v4l2_reqbufs = v4l2_requestbuffers {
count,
memory: Memory::Mmap as u32,
..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_exportbuf = v4l2_exportbuffer {
index,
type_: self.buf_type as u32,
flags: libc::O_RDWR as _,
..unsafe { mem::zeroed() }
};
let fd = unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_EXPBUF,
&mut v4l2_exportbuf as *mut _ as *mut std::os::raw::c_void,
)?;
OwnedFd::from_raw_fd(v4l2_exportbuf.fd)
};
self.bufs.push(fd);
}

let mut v4l2_reqbufs = v4l2_requestbuffers {
count,
memory: Memory::DmaBuf as u32,
..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,
)?;
}

Ok(v4l2_reqbufs.count)
}

pub fn release(&mut self) -> io::Result<()> {
// free all buffers by requesting 0
let mut v4l2_reqbufs = v4l2_requestbuffers {
count: 0,
memory: Memory::DmaBuf as u32,
..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,
)
}
}
}

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

if let Err(e) = self.release() {
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)
}
}
}
4 changes: 4 additions & 0 deletions src/io/dmabuf/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub(crate) mod arena;

pub mod stream;
pub use stream::Stream;
Loading

0 comments on commit d0911e5

Please sign in to comment.