diff --git a/Cargo.toml b/Cargo.toml index bb3fd17..c484729 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/src/buffer.rs b/src/buffer.rs index 7bdb2a3..050d3d9 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,4 +1,3 @@ -use bitflags::bitflags; use std::fmt; use crate::timestamp::Timestamp; @@ -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 { diff --git a/src/io/userptr/arena.rs b/src/io/userptr/arena.rs index 8f9c89c..d6b097f 100644 --- a/src/io/userptr/arena.rs +++ b/src/io/userptr/arena.rs @@ -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; @@ -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 { handle: Arc, - pub bufs: Vec>, + allocator: A, + pub bufs: Vec>, pub buf_type: buffer::Type, } -impl Arena { +impl Arena { /// Returns a new buffer manager instance /// /// You usually do not need to use this directly. @@ -27,6 +30,27 @@ impl Arena { /// * `buf_type` - Type of the buffers pub fn new(handle: Arc, buf_type: buffer::Type) -> Self { Arena { + allocator: allocator_api2::alloc::Global, + handle, + bufs: Vec::new(), + buf_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 + /// + /// * `dev` - Device handle to get its file descriptor + /// * `buf_type` - Type of the buffers + pub fn with_alloc(handle: Arc, buf_type: buffer::Type, allocator: A) -> Self { + Arena { + allocator, handle, bufs: Vec::new(), buf_type, @@ -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); @@ -102,7 +127,7 @@ impl Arena { } } -impl Drop for Arena { +impl Drop for Arena { fn drop(&mut self) { if self.bufs.is_empty() { // nothing to do diff --git a/src/io/userptr/mod.rs b/src/io/userptr/mod.rs index 8195783..5f82568 100644 --- a/src/io/userptr/mod.rs +++ b/src/io/userptr/mod.rs @@ -1,4 +1,4 @@ pub(crate) mod arena; pub mod stream; -pub use stream::Stream; +pub use stream::{AllocStream, Stream}; diff --git a/src/io/userptr/stream.rs b/src/io/userptr/stream.rs index 5261806..2c6933d 100644 --- a/src/io/userptr/stream.rs +++ b/src/io/userptr/stream.rs @@ -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}; @@ -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 { handle: Arc, - arena: Arena, + arena: Arena, arena_index: usize, buf_type: Type, buf_meta: Vec, @@ -24,6 +26,8 @@ pub struct Stream { active: bool, } +pub type Stream = AllocStream; + impl Stream { /// Returns a stream for frame capturing /// @@ -45,16 +49,27 @@ impl Stream { /// } /// ``` pub fn new(dev: &Device, buf_type: Type) -> io::Result { - 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 { - let mut arena = Arena::new(dev.handle(), buf_type); + Stream::with_buffers_and_alloc(dev, buf_type, buf_count, allocator_api2::alloc::Global) + } +} + +impl AllocStream { + pub fn with_buffers_and_alloc( + dev: &Device, + buf_type: Type, + buf_count: u32, + allocator: A, + ) -> io::Result { + 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, @@ -89,7 +104,7 @@ impl Stream { } } -impl Drop for Stream { +impl Drop for AllocStream { fn drop(&mut self) { if let Err(e) = self.stop() { if let Some(code) = e.raw_os_error() { @@ -107,7 +122,7 @@ impl Drop for Stream { } } -impl StreamTrait for Stream { +impl StreamTrait for AllocStream { type Item = [u8]; fn start(&mut self) -> io::Result<()> { @@ -139,7 +154,7 @@ impl StreamTrait for Stream { } } -impl<'a> CaptureStream<'a> for Stream { +impl<'a, A: Allocator + Clone> CaptureStream<'a> for AllocStream { fn queue(&mut self, index: usize) -> io::Result<()> { let buf = &mut self.arena.bufs[index]; let mut v4l2_buf = v4l2_buffer {