#[cfg(feature = "serde")]
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde")]
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::{collections::HashMap, fmt, path::PathBuf};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config {
pub device: PathBuf,
pub format: Format,
pub resolution: (u32, u32),
pub interval: (u32, u32),
pub rotation: Rotation,
#[cfg_attr(feature = "serde", serde(rename = "v4l2Controls"))]
pub v4l2_controls: HashMap<String, String>,
}
impl Default for Config {
fn default() -> Self {
Self {
device: PathBuf::from("/dev/video0"),
format: Format::YUYV,
resolution: (640, 480),
interval: (1, 30),
rotation: Rotation::R0,
v4l2_controls: HashMap::new(),
}
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[repr(u32)]
pub enum Format {
H264 = u32::from_be_bytes(*b"H264"),
YUYV = u32::from_be_bytes(*b"YUYV"),
YV12 = u32::from_be_bytes(*b"YV12"),
RGB3 = u32::from_be_bytes(*b"RGB3"),
BGR3 = u32::from_be_bytes(*b"BGR3"),
}
impl From<Format> for [u8; 4] {
fn from(format: Format) -> Self {
(format as u32).to_be_bytes()
}
}
impl TryFrom<u32> for Format {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match &value.to_be_bytes() {
b"H264" => Ok(Self::H264),
b"YUYV" => Ok(Self::YUYV),
b"YV12" => Ok(Self::YV12),
b"RGB3" => Ok(Self::RGB3),
b"BGR3" => Ok(Self::BGR3),
other => Err(format!("Invalid fourCC: {:?}", std::str::from_utf8(other))),
}
}
}
impl TryFrom<[u8; 4]> for Format {
type Error = String;
fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
u32::from_be_bytes(value).try_into()
}
}
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = &(*self as u32).to_be_bytes();
#[allow(clippy::unwrap_used)] let format = std::str::from_utf8(bytes).unwrap();
write!(f, "{format}")
}
}
#[cfg(feature = "serde")]
impl Serialize for Format {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(s)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Format {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let format = String::deserialize(d)?;
let format = format.as_bytes();
let format = [format[0], format[1], format[2], format[3]];
Self::try_from(u32::from_be_bytes(format)).map_err(D::Error::custom)
}
}
#[cfg_attr(feature = "serde", derive(Serialize_repr, Deserialize_repr))]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Rotation {
R0 = 0,
R90 = 90,
R180 = 180,
R270 = 270,
}