1#[cfg(feature = "serde")]
4use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
5#[cfg(feature = "serde")]
6use serde_repr::{Deserialize_repr, Serialize_repr};
7use std::{collections::HashMap, fmt, path::PathBuf};
8
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Config {
13 pub device: PathBuf,
15 pub format: Format,
17 pub resolution: (u32, u32),
19 pub interval: (u32, u32),
21 pub rotation: Rotation,
23 #[cfg_attr(feature = "serde", serde(rename = "v4l2Controls"))]
25 pub v4l2_controls: HashMap<String, String>,
26}
27
28impl Default for Config {
29 fn default() -> Self {
30 Self {
31 device: PathBuf::from("/dev/video0"),
32 format: Format::YUYV,
33 resolution: (640, 480),
34 interval: (1, 30),
35 rotation: Rotation::R0,
36 v4l2_controls: HashMap::new(),
37 }
38 }
39}
40
41#[allow(clippy::upper_case_acronyms)]
45#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
46#[repr(u32)]
47pub enum Format {
48 H264 = u32::from_be_bytes(*b"H264"),
51 YUYV = u32::from_be_bytes(*b"YUYV"),
56 YV12 = u32::from_be_bytes(*b"YV12"),
61 RGB3 = u32::from_be_bytes(*b"RGB3"),
64 BGR3 = u32::from_be_bytes(*b"BGR3"),
67}
68
69impl From<Format> for [u8; 4] {
70 fn from(format: Format) -> Self {
71 (format as u32).to_be_bytes()
72 }
73}
74
75impl TryFrom<u32> for Format {
76 type Error = String;
77
78 fn try_from(value: u32) -> Result<Self, Self::Error> {
79 match &value.to_be_bytes() {
80 b"H264" => Ok(Self::H264),
81 b"YUYV" => Ok(Self::YUYV),
82 b"YV12" => Ok(Self::YV12),
83 b"RGB3" => Ok(Self::RGB3),
84 b"BGR3" => Ok(Self::BGR3),
85 other => Err(format!("Invalid fourCC: {:?}", std::str::from_utf8(other))),
86 }
87 }
88}
89
90impl TryFrom<[u8; 4]> for Format {
91 type Error = String;
92
93 fn try_from(value: [u8; 4]) -> Result<Self, Self::Error> {
94 u32::from_be_bytes(value).try_into()
95 }
96}
97
98impl fmt::Display for Format {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 let bytes = &(*self as u32).to_be_bytes();
101 #[allow(clippy::unwrap_used)] let format = std::str::from_utf8(bytes).unwrap();
103 write!(f, "{format}")
104 }
105}
106
107#[cfg(feature = "serde")]
108impl Serialize for Format {
109 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
110 self.to_string().serialize(s)
111 }
112}
113
114#[cfg(feature = "serde")]
115impl<'de> Deserialize<'de> for Format {
116 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
117 let format = String::deserialize(d)?;
118 let format = format.as_bytes();
119 let format = [format[0], format[1], format[2], format[3]];
120 Self::try_from(u32::from_be_bytes(format)).map_err(D::Error::custom)
121 }
122}
123
124#[cfg_attr(feature = "serde", derive(Serialize_repr, Deserialize_repr))]
128#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
129#[repr(u32)]
130pub enum Rotation {
131 R0 = 0,
133 R90 = 90,
135 R180 = 180,
137 R270 = 270,
139}