mod decoder_cmd;
mod dqbuf;
mod encoder_cmd;
mod enum_fmt;
mod expbuf;
mod frameintervals;
mod framesizes;
mod g_audio;
mod g_dv_timings;
mod g_ext_ctrls;
mod g_fmt;
mod g_input;
mod g_jpegcomp;
mod g_parm;
mod g_selection;
mod mmap;
mod qbuf;
mod querybuf;
mod querycap;
mod queryctrl;
mod reqbufs;
mod request;
mod streamon;
mod subscribe_event;
pub use decoder_cmd::*;
pub use dqbuf::*;
pub use encoder_cmd::*;
pub use enum_fmt::*;
pub use expbuf::*;
pub use frameintervals::*;
pub use framesizes::*;
pub use g_audio::*;
pub use g_dv_timings::*;
pub use g_ext_ctrls::*;
pub use g_fmt::*;
pub use g_input::*;
pub use g_jpegcomp::*;
pub use g_parm::*;
pub use g_selection::*;
pub use mmap::*;
pub use qbuf::*;
pub use querybuf::*;
pub use querycap::*;
pub use queryctrl::*;
pub use reqbufs::*;
pub use request::*;
pub use streamon::*;
pub use subscribe_event::*;
use std::convert::Infallible;
use std::convert::TryFrom;
use std::ffi::CStr;
use std::ffi::FromBytesWithNulError;
use std::fmt::Debug;
use std::ops::Deref;
use std::ops::DerefMut;
use bitflags::bitflags;
use enumn::N;
use nix::errno::Errno;
use thiserror::Error;
use crate::bindings;
use crate::memory::DmaBuf;
use crate::memory::Memory;
use crate::memory::MemoryType;
use crate::memory::Mmap;
use crate::memory::UserPtr;
use crate::Colorspace;
use crate::PixelFormat;
use crate::Quantization;
use crate::QueueDirection;
use crate::QueueType;
use crate::XferFunc;
use crate::YCbCrEncoding;
fn string_from_cstr(c_str: &[u8]) -> Result<String, FromBytesWithNulError> {
let slice = match c_str.iter().position(|x| *x == b'\0') {
None => c_str,
Some(pos) => &c_str[..pos + 1],
};
Ok(CStr::from_bytes_with_nul(slice)?
.to_string_lossy()
.into_owned())
}
pub trait IntoErrno {
fn into_errno(self) -> i32;
}
impl<T> IntoErrno for T
where
T: Into<Errno>,
{
fn into_errno(self) -> i32 {
self.into() as i32
}
}
#[derive(Debug, Error)]
pub enum IoctlConvertError<IE: Debug, CE: Debug> {
#[error("error during ioctl: {0}")]
IoctlError(#[from] IE),
#[error("error while converting ioctl result: {0}")]
ConversionError(CE),
}
impl<IE, CE> IntoErrno for IoctlConvertError<IE, CE>
where
IE: Debug + Into<Errno>,
CE: Debug,
{
fn into_errno(self) -> i32 {
match self {
IoctlConvertError::IoctlError(e) => e.into_errno(),
IoctlConvertError::ConversionError(_) => Errno::EINVAL as i32,
}
}
}
#[allow(type_alias_bounds)]
pub type IoctlConvertResult<O, IE, CE> = Result<O, IoctlConvertError<IE, CE>>;
fn ioctl_and_convert<I, O, IE>(res: Result<I, IE>) -> IoctlConvertResult<O, IE, O::Error>
where
IE: std::fmt::Debug,
O: TryFrom<I>,
O::Error: std::fmt::Debug,
{
res.map_err(IoctlConvertError::IoctlError)
.and_then(|o| O::try_from(o).map_err(IoctlConvertError::ConversionError))
}
pub struct UncheckedV4l2Buffer(pub bindings::v4l2_buffer, pub Option<V4l2BufferPlanes>);
impl UncheckedV4l2Buffer {
pub fn new_for_querybuf(queue: QueueType, index: Option<u32>) -> Self {
let multiplanar = queue.is_multiplanar();
UncheckedV4l2Buffer(
bindings::v4l2_buffer {
index: index.unwrap_or_default(),
type_: queue as u32,
length: if multiplanar {
bindings::VIDEO_MAX_PLANES
} else {
Default::default()
},
..Default::default()
},
if multiplanar {
Some(Default::default())
} else {
None
},
)
}
}
impl TryFrom<UncheckedV4l2Buffer> for () {
type Error = Infallible;
fn try_from(_: UncheckedV4l2Buffer) -> Result<Self, Self::Error> {
Ok(())
}
}
impl From<V4l2Buffer> for UncheckedV4l2Buffer {
fn from(buffer: V4l2Buffer) -> Self {
let is_multiplanar = buffer.queue().is_multiplanar();
Self(
buffer.buffer,
if is_multiplanar {
Some(buffer.planes)
} else {
None
},
)
}
}
impl AsMut<bindings::v4l2_buffer> for UncheckedV4l2Buffer {
fn as_mut(&mut self) -> &mut bindings::v4l2_buffer {
match (QueueType::n(self.0.type_), &mut self.1) {
(Some(queue), Some(planes)) if queue.is_multiplanar() => {
self.0.m.planes = planes.as_mut_ptr()
}
_ => (),
}
&mut self.0
}
}
type V4l2BufferPlanes = [bindings::v4l2_plane; bindings::VIDEO_MAX_PLANES as usize];
bitflags! {
#[derive(Clone, Copy, Debug, Default)]
pub struct BufferFlags: u32 {
const MAPPED = bindings::V4L2_BUF_FLAG_MAPPED;
const QUEUED = bindings::V4L2_BUF_FLAG_QUEUED;
const DONE = bindings::V4L2_BUF_FLAG_DONE;
const ERROR = bindings::V4L2_BUF_FLAG_ERROR;
const KEYFRAME = bindings::V4L2_BUF_FLAG_KEYFRAME;
const PFRAME = bindings::V4L2_BUF_FLAG_PFRAME;
const BFRAME = bindings::V4L2_BUF_FLAG_BFRAME;
const TIMECODE = bindings::V4L2_BUF_FLAG_TIMECODE;
const PREPARED = bindings::V4L2_BUF_FLAG_PREPARED;
const NO_CACHE_INVALIDATE = bindings::V4L2_BUF_FLAG_NO_CACHE_CLEAN;
const NO_CACHE_CLEAN = bindings::V4L2_BUF_FLAG_NO_CACHE_INVALIDATE;
const LAST = bindings::V4L2_BUF_FLAG_LAST;
const TIMESTAMP_MONOTONIC = bindings::V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
const TIMESTAMP_COPY = bindings::V4L2_BUF_FLAG_TIMESTAMP_COPY;
const TSTAMP_SRC_EOF = bindings::V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
const TSTAMP_SRC_SOE = bindings::V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
const REQUEST_FD = bindings::V4L2_BUF_FLAG_REQUEST_FD;
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, N)]
#[repr(u32)]
pub enum BufferField {
#[default]
Any = bindings::v4l2_field_V4L2_FIELD_ANY,
None = bindings::v4l2_field_V4L2_FIELD_NONE,
Top = bindings::v4l2_field_V4L2_FIELD_TOP,
Interlaced = bindings::v4l2_field_V4L2_FIELD_INTERLACED,
SeqTb = bindings::v4l2_field_V4L2_FIELD_SEQ_TB,
SeqBt = bindings::v4l2_field_V4L2_FIELD_SEQ_BT,
Alternate = bindings::v4l2_field_V4L2_FIELD_ALTERNATE,
InterlacedTb = bindings::v4l2_field_V4L2_FIELD_INTERLACED_TB,
InterlacedBt = bindings::v4l2_field_V4L2_FIELD_INTERLACED_BT,
}
#[derive(Debug, Error)]
pub enum V4l2BufferResizePlanesError {
#[error("zero planes requested")]
ZeroPlanesRequested,
#[error("buffer is single planar and can only accomodate one plane")]
SinglePlanar,
#[error("more than VIDEO_MAX_PLANES have been requested")]
TooManyPlanes,
}
#[derive(Clone)]
#[repr(C)]
pub struct V4l2Buffer {
buffer: bindings::v4l2_buffer,
planes: V4l2BufferPlanes,
}
unsafe impl Send for V4l2Buffer {}
unsafe impl Sync for V4l2Buffer {}
impl Debug for V4l2Buffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("V4l2Buffer")
.field("index", &self.index())
.field("flags", &self.flags())
.field("sequence", &self.sequence())
.finish()
}
}
impl V4l2Buffer {
pub fn new(queue: QueueType, index: u32, memory: MemoryType) -> Self {
Self {
buffer: bindings::v4l2_buffer {
index,
type_: queue as u32,
memory: memory as u32,
length: if queue.is_multiplanar() {
1
} else {
Default::default()
},
..Default::default()
},
planes: Default::default(),
}
}
pub fn index(&self) -> u32 {
self.buffer.index
}
pub fn queue(&self) -> QueueType {
QueueType::n(self.buffer.type_).unwrap()
}
pub fn memory(&self) -> MemoryType {
MemoryType::n(self.buffer.memory).unwrap()
}
pub fn flags(&self) -> BufferFlags {
BufferFlags::from_bits_truncate(self.buffer.flags)
}
pub fn set_flags(&mut self, flags: BufferFlags) {
self.buffer.flags = flags.bits();
}
pub fn add_flags(&mut self, flags: BufferFlags) {
self.set_flags(self.flags() | flags);
}
pub fn clear_flags(&mut self, flags: BufferFlags) {
self.set_flags(self.flags() - flags);
}
pub fn field(&self) -> BufferField {
BufferField::n(self.buffer.field).unwrap()
}
pub fn set_field(&mut self, field: BufferField) {
self.buffer.field = field as u32;
}
pub fn is_last(&self) -> bool {
self.flags().contains(BufferFlags::LAST)
}
pub fn has_error(&self) -> bool {
self.flags().contains(BufferFlags::ERROR)
}
pub fn timestamp(&self) -> bindings::timeval {
self.buffer.timestamp
}
pub fn set_timestamp(&mut self, timestamp: bindings::timeval) {
self.buffer.timestamp = timestamp;
}
pub fn sequence(&self) -> u32 {
self.buffer.sequence
}
pub fn set_sequence(&mut self, sequence: u32) {
self.buffer.sequence = sequence;
}
pub fn num_planes(&self) -> usize {
if self.queue().is_multiplanar() {
self.buffer.length as usize
} else {
1
}
}
pub fn set_num_planes(&mut self, num_planes: usize) -> Result<(), V4l2BufferResizePlanesError> {
match (num_planes, self.queue().is_multiplanar()) {
(0, _) => Err(V4l2BufferResizePlanesError::ZeroPlanesRequested),
(n, _) if n > bindings::VIDEO_MAX_PLANES as usize => {
Err(V4l2BufferResizePlanesError::TooManyPlanes)
}
(1, false) => Ok(()),
(_, false) => Err(V4l2BufferResizePlanesError::SinglePlanar),
(num_planes, true) => {
for plane in &mut self.planes[num_planes..self.buffer.length as usize] {
*plane = Default::default();
}
self.buffer.length = num_planes as u32;
Ok(())
}
}
}
pub fn get_first_plane(&self) -> V4l2PlaneAccessor {
self.planes_iter().next().unwrap()
}
pub fn get_first_plane_mut(&mut self) -> V4l2PlaneMutAccessor {
self.planes_iter_mut().next().unwrap()
}
pub fn as_v4l2_buffer(&self) -> &bindings::v4l2_buffer {
&self.buffer
}
pub fn as_v4l2_planes(&self) -> &[bindings::v4l2_plane] {
let planes_upper = if self.queue().is_multiplanar() {
self.buffer.length as usize
} else {
0
};
&self.planes[0..planes_upper]
}
pub fn as_mut_ptr(&mut self) -> *mut bindings::v4l2_buffer {
if self.queue().is_multiplanar() && self.buffer.length > 0 {
self.buffer.m.planes = self.planes.as_mut_ptr();
}
&mut self.buffer as *mut _
}
pub fn planes_iter(&self) -> impl Iterator<Item = V4l2PlaneAccessor> {
let multiplanar = self.queue().is_multiplanar();
let planes_iter = self.as_v4l2_planes().iter();
std::iter::once(V4l2PlaneAccessor::new_single_planar(&self.buffer))
.chain(planes_iter.map(V4l2PlaneAccessor::new_multi_planar))
.skip(if multiplanar { 1 } else { 0 })
}
pub fn planes_iter_mut(&mut self) -> impl Iterator<Item = V4l2PlaneMutAccessor> {
let multiplanar = self.queue().is_multiplanar();
let planes_upper = if multiplanar {
self.buffer.length as usize
} else {
0
};
let planes_iter = self.planes[0..planes_upper].iter_mut();
std::iter::once(V4l2PlaneMutAccessor::new_single_planar(&mut self.buffer))
.chain(planes_iter.map(V4l2PlaneMutAccessor::new_multi_planar))
.skip(if multiplanar { 1 } else { 0 })
}
unsafe fn planes_iter_with_backing<M: Memory>(
&self,
) -> impl Iterator<Item = V4l2PlaneAccessorWithRawBacking<M>> {
let is_multiplanar = self.queue().is_multiplanar();
let planes_length = if is_multiplanar {
self.buffer.length as usize
} else {
0
};
let planes = &self.planes[0..planes_length];
std::iter::once(V4l2PlaneAccessorWithRawBacking::new_single_planar(
&self.buffer,
))
.chain(
planes
.iter()
.map(|p| V4l2PlaneAccessorWithRawBacking::new_multi_planar(p)),
)
.skip(if self.queue().is_multiplanar() { 1 } else { 0 })
}
pub fn planes_with_backing_iter(
&self,
) -> V4l2PlanesWithBacking<
impl Iterator<Item = V4l2PlaneAccessorWithRawBacking<Mmap>>,
impl Iterator<Item = V4l2PlaneAccessorWithRawBacking<UserPtr>>,
impl Iterator<Item = V4l2PlaneAccessorWithRawBacking<DmaBuf>>,
> {
match self.memory() {
MemoryType::Mmap => {
V4l2PlanesWithBacking::Mmap(unsafe { self.planes_iter_with_backing() })
}
MemoryType::UserPtr => {
V4l2PlanesWithBacking::UserPtr(unsafe { self.planes_iter_with_backing() })
}
MemoryType::DmaBuf => {
V4l2PlanesWithBacking::DmaBuf(unsafe { self.planes_iter_with_backing() })
}
MemoryType::Overlay => V4l2PlanesWithBacking::Overlay,
}
}
unsafe fn planes_iter_with_backing_mut<M: Memory>(
&mut self,
) -> impl Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<M>> {
let is_multiplanar = self.queue().is_multiplanar();
let planes_length = if is_multiplanar {
self.buffer.length as usize
} else {
0
};
let planes = &mut self.planes[0..planes_length];
std::iter::once(V4l2PlaneMutAccessorWithRawBacking::new_single_planar(
&mut self.buffer,
))
.chain(
planes
.iter_mut()
.map(|p| V4l2PlaneMutAccessorWithRawBacking::new_multi_planar(p)),
)
.skip(if is_multiplanar { 1 } else { 0 })
}
pub fn planes_with_backing_iter_mut(
&mut self,
) -> V4l2PlanesWithBackingMut<
impl Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<Mmap>>,
impl Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<UserPtr>>,
impl Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<DmaBuf>>,
> {
match self.memory() {
MemoryType::Mmap => {
V4l2PlanesWithBackingMut::Mmap(unsafe { self.planes_iter_with_backing_mut() })
}
MemoryType::UserPtr => {
V4l2PlanesWithBackingMut::UserPtr(unsafe { self.planes_iter_with_backing_mut() })
}
MemoryType::DmaBuf => {
V4l2PlanesWithBackingMut::DmaBuf(unsafe { self.planes_iter_with_backing_mut() })
}
MemoryType::Overlay => V4l2PlanesWithBackingMut::Overlay,
}
}
}
pub struct V4l2PlaneAccessor<'a> {
pub bytesused: &'a u32,
pub length: &'a u32,
pub data_offset: Option<&'a u32>,
}
impl<'a> V4l2PlaneAccessor<'a> {
fn new_single_planar(buffer: &'a bindings::v4l2_buffer) -> Self {
Self {
bytesused: &buffer.bytesused,
length: &buffer.length,
data_offset: None,
}
}
fn new_multi_planar(plane: &'a bindings::v4l2_plane) -> Self {
Self {
bytesused: &plane.bytesused,
length: &plane.length,
data_offset: Some(&plane.data_offset),
}
}
}
pub struct V4l2PlaneMutAccessor<'a> {
pub bytesused: &'a mut u32,
pub length: &'a mut u32,
pub data_offset: Option<&'a mut u32>,
}
impl<'a> V4l2PlaneMutAccessor<'a> {
fn new_single_planar(buffer: &'a mut bindings::v4l2_buffer) -> Self {
Self {
bytesused: &mut buffer.bytesused,
length: &mut buffer.length,
data_offset: None,
}
}
fn new_multi_planar(plane: &'a mut bindings::v4l2_plane) -> Self {
Self {
bytesused: &mut plane.bytesused,
length: &mut plane.length,
data_offset: Some(&mut plane.data_offset),
}
}
}
pub struct V4l2PlaneAccessorWithRawBacking<'a, M: Memory> {
data: V4l2PlaneAccessor<'a>,
backing: &'a M::RawBacking,
}
impl<'a, M: Memory> Deref for V4l2PlaneAccessorWithRawBacking<'a, M> {
type Target = V4l2PlaneAccessor<'a>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<'a, M: Memory> V4l2PlaneAccessorWithRawBacking<'a, M> {
pub unsafe fn new_single_planar(buffer: &'a bindings::v4l2_buffer) -> Self {
Self {
data: V4l2PlaneAccessor::new_single_planar(buffer),
backing: M::get_single_planar_buffer_backing(&buffer.m),
}
}
pub unsafe fn new_multi_planar(plane: &'a bindings::v4l2_plane) -> Self {
Self {
data: V4l2PlaneAccessor::new_multi_planar(plane),
backing: M::get_plane_buffer_backing(&plane.m),
}
}
}
impl<'a> V4l2PlaneAccessorWithRawBacking<'a, Mmap> {
pub fn mem_offset(&self) -> <Mmap as Memory>::RawBacking {
*self.backing
}
}
impl<'a> V4l2PlaneAccessorWithRawBacking<'a, UserPtr> {
pub fn userptr(&self) -> <UserPtr as Memory>::RawBacking {
*self.backing
}
}
impl<'a> V4l2PlaneAccessorWithRawBacking<'a, DmaBuf> {
pub fn fd(&self) -> <DmaBuf as Memory>::RawBacking {
*self.backing
}
}
pub enum V4l2PlanesWithBacking<
'a,
M: Iterator<Item = V4l2PlaneAccessorWithRawBacking<'a, Mmap>>,
U: Iterator<Item = V4l2PlaneAccessorWithRawBacking<'a, UserPtr>>,
D: Iterator<Item = V4l2PlaneAccessorWithRawBacking<'a, DmaBuf>>,
> {
Mmap(M),
UserPtr(U),
DmaBuf(D),
Overlay,
}
pub struct V4l2PlaneMutAccessorWithRawBacking<'a, M: Memory> {
data: V4l2PlaneMutAccessor<'a>,
backing: &'a mut M::RawBacking,
}
impl<'a, M: Memory> Deref for V4l2PlaneMutAccessorWithRawBacking<'a, M> {
type Target = V4l2PlaneMutAccessor<'a>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<'a, M: Memory> DerefMut for V4l2PlaneMutAccessorWithRawBacking<'a, M> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<'a, M: Memory> V4l2PlaneMutAccessorWithRawBacking<'a, M> {
pub unsafe fn new_single_planar(buffer: &'a mut bindings::v4l2_buffer) -> Self {
Self {
data: V4l2PlaneMutAccessor {
bytesused: &mut buffer.bytesused,
length: &mut buffer.length,
data_offset: None,
},
backing: M::get_single_planar_buffer_backing_mut(&mut buffer.m),
}
}
pub unsafe fn new_multi_planar(plane: &'a mut bindings::v4l2_plane) -> Self {
Self {
data: V4l2PlaneMutAccessor {
bytesused: &mut plane.bytesused,
length: &mut plane.length,
data_offset: Some(&mut plane.data_offset),
},
backing: M::get_plane_buffer_backing_mut(&mut plane.m),
}
}
}
impl<'a> V4l2PlaneMutAccessorWithRawBacking<'a, Mmap> {
pub fn mem_offset(&self) -> <Mmap as Memory>::RawBacking {
*self.backing
}
pub fn set_mem_offset(&mut self, mem_offset: <Mmap as Memory>::RawBacking) {
*self.backing = mem_offset;
}
}
impl<'a> V4l2PlaneMutAccessorWithRawBacking<'a, UserPtr> {
pub fn userptr(&self) -> <UserPtr as Memory>::RawBacking {
*self.backing
}
pub fn set_userptr(&mut self, userptr: <UserPtr as Memory>::RawBacking) {
*self.backing = userptr;
}
}
impl<'a> V4l2PlaneMutAccessorWithRawBacking<'a, DmaBuf> {
pub fn fd(&self) -> <DmaBuf as Memory>::RawBacking {
*self.backing
}
pub fn set_fd(&mut self, fd: <DmaBuf as Memory>::RawBacking) {
*self.backing = fd;
}
}
pub enum V4l2PlanesWithBackingMut<
'a,
M: Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<'a, Mmap>>,
U: Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<'a, UserPtr>>,
D: Iterator<Item = V4l2PlaneMutAccessorWithRawBacking<'a, DmaBuf>>,
> {
Mmap(M),
UserPtr(U),
DmaBuf(D),
Overlay,
}
#[derive(Debug, Error)]
pub enum V4l2BufferFromError {
#[error("unknown queue type {0}")]
UnknownQueueType(u32),
#[error("unknown memory type {0}")]
UnknownMemoryType(u32),
#[error("invalid number of planes {0}")]
InvalidNumberOfPlanes(u32),
#[error("plane {0} has bytesused field larger than its length ({1} > {2})")]
PlaneSizeOverflow(usize, u32, u32),
#[error("plane {0} has data_offset field larger or equal to its bytesused ({1} >= {2})")]
InvalidDataOffset(usize, u32, u32),
}
impl TryFrom<UncheckedV4l2Buffer> for V4l2Buffer {
type Error = V4l2BufferFromError;
fn try_from(buffer: UncheckedV4l2Buffer) -> Result<Self, Self::Error> {
let v4l2_buf = buffer.0;
let queue = QueueType::n(v4l2_buf.type_)
.ok_or(V4l2BufferFromError::UnknownQueueType(v4l2_buf.type_))?;
MemoryType::n(v4l2_buf.memory)
.ok_or(V4l2BufferFromError::UnknownMemoryType(v4l2_buf.memory))?;
let v4l2_planes = buffer.1.unwrap_or_default();
if queue.is_multiplanar() {
if v4l2_buf.length >= bindings::VIDEO_MAX_PLANES {
return Err(V4l2BufferFromError::InvalidNumberOfPlanes(v4l2_buf.length));
}
for (i, plane) in v4l2_planes[0..v4l2_buf.length as usize].iter().enumerate() {
if plane.bytesused > plane.length {
return Err(V4l2BufferFromError::PlaneSizeOverflow(
i,
plane.bytesused,
plane.length,
));
}
let bytesused = if plane.bytesused != 0 {
plane.bytesused
} else {
plane.length
};
if plane.data_offset != 0 && plane.data_offset >= bytesused {
return Err(V4l2BufferFromError::InvalidDataOffset(
i,
plane.data_offset,
bytesused,
));
}
}
} else if v4l2_buf.bytesused > v4l2_buf.length {
return Err(V4l2BufferFromError::PlaneSizeOverflow(
0,
v4l2_buf.bytesused,
v4l2_buf.length,
));
}
Ok(Self {
buffer: v4l2_buf,
planes: v4l2_planes,
})
}
}
#[derive(Clone)]
#[repr(transparent)]
pub struct V4l2MplaneFormat(bindings::v4l2_format);
impl AsRef<bindings::v4l2_format> for V4l2MplaneFormat {
fn as_ref(&self) -> &bindings::v4l2_format {
&self.0
}
}
impl AsRef<bindings::v4l2_pix_format_mplane> for V4l2MplaneFormat {
fn as_ref(&self) -> &bindings::v4l2_pix_format_mplane {
unsafe { &self.0.fmt.pix_mp }
}
}
#[derive(Debug, Error)]
pub enum V4l2MplaneFormatFromError {
#[error("format is not multi-planar")]
NotMultiPlanar,
#[error("invalid field type {0}")]
InvalidField(u32),
#[error("invalid colorspace {0}")]
InvalidColorSpace(u32),
#[error("invalid number of planes {0}")]
InvalidPlanesNumber(u8),
#[error("invalid YCbCr encoding {0}")]
InvalidYCbCr(u8),
#[error("invalid quantization {0}")]
InvalidQuantization(u8),
#[error("invalid Xfer func {0}")]
InvalidXferFunc(u8),
}
impl TryFrom<bindings::v4l2_format> for V4l2MplaneFormat {
type Error = V4l2MplaneFormatFromError;
fn try_from(format: bindings::v4l2_format) -> Result<Self, Self::Error> {
if !matches!(
QueueType::n(format.type_),
Some(QueueType::VideoCaptureMplane) | Some(QueueType::VideoOutputMplane)
) {
return Err(V4l2MplaneFormatFromError::NotMultiPlanar);
}
let pix_mp = unsafe { &format.fmt.pix_mp };
if pix_mp.num_planes == 0 || pix_mp.num_planes > bindings::VIDEO_MAX_PLANES as u8 {
return Err(V4l2MplaneFormatFromError::InvalidPlanesNumber(
pix_mp.num_planes,
));
}
let _ = BufferField::n(pix_mp.field)
.ok_or(V4l2MplaneFormatFromError::InvalidField(pix_mp.field))?;
let _ = Colorspace::n(pix_mp.colorspace).ok_or(
V4l2MplaneFormatFromError::InvalidColorSpace(pix_mp.colorspace),
)?;
let ycbcr_enc = unsafe { pix_mp.__bindgen_anon_1.ycbcr_enc };
let _ = YCbCrEncoding::n(ycbcr_enc as u32)
.ok_or(V4l2MplaneFormatFromError::InvalidYCbCr(ycbcr_enc));
let _ = Quantization::n(pix_mp.quantization as u32).ok_or(
V4l2MplaneFormatFromError::InvalidQuantization(pix_mp.quantization),
)?;
let _ = XferFunc::n(pix_mp.xfer_func as u32)
.ok_or(V4l2MplaneFormatFromError::InvalidXferFunc(pix_mp.xfer_func))?;
Ok(Self(format))
}
}
impl From<(QueueDirection, bindings::v4l2_pix_format_mplane)> for V4l2MplaneFormat {
fn from((direction, mut pix_mp): (QueueDirection, bindings::v4l2_pix_format_mplane)) -> Self {
pix_mp.field = BufferField::n(pix_mp.field).unwrap_or_default() as u32;
pix_mp.colorspace = Colorspace::n(pix_mp.colorspace).unwrap_or_default() as u32;
let ycbcr_enc = unsafe { pix_mp.__bindgen_anon_1.ycbcr_enc };
pix_mp.__bindgen_anon_1.ycbcr_enc =
YCbCrEncoding::n(ycbcr_enc as u32).unwrap_or_default() as u8;
pix_mp.quantization = Quantization::n(pix_mp.quantization as u32).unwrap_or_default() as u8;
pix_mp.xfer_func = XferFunc::n(pix_mp.xfer_func as u32).unwrap_or_default() as u8;
Self(bindings::v4l2_format {
type_: QueueType::from_dir_and_class(direction, crate::QueueClass::VideoMplane) as u32,
fmt: bindings::v4l2_format__bindgen_ty_1 { pix_mp },
})
}
}
impl V4l2MplaneFormat {
pub fn direction(&self) -> QueueDirection {
QueueType::n(self.0.type_).unwrap().direction()
}
pub fn size(&self) -> (u32, u32) {
let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
(pix_mp.width, pix_mp.height)
}
pub fn pixelformat(&self) -> PixelFormat {
let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
PixelFormat::from_u32(pix_mp.pixelformat)
}
pub fn field(&self) -> BufferField {
let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
BufferField::n(pix_mp.field).unwrap()
}
pub fn colorspace(&self) -> Colorspace {
let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
Colorspace::n(pix_mp.colorspace).unwrap()
}
pub fn ycbcr_enc(&self) -> YCbCrEncoding {
let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
YCbCrEncoding::n(unsafe { pix_mp.__bindgen_anon_1.ycbcr_enc as u32 }).unwrap()
}
pub fn quantization(&self) -> Quantization {
let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
Quantization::n(pix_mp.quantization as u32).unwrap()
}
pub fn xfer_func(&self) -> XferFunc {
let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
XferFunc::n(pix_mp.xfer_func as u32).unwrap()
}
pub fn planes(&self) -> &[bindings::v4l2_plane_pix_format] {
let pix_mp: &bindings::v4l2_pix_format_mplane = self.as_ref();
&pix_mp.plane_fmt[0..pix_mp.num_planes.min(bindings::VIDEO_MAX_PLANES as u8) as usize]
}
}
#[cfg(test)]
mod tests {
use crate::{bindings, QueueType};
use super::UncheckedV4l2Buffer;
#[test]
fn test_string_from_cstr() {
use super::string_from_cstr;
assert_eq!(string_from_cstr(b"Hello\0"), Ok(String::from("Hello")));
assert_eq!(string_from_cstr(b"Hi\0lo"), Ok(String::from("Hi")));
assert_eq!(string_from_cstr(b"Hi\0lo\0"), Ok(String::from("Hi")));
assert_eq!(string_from_cstr(b"\0ello"), Ok(String::from("")));
match string_from_cstr(b"Hello") {
Err(_) => {}
Ok(_) => panic!(),
};
match string_from_cstr(b"") {
Err(_) => {}
Ok(_) => panic!(),
};
}
#[test]
fn test_unchecked_v4l2_buffer() {
let mut v4l2_buf = UncheckedV4l2Buffer::new_for_querybuf(QueueType::VideoCapture, Some(2));
assert_eq!(v4l2_buf.0.type_, QueueType::VideoCapture as u32);
assert_eq!(v4l2_buf.0.index, 2);
assert_eq!(v4l2_buf.0.length, 0);
assert!(v4l2_buf.1.is_none());
assert_eq!(unsafe { v4l2_buf.as_mut().m.planes }, std::ptr::null_mut());
let mut v4l2_buf =
UncheckedV4l2Buffer::new_for_querybuf(QueueType::VideoCaptureMplane, None);
assert_eq!(v4l2_buf.0.type_, QueueType::VideoCaptureMplane as u32);
assert_eq!(v4l2_buf.0.index, 0);
assert_eq!(v4l2_buf.0.length, bindings::VIDEO_MAX_PLANES);
assert!(v4l2_buf.1.is_some());
let planes_ptr = v4l2_buf.1.as_mut().map(|p| p.as_mut_ptr()).unwrap();
let v4l2_buf_ref = v4l2_buf.as_mut();
assert_eq!(unsafe { v4l2_buf_ref.m.planes }, planes_ptr);
}
}