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

basic multi-plane support #71

Open
wants to merge 5 commits 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Currently, the focus is on implementing the high-level V4L2 single-planar API.
Multi-planar capture will not be targeted in the near future unless someone else starts working on it.


## [0.13.1] - 2023-05-26
### Added
- Basic multi-planar streaming support

## [0.13.1] - 2022-12-08
### Fixed
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "v4l"
description = "Safe video4linux (v4l) bindings"
version = "0.13.1"
version = "0.14.0"
authors = ["Christopher N. Hesse <[email protected]>"]
edition = "2018"
license = "MIT"
Expand All @@ -11,8 +11,8 @@ repository= "https://github.com/raymanfx/libv4l-rs"
[dependencies]
bitflags = "1.2.1"
libc = "0.2"
v4l-sys = { path = "v4l-sys", version = "0.2.0", optional = true }
v4l2-sys = { path = "v4l2-sys", version = "0.2.0", package="v4l2-sys-mit", optional = true }
v4l-sys = { path = "v4l-sys", version = "0.2.1", optional = true }
v4l2-sys = { path = "v4l2-sys", version = "0.2.1", package="v4l2-sys-mit", optional = true }

[dev-dependencies]
glium = "0.27.0"
Expand Down
9 changes: 9 additions & 0 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ pub enum Type {
Private = 0x80,
}

impl Type {
pub fn planar(&self) -> bool {
match self {
Type::VideoCaptureMplane | Type::VideoOutputMplane => true,
_ => false,
}
Comment on lines +35 to +38
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: there's a matches!(self, Type::VideoCaptureMplane | Type::VideoOutputMplane) macro for this:

Suggested change
match self {
Type::VideoCaptureMplane | Type::VideoOutputMplane => true,
_ => false,
}
matches!(self, Type::VideoCaptureMplane | Type::VideoOutputMplane)

}
}

