From deebc70a996fa49edc4c3cad718d7bcf0c48fd89 Mon Sep 17 00:00:00 2001 From: Morten Lysgaard Date: Fri, 21 Jun 2024 13:14:35 +0200 Subject: [PATCH 1/2] buffer.rs: Use module trick to silence clippy warning for bad_bit_mask error --- src/buffer.rs | 88 +++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index 7bdb2a3..db8a836 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -30,49 +30,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 { From 4f1ee6326d5b4f0fa47266e42b8661c63b699ce9 Mon Sep 17 00:00:00 2001 From: Morten Lysgaard Date: Fri, 21 Jun 2024 13:22:59 +0200 Subject: [PATCH 2/2] Add support for custom allocators using allocator-api compatible api This is usefull for devices where there are specific alignment requirements of the memory being written to by the V4L subsystem. One example where this is the case is the CSI-camera support Nvidia Tegra devices. --- Cargo.toml | 1 + src/buffer.rs | 1 - src/io/userptr/arena.rs | 35 ++++++++++++++++++++++++++++++----- src/io/userptr/mod.rs | 2 +- src/io/userptr/stream.rs | 31 +++++++++++++++++++++++-------- 5 files changed, 55 insertions(+), 15 deletions(-) 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 db8a836..050d3d9 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,4 +1,3 @@ -use bitflags::bitflags; use std::fmt; use crate::timestamp::Timestamp; 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 {