use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CameraDevice {
pub index: u32,
pub name: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Resolution {
pub width: u32,
pub height: u32,
}
impl Resolution {
pub fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
}
impl std::fmt::Display for Resolution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}x{}", self.width, self.height)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FrameRate {
pub numerator: u32,
pub denominator: u32,
}
impl FrameRate {
pub fn fps(fps: u32) -> Self {
Self {
numerator: fps,
denominator: 1,
}
}
pub fn as_f64(&self) -> f64 {
self.numerator as f64 / self.denominator.max(1) as f64
}
}
impl std::fmt::Display for FrameRate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:.1} fps", self.as_f64())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PixelFormat {
Rgb,
Bgr,
Rgba,
Yuv422,
Mjpeg,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct CameraFormat {
pub resolution: Resolution,
pub frame_rate: FrameRate,
pub pixel_format: PixelFormat,
}
impl CameraFormat {
pub fn new(resolution: Resolution, frame_rate: FrameRate, pixel_format: PixelFormat) -> Self {
Self {
resolution,
frame_rate,
pixel_format,
}
}
pub fn hd_1080p() -> Self {
Self::new(
Resolution::new(1920, 1080),
FrameRate::fps(30),
PixelFormat::Mjpeg,
)
}
pub fn hd_720p() -> Self {
Self::new(
Resolution::new(1280, 720),
FrameRate::fps(30),
PixelFormat::Mjpeg,
)
}
pub fn vga() -> Self {
Self::new(
Resolution::new(640, 480),
FrameRate::fps(30),
PixelFormat::Mjpeg,
)
}
}
#[derive(Debug, Clone)]
pub struct CameraFrame {
pub width: u32,
pub height: u32,
pub format: PixelFormat,
pub data: Vec<u8>,
pub timestamp_ms: u64,
}
impl CameraFrame {
pub fn bytes_per_pixel(&self) -> u32 {
match self.format {
PixelFormat::Rgb | PixelFormat::Bgr => 3,
PixelFormat::Rgba => 4,
PixelFormat::Yuv422 => 2,
PixelFormat::Mjpeg | PixelFormat::Unknown => 0,
}
}
}
#[derive(Debug, Error)]
pub enum CameraError {
#[error("no camera found at index {0}")]
NotFound(u32),
#[error("failed to open camera {index}: {reason}")]
OpenFailed { index: u32, reason: String },
#[error("failed to capture frame: {0}")]
CaptureFailed(String),
#[error("format not supported: {0:?}")]
FormatNotSupported(CameraFormat),
#[error("camera error: {0}")]
Other(String),
}