bitflags! {
#[allow(clippy::unreadable_literal)]
pub struct Flags: u32 {
Expand Down
16 changes: 12 additions & 4 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ use crate::v4l2::videodev::v4l2_ext_controls;
use crate::v4l_sys::*;
use crate::{capability::Capabilities, control::Control};

pub const PLANES_ONE: bool = false;
pub const PLANES_MANY: bool = true;

pub type Device = PlanarDevice<PLANES_ONE>;
pub type MultiPlaneDevice = PlanarDevice<PLANES_MANY>;

/// Linux capture device abstraction
pub struct Device {
pub struct PlanarDevice<const M: bool> {
/// Raw handle
handle: Arc<Handle>,
}

impl Device {
impl<const M: bool> PlanarDevice<M> {
/// Returns a capture device by index
///
/// Devices are usually enumerated by the system.
Expand All @@ -41,7 +47,7 @@ impl Device {
return Err(io::Error::last_os_error());
}

Ok(Device {
Ok(Self {
handle: Arc::new(Handle::new(fd)),
})
}
Expand All @@ -67,7 +73,7 @@ impl Device {
return Err(io::Error::last_os_error());
}

Ok(Device {
Ok(Self {
handle: Arc::new(Handle::new(fd)),
})
}
Expand All @@ -76,7 +82,9 @@ impl Device {
pub fn handle(&self) -> Arc<Handle> {
self.handle.clone()
}
}

impl Device {
/// Returns video4linux framework defined information such as card, driver, etc.
pub fn query_caps(&self) -> io::Result<Capabilities> {
unsafe {
Expand Down
94 changes: 90 additions & 4 deletions src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ impl fmt::Display for Flags {
}
}

#[derive(Debug, Copy, Clone)]
#[derive(Debug, Clone)]
/// Streaming format (single-planar)
pub struct Format {
pub struct PlanarFormat<P> {
/// width in pixels
pub width: u32,
/// height in pixels
Expand All @@ -59,9 +59,9 @@ pub struct Format {
pub field_order: FieldOrder,

/// bytes per line
pub stride: u32,
pub stride: P,
/// maximum number of bytes required to store an image
pub size: u32,
pub size: P,

/// flags set by the application or driver
pub flags: Flags,
Expand All @@ -74,6 +74,9 @@ pub struct Format {
pub transfer: TransferFunction,
}

pub type Format = PlanarFormat<u32>;
impl Copy for Format {}

impl Format {
/// Returns a capture format
///
Expand Down Expand Up @@ -154,3 +157,86 @@ impl From<Format> for v4l2_pix_format {
}
}
}

pub type MultiPlaneFormat = PlanarFormat<Vec<u32>>;

impl MultiPlaneFormat {
pub fn single_plane(width: u32, height: u32, fourcc: FourCC) -> Self {
Self {
width,
height,
fourcc,
field_order: FieldOrder::Any,
stride: vec![0],
size: vec![0],
flags: Flags::empty(),
colorspace: Colorspace::Default,
quantization: Quantization::Default,
transfer: TransferFunction::Default,
}
}
}

impl From<v4l2_pix_format_mplane> for MultiPlaneFormat {
fn from(fmt: v4l2_pix_format_mplane) -> Self {
let planes = &fmt.plane_fmt[0..fmt.num_planes as usize];
Self {
width: fmt.width,
height: fmt.height,
fourcc: FourCC::from(fmt.pixelformat),
field_order: FieldOrder::try_from(fmt.field).expect("Invalid field order"),
stride: planes.iter().map(|p| p.bytesperline).collect(),
size: planes.iter().map(|p| p.sizeimage).collect(),
flags: Flags::from(fmt.flags as u32),
colorspace: Colorspace::try_from(fmt.colorspace).expect("Invalid colorspace"),
quantization: Quantization::try_from(fmt.quantization as u32)
.expect("Invalid quantization"),
transfer: TransferFunction::try_from(fmt.xfer_func as u32)
.expect("Invalid transfer function"),
}
}
}

impl fmt::Display for MultiPlaneFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "width : {}", self.width)?;
writeln!(f, "height : {}", self.height)?;
writeln!(f, "fourcc : {}", self.fourcc)?;
writeln!(f, "field : {}", self.field_order)?;
writeln!(f, "stride : {:?}", self.stride)?;
writeln!(f, "size : {:?}", self.size)?;
writeln!(f, "colorspace : {}", self.colorspace)?;
writeln!(f, "quantization : {}", self.quantization)?;
writeln!(f, "transfer : {}", self.transfer)?;
Ok(())
}
}

impl From<MultiPlaneFormat> for v4l2_pix_format_mplane {
fn from(format: MultiPlaneFormat) -> Self {
let mut fmt = Self {
width: format.width,
height: format.height,
pixelformat: format.fourcc.into(),
field: format.field_order as u32,
plane_fmt: [
v4l2_plane_pix_format {
..unsafe { mem::zeroed() }
}
; 8
],
num_planes: format.size.len() as u8,
colorspace: format.colorspace as u32,
flags: Into::<u32>::into(format.flags) as u8,
quantization: format.quantization as u8,
xfer_func: format.transfer as u8,
..unsafe { mem::zeroed() }
};
for i in 0..format.size.len() {
fmt.plane_fmt[i].sizeimage = format.size[i];
fmt.plane_fmt[i].bytesperline = format.stride[i];
}
fmt
}
}

66 changes: 52 additions & 14 deletions src/io/mmap/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ use crate::v4l_sys::*;
/// In case of errors during unmapping, we panic because there is memory corruption going on.
pub struct Arena<'a> {
handle: Arc<Handle>,
pub bufs: Vec<&'a mut [u8]>,
pub bufs: Vec<Vec<&'a mut [u8]>>,
pub buf_type: buffer::Type,
pub planes: Vec<Vec<v4l2_plane>>,
}

impl<'a> Arena<'a> {
Expand All @@ -31,6 +32,7 @@ impl<'a> Arena<'a> {
handle,
bufs: Vec::new(),
buf_type,
planes: Vec::new(),
}
}

Expand All @@ -51,6 +53,24 @@ impl<'a> Arena<'a> {
}

pub fn allocate(&mut self, count: u32) -> io::Result<u32> {
let num_planes = if !self.buf_type.planar() {
1
} else {
// we need to get the number of image planes from the format
let mut v4l2_fmt: v4l2_format;
unsafe {
v4l2_fmt = mem::zeroed();
v4l2_fmt.type_ = self.buf_type as u32;
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_G_FMT,
&mut v4l2_fmt as *mut _ as *mut std::os::raw::c_void,
)?;

v4l2_fmt.fmt.pix_mp.num_planes as usize
}
};

let mut v4l2_reqbufs = v4l2_requestbuffers {
count,
..self.requestbuffers_desc()
Expand All @@ -64,29 +84,45 @@ impl<'a> Arena<'a> {
}

for index in 0..v4l2_reqbufs.count {
let mut v4l2_planes: Vec<v4l2_plane> = Vec::new();
unsafe {
v4l2_planes.resize(num_planes as usize, mem::zeroed());
}
let mut v4l2_buf = v4l2_buffer {
index,
..self.buffer_desc()
};
if self.buf_type.planar() {
v4l2_buf.length = num_planes as u32;
v4l2_buf.m.planes = v4l2_planes.as_mut_ptr();
}
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,
)?;
// each plane has to be mapped separately
let mut planes = Vec::new();
for plane in &v4l2_planes {
let ptr = v4l2::mmap(
ptr::null_mut(),
plane.length as usize,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
self.handle.fd(),
plane.m.mem_offset as libc::off_t,
)?;

planes.push(slice::from_raw_parts_mut::<u8>(
ptr as *mut u8, plane.length as usize
));
Comment on lines +109 to +120

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I tried to use this branch for my application, which use both multi plane and single plane, I received error with single plane device. This fix works with my environment.

Suggested change
let ptr = v4l2::mmap(
ptr::null_mut(),
plane.length as usize,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
self.handle.fd(),
plane.m.mem_offset as libc::off_t,
)?;
planes.push(slice::from_raw_parts_mut::<u8>(
ptr as *mut u8, plane.length as usize
));
let length = if !self.buf_type.planar() { v4l2_buf.length as usize } else { plane.length as usize };
let ptr = v4l2::mmap(
ptr::null_mut(),
length,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
self.handle.fd(),
plane.m.mem_offset as libc::off_t,
)?;
planes.push(slice::from_raw_parts_mut::<u8>(
ptr as *mut u8, length,
));

}

let slice =
slice::from_raw_parts_mut::<u8>(ptr as *mut u8, v4l2_buf.length as usize);
self.bufs.push(slice);
// finally, add the buffer (with all its planes) to the set
self.bufs.push(planes);
self.planes.push(v4l2_planes);
}
}

Expand All @@ -95,8 +131,10 @@ impl<'a> Arena<'a> {

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())?;
for plane in buf {
unsafe {
v4l2::munmap(plane.as_ptr() as *mut core::ffi::c_void, buf.len())?;
}
}
}

Expand Down
Loading