azul_core/camera.rs
1//! POD types for the camera-capture surface
2//! (SUPER_PLAN_2 §4 Priority 6 + research/01).
3//!
4//! Camera frames are GPU textures, not scalar samples, so the stateful side
5//! is heavier than the sensors': `azul_layout::managers::camera` owns a
6//! `CameraStream` per capture, each holding a shared `ImageRef` texture the
7//! capture thread writes into (zero-copy - clones see new bytes via the
8//! `ImageRef` `Arc`). A `CameraPreview` node renders that texture and, by
9//! appearing in the DOM, declares "I need the camera" to the permission
10//! layer (research/01 §"permission-as-DOM").
11//!
12//! Defined here in `azul-core` so the config / id / status types cross the
13//! FFI without `azul-layout` (or AVFoundation / Camera2) as a dependency -
14//! these are what an app passes to `start_camera` and reads back from a
15//! stream. The `Nv12` zero-copy output format is a `RawImageFormat` addition
16//! deferred to the backend tick; configs default to `BGRA8`.
17
18use crate::resources::RawImageFormat;
19
20/// Identifies one camera capture stream - assigned by `start_camera`, used
21/// to read the stream back (`get_camera_frame`) and to stop / pause / flip it.
22#[repr(C)]
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct CaptureStreamId {
25 pub id: u64,
26}
27
28/// Which physical camera to open.
29#[repr(C)]
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub enum CameraFacing {
32 /// User-facing (selfie) camera.
33 Front,
34 /// World-facing (rear) camera.
35 Back,
36 /// An external / USB camera (desktop webcams report here).
37 External,
38}
39
40/// Lifecycle of a capture stream.
41#[repr(C)]
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub enum StreamState {
44 /// Opening the device / negotiating the format.
45 Starting,
46 /// Delivering frames.
47 Running,
48 /// Temporarily suspended (app backgrounded, `pause_camera`).
49 Paused,
50 /// Stopped by the app (`stop_camera`) or torn down.
51 Stopped,
52 /// Failed - see the stream's [`CaptureErrorCode`].
53 Error,
54}
55
56/// Rotation / mirroring the capture needs relative to the display (the
57/// sensor's native orientation rarely matches the UI's).
58#[repr(C)]
59#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
60pub enum CaptureOrientation {
61 /// Upright (0°).
62 Up,
63 /// Upside down (180°).
64 Down,
65 /// Rotated 90° counter-clockwise.
66 Left,
67 /// Rotated 90° clockwise.
68 Right,
69 /// Horizontally mirrored (typical for the front camera).
70 Mirror,
71}
72
73/// Why a capture stream failed ([`StreamState::Error`]).
74#[repr(C)]
75#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub enum CaptureErrorCode {
77 /// The user denied (or hasn't granted) camera permission.
78 PermissionDenied,
79 /// No camera matched the requested [`CameraFacing`].
80 DeviceUnavailable,
81 /// The device disappeared mid-capture (unplugged / claimed).
82 DeviceLost,
83 /// The requested format / resolution isn't supported.
84 Unsupported,
85 /// A platform error not covered above.
86 Internal,
87}
88
89/// Requested capture configuration - the input to `start_camera`. Zero
90/// `width`/`height`/`fps` mean "let the backend pick its default".
91#[repr(C)]
92#[derive(Debug, Clone, Copy, PartialEq)]
93pub struct CameraConfig {
94 /// Which camera to open.
95 pub facing: CameraFacing,
96 /// Preferred frame width in px (0 = backend default).
97 pub width: u32,
98 /// Preferred frame height in px (0 = backend default).
99 pub height: u32,
100 /// Preferred frame rate (0 = backend default).
101 pub fps: u32,
102 /// Texture format the backend should deliver. `BGRA8` is the portable
103 /// default; `Nv12` (a later `RawImageFormat` addition) is the zero-copy
104 /// path on platforms that produce it natively.
105 pub output_format: RawImageFormat,
106}
107
108impl Default for CameraConfig {
109 fn default() -> Self {
110 Self {
111 facing: CameraFacing::Back,
112 width: 0,
113 height: 0,
114 fps: 0,
115 output_format: RawImageFormat::BGRA8,
116 }
117 }
118}
119
120impl CameraConfig {
121 /// A default config for the given `facing` (backend-chosen size/fps,
122 /// `BGRA8`).
123 pub fn new(facing: CameraFacing) -> Self {
124 Self {
125 facing,
126 ..Self::default()
127 }
128 }
129}
130
131/// Runtime stats for a capture stream - surfaced for HUD / debugging.
132#[repr(C)]
133#[derive(Debug, Clone, Copy, PartialEq)]
134pub struct CaptureStats {
135 /// Measured delivery rate (frames/s), smoothed by the backend.
136 pub measured_fps: f32,
137 /// Frames delivered to the texture since the stream started.
138 pub frames_delivered: u64,
139 /// Frames the backend dropped (couldn't keep up / late).
140 pub frames_dropped: u64,
141}
142
143impl Default for CaptureStats {
144 fn default() -> Self {
145 Self {
146 measured_fps: 0.0,
147 frames_delivered: 0,
148 frames_dropped: 0,
149 }
150 }
151}