1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
//! A cross-platform camera library for Rust, built with data-oriented design.
//!
//! `cameras` exposes plain data ([`Device`], [`Capabilities`], [`FormatDescriptor`],
//! [`StreamConfig`], [`Frame`]) and free functions that operate on that data. Every
//! public type has public fields. Format negotiation is explicit: you probe, you pick,
//! you open. Errors are typed. Platform dispatch happens at compile time via `cfg` and
//! the associated-type [`Backend`] trait; there are zero trait objects anywhere in the
//! library.
//!
//! # Platform support
//!
//! | Platform | USB / Built-in | RTSP (`rtsp` feature) |
//! |----------|---------------|-----------------------|
//! | macOS | AVFoundation via `objc2` | retina + VideoToolbox (H.264 / H.265 / MJPEG) |
//! | Windows | Media Foundation via `windows` | retina + Media Foundation (H.264 / H.265 / MJPEG) |
//! | Linux | V4L2 via `v4l` | not supported |
//!
//! # Quick Start
//!
//! ```no_run
//! use std::time::Duration;
//!
//! fn main() -> Result<(), cameras::Error> {
//! let devices = cameras::devices()?;
//! let device = devices.first().expect("no cameras");
//!
//! let capabilities = cameras::probe(device)?;
//! println!("{} formats available", capabilities.formats.len());
//!
//! let config = cameras::StreamConfig {
//! resolution: cameras::Resolution { width: 1280, height: 720 },
//! framerate: 30,
//! pixel_format: cameras::PixelFormat::Bgra8,
//! };
//!
//! let camera = cameras::open(device, config)?;
//!
//! for _ in 0..30 {
//! let frame = cameras::next_frame(&camera, Duration::from_secs(2))?;
//! let rgb = cameras::to_rgb8(&frame)?;
//! println!("{}x{} -> {} bytes rgb", frame.width, frame.height, rgb.len());
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! Dropping the [`Camera`] stops the stream. Dropping the [`DeviceMonitor`] joins its
//! polling worker.
//!
//! # Higher-level primitives
//!
//! Two modules layer on top of the [`Camera`] / [`next_frame`] core. They are optional;
//! callers who want full control can stick with the core API.
//!
//! - [`source`]: a [`CameraSource`] enum that unifies USB and RTSP, plus
//! [`open_source`] which dispatches to [`open`] or [`open_rtsp`] automatically.
//! Useful for UIs and config files that want a single "where do frames come from"
//! value type.
//! - [`pump`]: a long-running background worker that pulls frames and hands them to a
//! caller-provided sink closure. Supports [`pump::set_active`] (pause / resume without
//! closing the camera), [`pump::capture_frame`] (single fresh frame on demand, works
//! while paused), and [`pump::stop_and_join`] (deterministic teardown). This is the
//! primitive higher-level integrations (for example, the `dioxus-cameras` hook) are
//! built on.
//!
//! # Design
//!
//! - **Data-oriented**: Types hold data. Functions operate on data. No `impl` blocks with
//! hidden accessors, no trait objects, no inheritance.
//! - **Explicit format negotiation**: [`probe`] returns every format a device supports.
//! You pick one and pass it to [`open`] via [`StreamConfig`]. If you want a fallback,
//! [`best_format`] picks the closest match.
//! - **Push-based delivery**: Each [`Camera`] owns a worker thread and a bounded crossbeam
//! channel. The consumer pulls frames with a timeout via [`next_frame`]. If the consumer
//! falls behind, old frames are dropped, not buffered.
//! - **Typed errors**: See [`Error`].
//! - **Pluggable pixel conversion**: [`to_rgb8`] / [`to_rgba8`] decode from BGRA, RGBA,
//! YUYV, NV12, and MJPEG (via `zune-jpeg`), honoring stride.
//! - **Hotplug**: [`monitor()`] returns a [`DeviceMonitor`] that emits
//! [`DeviceEvent::Added`] / [`DeviceEvent::Removed`] as cameras appear and disappear.
//! - **Unified opening**: [`open_source`] + [`CameraSource`] let you treat USB and RTSP
//! cameras uniformly in higher-level code.
//! - **Background pump with pause + capture**: [`pump::spawn`] runs the frame loop off
//! the calling thread, with [`pump::set_active`] for pause / resume and
//! [`pump::capture_frame`] for single-shot snapshots while paused.
//! - **Compile-time backend contract**: Platform backends are selected with `cfg`. Each is
//! a `Driver` struct that implements [`Backend`]. No `Box<dyn Backend>`; the compiler
//! verifies every platform implements the same surface.
pub type ActiveBackend = crateDriver;
pub type ActiveBackend = crateDriver;
pub type ActiveBackend = crateDriver;
pub type ActiveBackend = crateDriver;
pub use Backend;
pub use ;
pub use ;
pub use Error;
pub use ;
pub use ;
pub use ;
pub use open_rtsp;
use Duration;
/// Enumerate every video capture device the platform currently sees.
///
/// On macOS this triggers the system camera permission prompt on first call
/// if it hasn't been granted. On Linux this reads `/dev/video*`. On Windows
/// this queries Media Foundation via `MFEnumDeviceSources`.
/// Inspect a device's full set of supported formats without opening a stream.
///
/// Returns every native `(resolution, framerate_range, pixel_format)` tuple the
/// device reports. On macOS and Linux this is cheap metadata; on Windows it
/// instantiates a source reader briefly.
/// Open a camera with the given configuration and start streaming.
///
/// The returned [`Camera`] owns a worker thread that pushes frames into a
/// bounded crossbeam channel. Read them with [`next_frame`] or [`try_next_frame`].
/// Dropping the [`Camera`] stops the stream.
/// Start a hotplug monitor.
///
/// Returns a [`DeviceMonitor`] that emits [`DeviceEvent::Added`] / [`DeviceEvent::Removed`]
/// as cameras appear and disappear. Initial events are emitted for every device already
/// present when the monitor starts. Dropping the monitor joins its polling worker.
/// Pick the closest supported format to a requested `StreamConfig`.
///
/// Tries, in order:
/// 1. An exact match on `(pixel_format, resolution, framerate)`.
/// 2. Any format at the requested resolution.
/// 3. The format whose resolution has the smallest total width + height delta
/// from the request.
///
/// Returns `None` only if `capabilities.formats` is empty.
/// A reasonable default timeout for [`next_frame`] when you don't want to hand-pick one.
pub const DEFAULT_FRAME_TIMEOUT: Duration = from_millis;