use std::fs::File;
use std::io;
use std::os::fd::AsRawFd;
use std::path::Path;
use thiserror::Error;
use v4l2_subdev::{self, MediaBusFmt, Zeroed, v4l2_colorspace, v4l2_quantization, v4l2_field, v4l2_xfer_func, MbusFrameFormatFlags};
#[derive(Error, Debug)]
pub enum Error {
#[error("I/O error")]
Io(io::Error),
#[error("This is not a Media subdevice")]
NotASubdevice,
}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
pub struct Io;
impl Io {
pub fn open(&mut self, path: &Path) -> Result<Subdevice, Error> {
let f = File::open(path)?;
Ok(Subdevice(f))
}
}
pub struct Subdevice(File);
impl Subdevice {
pub fn enum_mbus_code(&self, _io: &mut Io, pad: u32) -> io::Result<Vec<SubdeviceMbusCode>> {
let mut out = Vec::new();
loop {
let mut v = v4l2_subdev::sys::v4l2_subdev_mbus_code_enum {
index: out.len() as u32,
pad,
..Zeroed::zeroed()
};
let ret = unsafe {
v4l2_subdev::enum_mbus_code(self.0.as_raw_fd(), &mut v)
};
match ret {
Err(nix::Error::EINVAL) => {
return if out.is_empty() {
Err(nix::Error::EINVAL.into())
} else {
Ok(out)
}
},
Ok(_) => {},
Err(other) => { return Err(other.into()) },
};
out.push(SubdeviceMbusCode { code: v.code.into() , flags: v.flags });
}
}
pub fn enum_frame_size(&self, _io: &mut Io, pad: u32, code: MediaBusFmt) -> io::Result<Vec<SubdeviceFrameSize>> {
let mut out = Vec::new();
loop {
let mut v = v4l2_subdev::sys::v4l2_subdev_frame_size_enum {
index: out.len() as u32,
pad,
code: code.into(),
..Zeroed::zeroed()
};
let ret = unsafe {
v4l2_subdev::enum_frame_size(self.0.as_raw_fd(), &mut v)
};
match ret {
Err(nix::Error::EINVAL) => {
return if out.is_empty() {
Err(nix::Error::EINVAL.into())
} else {
Ok(out)
}
},
Ok(_) => {},
Err(other) => { return Err(other.into()) },
};
out.push(SubdeviceFrameSize {
min_width: v.min_width,
max_width: v.max_width,
min_height: v.min_height,
max_height: v.max_height,
});
}
}
pub fn get_format(&self, _io:&mut Io, pad: u32) -> io::Result<MbusFrameFormat> {
let mut v = v4l2_subdev::sys::v4l2_subdev_format {
pad,
..Zeroed::zeroed()
};
unsafe {
v4l2_subdev::g_fmt(self.0.as_raw_fd(), &mut v)
}?;
Ok(v.format.into())
}
pub fn set_format(&mut self, _io:&mut Io, pad: u32, format: MbusFrameFormat) -> io::Result<()> {
let mut v = v4l2_subdev::sys::v4l2_subdev_format {
pad,
format: format.into(),
..Zeroed::zeroed()
};
unsafe {
v4l2_subdev::s_fmt(self.0.as_raw_fd(), &mut v)
}?;
Ok(())
}
}
#[derive(Debug)]
pub struct SubdeviceMbusCode {
pub code: MediaBusFmt,
pub flags: u32,
}
#[derive(Debug)]
pub struct SubdeviceFrameSize {
pub min_width: u32,
pub max_width: u32,
pub min_height: u32,
pub max_height: u32,
}
#[derive(Debug, Clone)]
pub struct MbusFrameFormat {
pub width: u32,
pub height: u32,
pub code: MediaBusFmt,
pub field: u32,pub colorspace: u32,pub quantization: u16,pub xfer_func: u16,pub flags: MbusFrameFormatFlags,
}
impl From<v4l2_subdev::sys::v4l2_mbus_framefmt> for MbusFrameFormat {
fn from(value: v4l2_subdev::sys::v4l2_mbus_framefmt) -> Self {
Self {
width: value.width,
height: value.height,
code: value.code.into(),
field: value.field,
colorspace: value.colorspace,
quantization: value.quantization,
xfer_func: value.xfer_func,
flags: MbusFrameFormatFlags::from_bits_retain(value.flags),
}
}
}
impl From<MbusFrameFormat> for v4l2_subdev::sys::v4l2_mbus_framefmt {
fn from(value: MbusFrameFormat) -> Self {
Self {
width: value.width,
height: value.height,
code: value.code.into(),
field: value.field,
colorspace: value.colorspace,
quantization: value.quantization,
xfer_func: value.xfer_func,
flags: value.flags.bits(),
..Self::zeroed()
}
}
}