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

Feature/custom userptr allocator to take into account special hardware-aligment requirements for some v4l devices #103

Open
wants to merge 2 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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repository= "https://github.com/raymanfx/libv4l-rs"
[dependencies]
bitflags = "1.2.1"
libc = "0.2"
allocator-api2 = "0.2.18"
v4l-sys = { path = "v4l-sys", version = "0.3.0", optional = true }
v4l2-sys = { path = "v4l2-sys", version = "0.3.0", package="v4l2-sys-mit", optional = true }

Expand Down
89 changes: 47 additions & 42 deletions src/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use bitflags::bitflags;
use std::fmt;

use crate::timestamp::Timestamp;
Expand Down Expand Up @@ -30,49 +29,55 @@ pub enum Type {
Private = 0x80,
}

bitflags! {
#[allow(clippy::unreadable_literal)]
pub struct Flags: u32 {
/// Buffer is mapped
const MAPPED = 0x00000001;
/// Buffer is queued for processing
const QUEUED = 0x00000002;
/// Buffer is ready
const DONE = 0x00000004;
/// Image is a keyframe (I-frame)
const KEYFRAME = 0x00000008;
/// Image is a P-frame
const PFRAME = 0x00000010;
/// Image is a B-frame
const BFRAME = 0x00000020;
/// Buffer is ready, but the data contained within is corrupted
const ERROR = 0x00000040;
/// Buffer is added to an unqueued request
const IN_REQUEST = 0x00000080;
/// Timecode field is valid
const TIMECODE = 0x00000100;
/// Don't return the capture buffer until OUTPUT timestamp changes
const M2M_HOLD_CAPTURE_BUF = 0x00000200;
/// Buffer is prepared for queuing
const PREPARED = 0x00000400;
/// Cache handling flags
const NO_CACHE_INVALIDATE = 0x00000800;
const NO_CACHE_CLEAN = 0x00001000;
/// Timestamp type
const TIMESTAMP_MASK = 0x0000e000;
const TIMESTAMP_UNKNOWN = 0x00000000;
const TIMESTAMP_MONOTONIC = 0x00002000;
const TIMESTAMP_COPY = 0x00004000;
/// Timestamp sources
const TSTAMP_SRC_MASK = 0x00070000;
const TSTAMP_SRC_EOF = 0x00000000;
const TSTAMP_SRC_SOE = 0x00010000;
/// mem2mem encoder/decoder
const LAST = 0x00100000;
/// request_fd is valid
const REQUEST_FD = 0x00800000;
// Module trick to make it possible to allow the clippy warning of the output of a macro
#[allow(clippy::bad_bit_mask)]
mod flags {
use bitflags::bitflags;
bitflags! {
#[allow(clippy::unreadable_literal)]
pub struct Flags: u32 {
/// Buffer is mapped
const MAPPED = 0x00000001;
/// Buffer is queued for processing
const QUEUED = 0x00000002;
/// Buffer is ready
const DONE = 0x00000004;
/// Image is a keyframe (I-frame)
const KEYFRAME = 0x00000008;
/// Image is a P-frame
const PFRAME = 0x00000010;
/// Image is a B-frame
const BFRAME = 0x00000020;
/// Buffer is ready, but the data contained within is corrupted
const ERROR = 0x00000040;
/// Buffer is added to an unqueued request
const IN_REQUEST = 0x00000080;
/// Timecode field is valid
const TIMECODE = 0x00000100;
/// Don't return the capture buffer until OUTPUT timestamp changes
const M2M_HOLD_CAPTURE_BUF = 0x00000200;
/// Buffer is prepared for queuing
const PREPARED = 0x00000400;
/// Cache handling flags
const NO_CACHE_INVALIDATE = 0x00000800;
const NO_CACHE_CLEAN = 0x00001000;
/// Timestamp type
const TIMESTAMP_MASK = 0x0000e000;
const TIMESTAMP_UNKNOWN = 0x00000000;
const TIMESTAMP_MONOTONIC = 0x00002000;
const TIMESTAMP_COPY = 0x00004000;
/// Timestamp sources
const TSTAMP_SRC_MASK = 0x00070000;
const TSTAMP_SRC_EOF = 0x00000000;
const TSTAMP_SRC_SOE = 0x00010000;
/// mem2mem encoder/decoder
const LAST = 0x00100000;
/// request_fd is valid
const REQUEST_FD = 0x00800000;
}
}
}
use flags::*;

impl Default for Flags {
fn default() -> Self {
Expand Down
35 changes: 30 additions & 5 deletions src/io/userptr/arena.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::{io, mem, sync::Arc};

use allocator_api2::{alloc::Allocator, vec::Vec as AllocatorVec};

use crate::buffer;
use crate::device::Handle;
use crate::memory::Memory;
Expand All @@ -9,13 +11,14 @@ use crate::v4l_sys::*;
/// Manage user allocated buffers
///
/// All buffers are released in the Drop impl.
pub struct Arena {
pub struct Arena<A: Allocator + Clone> {
handle: Arc<Handle>,
pub bufs: Vec<Vec<u8>>,
allocator: A,
pub bufs: Vec<AllocatorVec<u8, A>>,
pub buf_type: buffer::Type,
}

impl Arena {
impl Arena<allocator_api2::alloc::Global> {
/// Returns a new buffer manager instance
///
/// You usually do not need to use this directly.
Expand All @@ -27,6 +30,27 @@ impl Arena {
/// * `buf_type` - Type of the buffers
pub fn new(handle: Arc<Handle>, buf_type: buffer::Type) -> Self {
Arena {
allocator: allocator_api2::alloc::Global,
handle,
bufs: Vec::new(),
buf_type,
}
}
}

impl<A: Allocator + Clone> Arena<A> {
/// 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
///
/// * `dev` - Device handle to get its file descriptor
/// * `buf_type` - Type of the buffers
pub fn with_alloc(handle: Arc<Handle>, buf_type: buffer::Type, allocator: A) -> Self {
Arena {
allocator,
handle,
bufs: Vec::new(),
buf_type,
Expand Down Expand Up @@ -75,8 +99,9 @@ impl Arena {
}

// allocate the new user buffers
self.bufs.resize(v4l2_reqbufs.count as usize, Vec::new());
self.bufs.clear();
for i in 0..v4l2_reqbufs.count {
self.bufs.push(AllocatorVec::new_in(self.allocator.clone()));
let buf = &mut self.bufs[i as usize];
unsafe {
buf.resize(v4l2_fmt.fmt.pix.sizeimage as usize, 0);
Expand All @@ -102,7 +127,7 @@ impl Arena {
}
}

impl Drop for Arena {
impl<A: Allocator + Clone> Drop for Arena<A> {
fn drop(&mut self) {
if self.bufs.is_empty() {
// nothing to do
Expand Down
2 changes: 1 addition & 1 deletion src/io/userptr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub(crate) mod arena;

pub mod stream;
pub use stream::Stream;
pub use stream::{AllocStream, Stream};
31 changes: 23 additions & 8 deletions src/io/userptr/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::convert::TryInto;
use std::time::Duration;
use std::{io, mem, sync::Arc};

use allocator_api2::alloc::Allocator;

use crate::buffer::{Metadata, Type};
use crate::device::{Device, Handle};
use crate::io::traits::{CaptureStream, Stream as StreamTrait};
Expand All @@ -13,9 +15,9 @@ use crate::v4l_sys::*;
/// Stream of user buffers
///
/// An arena instance is used internally for buffer handling.
pub struct Stream {
pub struct AllocStream<A: Allocator + Clone> {
handle: Arc<Handle>,
arena: Arena,
arena: Arena<A>,
arena_index: usize,
buf_type: Type,
buf_meta: Vec<Metadata>,
Expand All @@ -24,6 +26,8 @@ pub struct Stream {
active: bool,
}

pub type Stream = AllocStream<allocator_api2::alloc::Global>;

impl Stream {
/// Returns a stream for frame capturing
///
Expand All @@ -45,16 +49,27 @@ impl Stream {
/// }
/// ```
pub fn new(dev: &Device, buf_type: Type) -> io::Result<Self> {
Stream::with_buffers(dev, buf_type, 4)
Stream::with_buffers_and_alloc(dev, buf_type, 4, allocator_api2::alloc::Global)
}

pub fn with_buffers(dev: &Device, buf_type: Type, buf_count: u32) -> io::Result<Self> {
let mut arena = Arena::new(dev.handle(), buf_type);
Stream::with_buffers_and_alloc(dev, buf_type, buf_count, allocator_api2::alloc::Global)
}
}

impl<A: Allocator + Clone> AllocStream<A> {
pub fn with_buffers_and_alloc(
dev: &Device,
buf_type: Type,
buf_count: u32,
allocator: A,
) -> io::Result<Self> {
let mut arena = Arena::with_alloc(dev.handle(), buf_type, allocator);
let count = arena.allocate(buf_count)?;
let mut buf_meta = Vec::new();
buf_meta.resize(count as usize, Metadata::default());

Ok(Stream {
Ok(AllocStream {
handle: dev.handle(),
arena,
arena_index: 0,
Expand Down Expand Up @@ -89,7 +104,7 @@ impl Stream {
}
}

impl Drop for Stream {
impl<A: Allocator + Clone> Drop for AllocStream<A> {
fn drop(&mut self) {
if let Err(e) = self.stop() {
if let Some(code) = e.raw_os_error() {
Expand All @@ -107,7 +122,7 @@ impl Drop for Stream {
}
}

impl StreamTrait for Stream {
impl<A: Allocator + Clone> StreamTrait for AllocStream<A> {
type Item = [u8];

fn start(&mut self) -> io::Result<()> {
Expand Down Expand Up @@ -139,7 +154,7 @@ impl StreamTrait for Stream {
}
}

impl<'a> CaptureStream<'a> for Stream {
impl<'a, A: Allocator + Clone> CaptureStream<'a> for AllocStream<A> {
fn queue(&mut self, index: usize) -> io::Result<()> {
let buf = &mut self.arena.bufs[index];
let mut v4l2_buf = v4l2_buffer {
Expand Down