Skip to main content

brainwires_hardware/camera/
types.rs

1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4/// A discovered camera or video capture device.
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct CameraDevice {
7    /// Zero-based device index (passed to `open_camera`).
8    pub index: u32,
9    /// Human-readable device name (e.g. "HD Pro Webcam C920").
10    pub name: String,
11    /// Additional description from the OS, if available.
12    pub description: Option<String>,
13}
14
15/// Frame resolution in pixels.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17pub struct Resolution {
18    /// Horizontal pixel count.
19    pub width: u32,
20    /// Vertical pixel count.
21    pub height: u32,
22}
23
24impl Resolution {
25    /// Create a resolution from explicit `width` × `height` in pixels.
26    pub fn new(width: u32, height: u32) -> Self {
27        Self { width, height }
28    }
29}
30
31impl std::fmt::Display for Resolution {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(f, "{}x{}", self.width, self.height)
34    }
35}
36
37/// Frame rate expressed as a fraction (e.g. 30/1 = 30 fps).
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
39pub struct FrameRate {
40    /// Frames numerator (e.g. `30` for `30/1` = 30 fps).
41    pub numerator: u32,
42    /// Frames denominator (e.g. `1` for integer fps; `1001` for NTSC 29.97).
43    pub denominator: u32,
44}
45
46impl FrameRate {
47    /// Build an integer frame rate (e.g. `FrameRate::fps(30)`).
48    pub fn fps(fps: u32) -> Self {
49        Self {
50            numerator: fps,
51            denominator: 1,
52        }
53    }
54
55    /// Approximate fps as a float.
56    pub fn as_f64(&self) -> f64 {
57        self.numerator as f64 / self.denominator.max(1) as f64
58    }
59}
60
61impl std::fmt::Display for FrameRate {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        write!(f, "{:.1} fps", self.as_f64())
64    }
65}
66
67/// Pixel encoding format of a camera frame.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
69pub enum PixelFormat {
70    /// 24-bit RGB, packed.
71    Rgb,
72    /// 24-bit BGR, packed.
73    Bgr,
74    /// 32-bit RGBA, packed.
75    Rgba,
76    /// YUV 4:2:2 packed (YUYV).
77    Yuv422,
78    /// Motion JPEG (compressed; decode to RGB before processing).
79    Mjpeg,
80    /// Platform-reported format not otherwise recognised.
81    Unknown,
82}
83
84/// Capture format: resolution + frame rate + pixel encoding.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86pub struct CameraFormat {
87    /// Width × height requested from the device.
88    pub resolution: Resolution,
89    /// Frame rate requested from the device.
90    pub frame_rate: FrameRate,
91    /// Pixel encoding that frames will be delivered in.
92    pub pixel_format: PixelFormat,
93}
94
95impl CameraFormat {
96    /// Bundle an explicit resolution, frame rate, and pixel format.
97    pub fn new(resolution: Resolution, frame_rate: FrameRate, pixel_format: PixelFormat) -> Self {
98        Self {
99            resolution,
100            frame_rate,
101            pixel_format,
102        }
103    }
104
105    /// Common 1080p30 RGB format.
106    pub fn hd_1080p() -> Self {
107        Self::new(
108            Resolution::new(1920, 1080),
109            FrameRate::fps(30),
110            PixelFormat::Mjpeg,
111        )
112    }
113
114    /// Common 720p30 format.
115    pub fn hd_720p() -> Self {
116        Self::new(
117            Resolution::new(1280, 720),
118            FrameRate::fps(30),
119            PixelFormat::Mjpeg,
120        )
121    }
122
123    /// Common VGA 640x480 @ 30 fps.
124    pub fn vga() -> Self {
125        Self::new(
126            Resolution::new(640, 480),
127            FrameRate::fps(30),
128            PixelFormat::Mjpeg,
129        )
130    }
131}
132
133/// A captured video frame.
134#[derive(Debug, Clone)]
135pub struct CameraFrame {
136    /// Frame width in pixels.
137    pub width: u32,
138    /// Frame height in pixels.
139    pub height: u32,
140    /// Pixel format of `data`.
141    pub format: PixelFormat,
142    /// Raw pixel data.
143    pub data: Vec<u8>,
144    /// Milliseconds since capture stream was opened.
145    pub timestamp_ms: u64,
146}
147
148impl CameraFrame {
149    /// Bytes per pixel for packed formats; 0 for compressed formats like MJPEG.
150    pub fn bytes_per_pixel(&self) -> u32 {
151        match self.format {
152            PixelFormat::Rgb | PixelFormat::Bgr => 3,
153            PixelFormat::Rgba => 4,
154            PixelFormat::Yuv422 => 2,
155            PixelFormat::Mjpeg | PixelFormat::Unknown => 0,
156        }
157    }
158}
159
160/// Errors that can occur during camera operations.
161#[derive(Debug, Error)]
162pub enum CameraError {
163    /// No camera exists at the requested device index.
164    #[error("no camera found at index {0}")]
165    NotFound(u32),
166    /// The camera was discoverable but could not be opened.
167    #[error("failed to open camera {index}: {reason}")]
168    OpenFailed {
169        /// Device index that failed to open.
170        index: u32,
171        /// OS-level failure reason.
172        reason: String,
173    },
174    /// Opening succeeded but capturing a frame failed.
175    #[error("failed to capture frame: {0}")]
176    CaptureFailed(String),
177    /// The requested `CameraFormat` is not offered by this device.
178    #[error("format not supported: {0:?}")]
179    FormatNotSupported(CameraFormat),
180    /// Catch-all for platform-specific failures.
181    #[error("camera error: {0}")]
182    Other(String),
183}