mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-09-21 23:13:21 +08:00
chore: vendor trimmed v4l2r capture crate
This commit is contained in:
300
libs/v4l2r/src/ioctl/g_fmt.rs
Normal file
300
libs/v4l2r/src/ioctl/g_fmt.rs
Normal file
@@ -0,0 +1,300 @@
|
||||
//! Safe wrapper for the `VIDIOC_(G|S|TRY)_FMT` ioctls.
|
||||
use nix::errno::Errno;
|
||||
use std::convert::{From, Into, TryFrom, TryInto};
|
||||
use std::default::Default;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::bindings;
|
||||
use crate::bindings::v4l2_format;
|
||||
use crate::Format;
|
||||
use crate::FormatConversionError;
|
||||
use crate::PlaneLayout;
|
||||
use crate::QueueType;
|
||||
|
||||
impl TryFrom<(QueueType, &Format)> for v4l2_format {
|
||||
type Error = FormatConversionError;
|
||||
|
||||
fn try_from((queue, format): (QueueType, &Format)) -> Result<Self, Self::Error> {
|
||||
Ok(v4l2_format {
|
||||
type_: queue as u32,
|
||||
fmt: match queue {
|
||||
QueueType::VideoCaptureMplane | QueueType::VideoOutputMplane => {
|
||||
bindings::v4l2_format__bindgen_ty_1 {
|
||||
pix_mp: {
|
||||
if format.plane_fmt.len() > bindings::VIDEO_MAX_PLANES as usize {
|
||||
return Err(Self::Error::TooManyPlanes(format.plane_fmt.len()));
|
||||
}
|
||||
|
||||
let mut pix_mp = bindings::v4l2_pix_format_mplane {
|
||||
width: format.width,
|
||||
height: format.height,
|
||||
pixelformat: format.pixelformat.into(),
|
||||
num_planes: format.plane_fmt.len() as u8,
|
||||
plane_fmt: Default::default(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for (plane, v4l2_plane) in
|
||||
format.plane_fmt.iter().zip(pix_mp.plane_fmt.iter_mut())
|
||||
{
|
||||
*v4l2_plane = plane.into();
|
||||
}
|
||||
|
||||
pix_mp
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => bindings::v4l2_format__bindgen_ty_1 {
|
||||
pix: {
|
||||
if format.plane_fmt.len() > 1 {
|
||||
return Err(Self::Error::TooManyPlanes(format.plane_fmt.len()));
|
||||
}
|
||||
|
||||
let (bytesperline, sizeimage) = if !format.plane_fmt.is_empty() {
|
||||
(
|
||||
format.plane_fmt[0].bytesperline,
|
||||
format.plane_fmt[0].sizeimage,
|
||||
)
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
bindings::v4l2_pix_format {
|
||||
width: format.width,
|
||||
height: format.height,
|
||||
pixelformat: format.pixelformat.into(),
|
||||
bytesperline,
|
||||
sizeimage,
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlaneLayout> for bindings::v4l2_plane_pix_format {
|
||||
fn from(plane: &PlaneLayout) -> Self {
|
||||
bindings::v4l2_plane_pix_format {
|
||||
sizeimage: plane.sizeimage,
|
||||
bytesperline: plane.bytesperline,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
mod ioctl {
|
||||
use crate::bindings::v4l2_format;
|
||||
nix::ioctl_readwrite!(vidioc_g_fmt, b'V', 4, v4l2_format);
|
||||
nix::ioctl_readwrite!(vidioc_s_fmt, b'V', 5, v4l2_format);
|
||||
nix::ioctl_readwrite!(vidioc_try_fmt, b'V', 64, v4l2_format);
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GFmtError {
|
||||
#[error("error while converting from V4L2 format")]
|
||||
FromV4L2FormatConversionError,
|
||||
#[error("invalid buffer type requested")]
|
||||
InvalidBufferType,
|
||||
#[error("unexpected ioctl error: {0}")]
|
||||
IoctlError(nix::Error),
|
||||
}
|
||||
|
||||
impl From<GFmtError> for Errno {
|
||||
fn from(err: GFmtError) -> Self {
|
||||
match err {
|
||||
GFmtError::FromV4L2FormatConversionError => Errno::EINVAL,
|
||||
GFmtError::InvalidBufferType => Errno::EINVAL,
|
||||
GFmtError::IoctlError(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe wrapper around the `VIDIOC_G_FMT` ioctl.
|
||||
pub fn g_fmt<O: TryFrom<v4l2_format>>(fd: &impl AsRawFd, queue: QueueType) -> Result<O, GFmtError> {
|
||||
let mut fmt = v4l2_format {
|
||||
type_: queue as u32,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
match unsafe { ioctl::vidioc_g_fmt(fd.as_raw_fd(), &mut fmt) } {
|
||||
Ok(_) => Ok(fmt
|
||||
.try_into()
|
||||
.map_err(|_| GFmtError::FromV4L2FormatConversionError)?),
|
||||
Err(Errno::EINVAL) => Err(GFmtError::InvalidBufferType),
|
||||
Err(e) => Err(GFmtError::IoctlError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SFmtError {
|
||||
#[error("error while converting from V4L2 format")]
|
||||
FromV4L2FormatConversionError,
|
||||
#[error("error while converting to V4L2 format")]
|
||||
ToV4L2FormatConversionError,
|
||||
#[error("invalid buffer type requested")]
|
||||
InvalidBufferType,
|
||||
#[error("device currently busy")]
|
||||
DeviceBusy,
|
||||
#[error("ioctl error: {0}")]
|
||||
IoctlError(nix::Error),
|
||||
}
|
||||
|
||||
impl From<SFmtError> for Errno {
|
||||
fn from(err: SFmtError) -> Self {
|
||||
match err {
|
||||
SFmtError::FromV4L2FormatConversionError => Errno::EINVAL,
|
||||
SFmtError::ToV4L2FormatConversionError => Errno::EINVAL,
|
||||
SFmtError::InvalidBufferType => Errno::EINVAL,
|
||||
SFmtError::DeviceBusy => Errno::EBUSY,
|
||||
SFmtError::IoctlError(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe wrapper around the `VIDIOC_S_FMT` ioctl.
|
||||
pub fn s_fmt<I: TryInto<v4l2_format>, O: TryFrom<v4l2_format>>(
|
||||
fd: &mut impl AsRawFd,
|
||||
format: I,
|
||||
) -> Result<O, SFmtError> {
|
||||
let mut fmt: v4l2_format = format
|
||||
.try_into()
|
||||
.map_err(|_| SFmtError::ToV4L2FormatConversionError)?;
|
||||
|
||||
match unsafe { ioctl::vidioc_s_fmt(fd.as_raw_fd(), &mut fmt) } {
|
||||
Ok(_) => Ok(fmt
|
||||
.try_into()
|
||||
.map_err(|_| SFmtError::FromV4L2FormatConversionError)?),
|
||||
Err(Errno::EINVAL) => Err(SFmtError::InvalidBufferType),
|
||||
Err(Errno::EBUSY) => Err(SFmtError::DeviceBusy),
|
||||
Err(e) => Err(SFmtError::IoctlError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TryFmtError {
|
||||
#[error("error while converting from V4L2 format")]
|
||||
FromV4L2FormatConversionError,
|
||||
#[error("error while converting to V4L2 format")]
|
||||
ToV4L2FormatConversionError,
|
||||
#[error("invalid buffer type requested")]
|
||||
InvalidBufferType,
|
||||
#[error("ioctl error: {0}")]
|
||||
IoctlError(nix::Error),
|
||||
}
|
||||
|
||||
impl From<TryFmtError> for Errno {
|
||||
fn from(err: TryFmtError) -> Self {
|
||||
match err {
|
||||
TryFmtError::FromV4L2FormatConversionError => Errno::EINVAL,
|
||||
TryFmtError::ToV4L2FormatConversionError => Errno::EINVAL,
|
||||
TryFmtError::InvalidBufferType => Errno::EINVAL,
|
||||
TryFmtError::IoctlError(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe wrapper around the `VIDIOC_TRY_FMT` ioctl.
|
||||
pub fn try_fmt<I: TryInto<v4l2_format>, O: TryFrom<v4l2_format>>(
|
||||
fd: &impl AsRawFd,
|
||||
format: I,
|
||||
) -> Result<O, TryFmtError> {
|
||||
let mut fmt: v4l2_format = format
|
||||
.try_into()
|
||||
.map_err(|_| TryFmtError::ToV4L2FormatConversionError)?;
|
||||
|
||||
match unsafe { ioctl::vidioc_try_fmt(fd.as_raw_fd(), &mut fmt) } {
|
||||
Ok(_) => Ok(fmt
|
||||
.try_into()
|
||||
.map_err(|_| TryFmtError::FromV4L2FormatConversionError)?),
|
||||
Err(Errno::EINVAL) => Err(TryFmtError::InvalidBufferType),
|
||||
Err(e) => Err(TryFmtError::IoctlError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[test]
|
||||
// Convert from Format to multi-planar v4l2_format and back.
|
||||
fn mplane_to_v4l2_format() {
|
||||
// This is not a real format but let us use unique values per field.
|
||||
let mplane = Format {
|
||||
width: 632,
|
||||
height: 480,
|
||||
pixelformat: b"NM12".into(),
|
||||
plane_fmt: vec![
|
||||
PlaneLayout {
|
||||
sizeimage: 307200,
|
||||
bytesperline: 640,
|
||||
},
|
||||
PlaneLayout {
|
||||
sizeimage: 153600,
|
||||
bytesperline: 320,
|
||||
},
|
||||
PlaneLayout {
|
||||
sizeimage: 76800,
|
||||
bytesperline: 160,
|
||||
},
|
||||
],
|
||||
};
|
||||
let v4l2_format = v4l2_format {
|
||||
..(QueueType::VideoCaptureMplane, &mplane).try_into().unwrap()
|
||||
};
|
||||
let mplane2: Format = v4l2_format.try_into().unwrap();
|
||||
assert_eq!(mplane, mplane2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Convert from Format to single-planar v4l2_format and back.
|
||||
fn splane_to_v4l2_format() {
|
||||
// This is not a real format but let us use unique values per field.
|
||||
let splane = Format {
|
||||
width: 632,
|
||||
height: 480,
|
||||
pixelformat: b"NV12".into(),
|
||||
plane_fmt: vec![PlaneLayout {
|
||||
sizeimage: 307200,
|
||||
bytesperline: 640,
|
||||
}],
|
||||
};
|
||||
// Conversion to/from single-planar format.
|
||||
let v4l2_format = v4l2_format {
|
||||
..(QueueType::VideoCapture, &splane).try_into().unwrap()
|
||||
};
|
||||
let splane2: Format = v4l2_format.try_into().unwrap();
|
||||
assert_eq!(splane, splane2);
|
||||
|
||||
// Trying to use a multi-planar format with the single-planar API should
|
||||
// fail.
|
||||
let mplane = Format {
|
||||
width: 632,
|
||||
height: 480,
|
||||
pixelformat: b"NM12".into(),
|
||||
// This is not a real format but let us use unique values per field.
|
||||
plane_fmt: vec![
|
||||
PlaneLayout {
|
||||
sizeimage: 307200,
|
||||
bytesperline: 640,
|
||||
},
|
||||
PlaneLayout {
|
||||
sizeimage: 153600,
|
||||
bytesperline: 320,
|
||||
},
|
||||
PlaneLayout {
|
||||
sizeimage: 76800,
|
||||
bytesperline: 160,
|
||||
},
|
||||
],
|
||||
};
|
||||
assert_eq!(
|
||||
TryInto::<v4l2_format>::try_into((QueueType::VideoCapture, &mplane)).err(),
|
||||
Some(FormatConversionError::TooManyPlanes(3))
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user