Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io: dmabuf stream implementation #119

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.34"
jpeg-decoder = "0.3.0"
memmap2 = "0.9"
winit = "0.29"

[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