### Basic Dioxus Camera App Setup
Source: https://github.com/matthewjberger/cameras/blob/main/crates/dioxus-cameras/README.md
Initialize the camera preview server, register it with the Dioxus launch builder, and define the main application component. This example demonstrates setting up a camera stream and controlling its preview.
```rust
use dioxus_cameras::cameras::{self, CameraSource, PixelFormat, Resolution, StreamConfig};
use dioxus::prelude::*;
use dioxus_cameras::{
PreviewScript,
StreamPreview,
register_with,
start_preview_server,
use_camera_stream,
};
fn main() {
let server = start_preview_server().expect("preview server");
register_with(&server, dioxus::LaunchBuilder::desktop()).launch(App);
}
fn App() -> Element {
let source = use_signal::>(|| None);
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let stream = use_camera_stream(0, source, config);
rsx! {
StreamPreview { id: 0 }
p { "{stream.status}" }
button {
onclick: move |_| stream.active.clone().set(!*stream.active.read()),
"Toggle preview"
}
button {
onclick: move |_| { let _ = stream.capture_frame.call(()); },
"Take picture"
}
PreviewScript {}
}
}
```
--------------------------------
### Run monitor example
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Executes the monitor example, which listens for camera hotplug events using the monitor and next_event functions. The process continues until interrupted.
```bash
just run-monitor
```
--------------------------------
### Complete Egui Camera Viewer Example
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/egui-cameras.md
A full example demonstrating how to set up a camera stream, continuously update its texture, display the preview in an egui UI, and provide controls for pausing, resuming, and capturing snapshots.
```rust
use egui_cameras::{self, cameras};
use eframe::egui;
struct App {
stream: egui_cameras::Stream,
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Upload latest frame to egui texture
let _result = egui_cameras::update_texture(&mut self.stream, ctx);
ctx.request_repaint();
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("Camera Preview");
egui_cameras::show(&self.stream, ui);
ui.horizontal(|ui| {
if ui.button("Pause").clicked() {
egui_cameras::set_active(&self.stream.pump, false);
}
if ui.button("Resume").clicked() {
egui_cameras::set_active(&self.stream.pump, true);
}
if ui.button("Snapshot").clicked() {
if let Some(frame) = egui_cameras::capture_frame(&self.stream.pump) {
println!("Captured: {}x{}", frame.width, frame.height);
// Save to disk, send to server, etc.
}
}
});
});
}
}
fn main() -> Result<(), eframe::Error> {
let devices = cameras::devices()?;
let device = devices.first().expect("no cameras");
let config = cameras::StreamConfig {
resolution: cameras::Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: cameras::PixelFormat::Bgra8,
};
let camera = cameras::open(device, config)?;
let stream = egui_cameras::spawn(camera);
let options = eframe::NativeOptions::default();
eframe::run_native(
"Camera Viewer",
options,
Box::new(|_cc| Ok(Box::new(App { stream }))),
)
}
```
--------------------------------
### Run snapshot example
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Executes the snapshot example, which opens a camera, captures a single frame, and saves it as a PNG file. This demonstrates basic frame grabbing and file I/O.
```bash
just run-snapshot
```
--------------------------------
### Run pump example
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Executes the pump example, demonstrating a 5-second stream, pausing, and capturing a frame while paused. This is a template for integrating cameras into custom runtimes.
```bash
just run-pump
```
--------------------------------
### Start Dioxus Cameras Preview Server
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/dioxus-cameras.md
Starts the HTTP preview server for Dioxus camera streams. This server listens on localhost:8080 and publishes frame bytes for Web canvas rendering. It should be started before launching the Dioxus application.
```rust
use dioxus_cameras;
fn main() {
let server = dioxus_cameras::start_preview_server()
.expect("failed to start preview server");
dioxus_cameras::register_with(
&server,
dioxus::LaunchBuilder::desktop(),
).launch(app);
}
```
--------------------------------
### Open RTSP Camera Source Example
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/source.md
Demonstrates how to open an RTSP camera source using the open_source function. This example requires the 'rtsp' feature to be enabled.
```rust
use cameras::{self, CameraSource, PixelFormat, Resolution, StreamConfig};
fn main() -> Result<(), cameras::Error> {
let source = CameraSource::Rtsp {
url: "rtsp://example.com:554/stream".to_string(),
credentials: Some(cameras::Credentials {
username: "user".to_string(),
password: "pass".to_string(),
}),
};
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open_source(source, config)?;
println!("Opened: {}x{}", camera.config.resolution.width, camera.config.resolution.height);
Ok(())
}
```
--------------------------------
### Use Dioxus Hook to Select Camera Device
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/dioxus-cameras.md
This example demonstrates using the `use_devices` hook to get a list of available cameras and populate a select dropdown. It also shows how to handle device selection changes.
```rust
use dioxus::prelude::*;
use dioxus_cameras::{use_devices, use_camera_stream};
fn device_selector() -> Element {
let devices_sig = use_devices();
let devices = devices_sig.read();
let source = use_signal:: >(|| None);
rsx! {
select {
onchange: move |evt| {
let name = evt.value();
if let Some(dev) = devices.devices.iter().find(|d| d.name == name) {
source.set(Some(CameraSource::Usb(dev.clone())));
}
},
option { "Select camera..." }
for device in &devices.devices {
option { value: device.name.clone(), "{device.name}" }
}
}
}
}
```
--------------------------------
### start_preview_server()
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/dioxus-cameras.md
Starts the HTTP preview server, which listens on localhost:8080 and publishes camera frames.
```APIDOC
## Function: start_preview_server()
### Description
Starts the HTTP preview server that listens on `localhost:8080` and publishes camera frames. The server runs in a separate thread and shuts down when all `PreviewServer` clones are dropped.
### Signature
```rust
pub fn start_preview_server() -> Result>
```
### Return type
- `Result>` – A handle to the running `PreviewServer` on success, or an error if setup fails.
```
--------------------------------
### Get Camera Source Label Example
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/source.md
Shows how to obtain a human-readable label for a USB camera source. This involves first retrieving available devices and then creating a CameraSource::Usb variant.
```rust
use cameras::{self, CameraSource};
fn main() -> Result<(), cameras::Error> {
let devices = cameras::devices()?;
for device in devices {
let source = CameraSource::Usb(device.clone());
println!("Camera: {}", cameras::source_label(&source));
}
Ok(())
}
```
--------------------------------
### Start Preview Server Helper
Source: https://github.com/matthewjberger/cameras/blob/main/crates/dioxus-cameras/README.md
The `start_preview_server` function binds an HTTP server on an ephemeral port, returning a `PreviewServer` instance. This is essential for serving camera previews.
```rust
let server = start_preview_server().expect("preview server");
```
--------------------------------
### Construct DiscoverConfig
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/discover.md
Example of creating a `DiscoverConfig` instance with specific subnets, endpoints, and timeouts. Note the use of `parse().unwrap()` for string-to-IP conversions.
```rust
use cameras::discover::{DiscoverConfig};
use std::net::{IpAddr, SocketAddr};
use std::time::Duration;
let config = DiscoverConfig {
subnets: vec![
"192.168.1.0/24".parse().unwrap(),
],
endpoints: vec![
"10.0.0.50:554".parse().unwrap(),
],
rtsp_port: 554,
connect_timeout: Duration::from_secs(2),
rtsp_timeout: Duration::from_secs(3),
concurrency: 50, // Probe 50 hosts in parallel
};
```
--------------------------------
### Start Camera Discovery
Source: https://github.com/matthewjberger/cameras/blob/main/crates/egui-cameras/README.md
Initiates a camera discovery session. The `config` parameter should specify subnets or endpoints for the scan. Returns a `DiscoverySession` object.
```rust
let config = DiscoveryConfig::new().subnets(["192.168.1.0/24"]);
let mut session = start_discovery(config);
```
--------------------------------
### Example: Collect and Analyze Frame Sharpness Scores
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/analysis.md
This example demonstrates how to open a camera, capture frames, calculate their sharpness using blur_variance, and then compute an average sharpness score. This average can be used as a threshold for calibration.
```rust
use cameras::{self, PixelFormat, Resolution, StreamConfig};
use std::time::Duration;
fn main() -> Result<(), cameras::Error> {
let devices = cameras::devices()?;
let device = devices.first().expect("no cameras");
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open(device, config)?;
// Collect sharpness scores
let mut scores = Vec::new();
for _ in 0..10 {
let frame = cameras::next_frame(&camera, Duration::from_secs(2))?;
let sharpness = cameras::analysis::blur_variance(&frame);
scores.push(sharpness);
println!("Sharpness: {:.2}", sharpness);
}
// Calibrate: average score becomes your threshold
let avg = scores.iter().sum::() / scores.len() as f32;
println!("Average sharpness: {:.2}", avg);
Ok(())
}
```
--------------------------------
### Open RTSP Stream Example
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/rtsp.md
Demonstrates how to open an RTSP stream with credentials and a desired configuration, then read the first 10 frames. Inspect camera.config after opening for actual stream parameters.
```rust
use cameras::{self, CameraSource, PixelFormat, Resolution, StreamConfig};
use std::time::Duration;
fn main() -> Result<(), cameras::Error> {
let url = "rtsp://example.com:554/stream";
let credentials = Some(cameras::Credentials {
username: "user".to_string(),
password: "pass".to_string(),
});
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open_rtsp(url, credentials, config)?;
println!("Opened RTSP stream: {}x{}",
camera.config.resolution.width,
camera.config.resolution.height
);
// Read frames
for i in 0..10 {
let frame = cameras::next_frame(&camera, Duration::from_secs(2))?;
println!("Frame {}: {}x{}, quality: {:?}",
i, frame.width, frame.height, frame.quality);
}
Ok(())
}
```
--------------------------------
### open
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/cameras-core.md
Opens a camera device with the specified configuration and starts a video stream. The returned `Camera` handle provides access to the stream, and dropping it will stop the stream and release the device.
```APIDOC
## Function: open
### Description
Opens a camera device with the specified configuration and starts a video stream. The returned `Camera` handle provides access to the stream, and dropping it will stop the stream and release the device.
### Signature
```rust
pub fn open(device: &Device, config: StreamConfig) -> Result
```
### Parameters
#### Path Parameters
- **device** (`&Device`) - Required - Device from [`devices()`](#function-devices)
- **config** (`StreamConfig`) - Required - Requested stream parameters: resolution, framerate, pixel format
### Return type
- `Result` — Open camera handle with frame channel
### Throws/Errors
- `Error::PermissionDenied`: Camera access denied by OS
- `Error::DeviceNotFound`: Device no longer connected
- `Error::DeviceInUse`: Another application holds exclusive access
- `Error::FormatNotSupported`: The requested [`StreamConfig`] not supported by device
- `Error::Backend`: Platform-specific failures
### Behavior
- The returned [`Camera`] owns a worker thread that pushes frames into a bounded crossbeam channel
- The actual applied configuration may differ from the request (e.g., rounded framerate); inspect `camera.config` after opening
- Dropping the `Camera` stops the stream and releases the underlying session
### Example
```rust
use cameras::{self, PixelFormat, Resolution, StreamConfig};
use std::time::Duration;
fn main() -> Result<(), cameras::Error> {
let devices = cameras::devices()?;
let device = devices.first().expect("no cameras");
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open(device, config)?;
// Read 10 frames
for _ in 0..10 {
let frame = cameras::next_frame(&camera, Duration::from_secs(2))?;
println!("Frame: {}x{}, {} bytes", frame.width, frame.height,
frame.plane_primary.len());
}
Ok(())
}
```
```
--------------------------------
### Rust: Example of Checking and Applying Focus Control
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/controls.md
Demonstrates how to query focus control capabilities, calculate the midpoint of the supported range, and apply it using the `Controls` struct.
```rust
use cameras::{self, ControlKind};
let device = /* ... */;
let caps = cameras::control_capabilities(device)?;
if let Some(focus_range) = caps.focus {
println!("Focus: {} to {} (step {})",
focus_range.min, focus_range.max, focus_range.step);
// Apply focus in the middle of the supported range
let mid_focus = (focus_range.min + focus_range.max) / 2.0;
let mut controls = cameras::Controls::default();
controls.focus = Some(mid_focus);
cameras::apply_controls(device, &controls)?;
} else {
println!("Device does not support focus control");
}
```
--------------------------------
### Initialize and Display Camera Stream in Egui App
Source: https://github.com/matthewjberger/cameras/blob/main/crates/egui-cameras/README.md
This example demonstrates how to set up a camera stream and display it within an egui application. It includes initializing the camera, creating a stream, and updating the texture in the app's update loop.
```rust
use egui_cameras::cameras::{self, PixelFormat, Resolution, StreamConfig};
use eframe::egui;
struct App {
stream: egui_cameras::Stream,
}
impl App {
fn new() -> Result {
let devices = cameras::devices()?;
let device = devices.first().ok_or(cameras::Error::DeviceNotFound("no cameras".into()))?;
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open(device, config)?;
Ok(Self { stream: egui_cameras::spawn(camera) })
}
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui_cameras::update_texture(&mut self.stream, ctx).ok();
egui::CentralPanel::default().show(ctx, |ui| {
egui_cameras::show(&self.stream, ui);
});
ctx.request_repaint();
}
}
```
--------------------------------
### discover
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/discover.md
Starts a network discovery scan with the given configuration. It returns immediately with a running discovery session, and events stream back asynchronously.
```APIDOC
## Function: `discover(config: DiscoverConfig) -> Result`
### Description
Start a network discovery scan.
### Parameters
#### Path Parameters
- **config** (`DiscoverConfig`) - Required - Scan parameters
### Return type
- `Result` — Running discovery session, or config error
### Throws/Errors
- `Error::InvalidSubnet` - Unparsable, IPv6, or expands beyond 65,536 hosts
- `Error::Backend` - Tokio runtime setup failure
### Behavior
- Returns immediately with a running discovery session
- Events stream back via [`next_event()`](#function-nexteventdiscovery-timeout) or [`try_next_event()`](#function-trynexteventdiscovery)
- Scanning completes asynchronously; final event is always `Done`
```
--------------------------------
### Rust: Example of Conditionally Applying Controls
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/controls.md
Shows how to check for the support of specific controls like focus and auto-focus using `ControlCapabilities` before attempting to set them.
```rust
use cameras::{self, Controls};
let device = /* ... */;
let caps = cameras::control_capabilities(device)?;
let mut controls = Controls::default();
// Only set if supported
if caps.focus.is_some() {
controls.focus = Some(0.5);
}
if caps.auto_focus {
controls.auto_focus = Some(true);
}
cameras::apply_controls(device, &controls)?;
```
--------------------------------
### Complete Dioxus Camera Stream Example
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/dioxus-cameras.md
This snippet demonstrates a full Dioxus application that initializes and displays two camera streams. It includes device selection, stream configuration, and controls for pausing/resuming streams.
```rust
use dioxus_cameras::cameras::{self, CameraSource, PixelFormat, Resolution, StreamConfig};
use dioxus::prelude::*;
use dioxus_cameras::{PreviewScript, StreamPreview, use_camera_stream, use_devices};
fn main() {
let server = dioxus_cameras::start_preview_server()
.expect("preview server");
dioxus_cameras::register_with(&server, dioxus::LaunchBuilder::desktop())
.launch(app);
}
fn app() -> Element {
rsx! {
PreviewScript {}
CameraGrid {}
}
}
fn CameraGrid() -> Element {
let devices_sig = use_devices();
let devices = devices_sig.read();
let stream0_source = use_signal::>(|| None);
let stream1_source = use_signal:: >(|| None);
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let stream0 = use_camera_stream(0, stream0_source, config);
let stream1 = use_camera_stream(1, stream1_source, config);
rsx! {
div {
style: "display: grid; grid-template-columns: 1fr 1fr; gap: 10px;",
div {
StreamPreview { id: 0 }
select {
onchange: move |evt| {
if let Some(dev) = devices.devices.iter()
.find(|d| d.name == evt.value()) {
stream0_source.set(Some(CameraSource::Usb(dev.clone())));
}
},
option { "Stream 0..." }
for device in &devices.devices {
option { "{device.name}" }
}
}
button {
onclick: move |_| stream0.active.set(!*stream0.active.read()),
"Pause/Resume"
}
p { "{stream0.status}" }
}
div {
StreamPreview { id: 1 }
select {
onchange: move |evt| {
if let Some(dev) = devices.devices.iter()
.find(|d| d.name == evt.value()) {
stream1_source.set(Some(CameraSource::Usb(dev.clone())));
}
},
option { "Stream 1..." }
for device in &devices.devices {
option { "{device.name}" }
}
}
button {
onclick: move |_| stream1.active.set(!*stream1.active.read()),
"Pause/Resume"
}
p { "{stream1.status}" }
}
}
}
}
```
--------------------------------
### Enumerate and probe camera devices
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Get a list of available camera devices and probe their capabilities. Assumes at least one device is available.
```rust
let devices = cameras::devices()?;
let capabilities = cameras::probe(&devices[0])?;
```
--------------------------------
### Start Network Discovery
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/discover.md
Initiates a network discovery scan with the specified configuration. Returns a running discovery session immediately. Events are streamed via `next_event()` or `try_next_event()`.
```rust
use cameras::discover::{self, DiscoverConfig};
use std::time::Duration;
fn main() -> Result<(), cameras::Error> {
let net: ipnet::IpNet = "192.168.1.0/24".parse().unwrap();
let discovery = discover::discover(DiscoverConfig {
subnets: vec![net],
..Default::default()
})?;
loop {
match discover::next_event(&discovery, Duration::from_millis(500)) {
Ok(discover::DiscoverEvent::CameraDiscovered(camera)) => {
println!("Found: {} on {}", camera.vendor.unwrap_or_default(), camera.host);
}
Ok(discover::DiscoverEvent::HostUnmatched { host, server_header }) => {
println!("Unknown device at {}: {}", host, server_header);
}
Ok(discover::DiscoverEvent::Done) => {
println!("Scan complete");
break;
}
Ok(_) => continue,
Err(cameras::Error::Timeout) => continue,
Err(e) => return Err(e),
}
}
Ok(())
}
```
--------------------------------
### Start Camera Discovery Scan
Source: https://github.com/matthewjberger/cameras/blob/main/crates/dioxus-cameras/README.md
Initiates a new camera discovery scan with the provided configuration. If a scan is already running, it will be cancelled before the new one starts. This action resets all result signals.
```rust
stream.start(config)
```
--------------------------------
### Test RTSP Locally
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Instructions for testing RTSP streams locally using mediamtx and ffmpeg. This involves starting the RTSP host, publishing an MP4 file, and running the demo app.
```bash
# terminal 1: start mediamtx with the repo's mediamtx.yml
just rtsp-host
# terminal 2: publish an MP4 file as an RTSP stream on rtsp://127.0.0.1:8554/live
just rtsp-publish path/to/some.mp4
# terminal 3: launch the demo app
just run
```
--------------------------------
### Register Dioxus Cameras Preview Server with App Builder
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/dioxus-cameras.md
Registers the started preview server with a Dioxus application builder. This prepares the builder to handle camera stream data and launch the application with the server integrated. The modified builder is then used to launch the Dioxus app.
```rust
use dioxus_cameras;
use dioxus::prelude::*;
fn main() {
let server = dioxus_cameras::start_preview_server().expect("server");
dioxus_cameras::register_with(&server, dioxus::LaunchBuilder::desktop())
.launch(app);
}
fn app() -> Element {
// Your app component
rsx! { div { "Hello" } }
}
```
--------------------------------
### Handling FormatNotSupported Error
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/errors.md
This example shows how to handle cases where the requested stream configuration (resolution, framerate, pixel format) is not supported by the camera. It includes logic to probe for and select a compatible format.
```rust
let config = StreamConfig { /* user's request */ };
match cameras::open(&device, config) {
Err(cameras::Error::FormatNotSupported) => {
// Probe and use best_format to find a supported alternative
let caps = cameras::probe(&device)?;
if let Some(best) = cameras::best_format(&caps, &config) {
let camera = cameras::open(&device, StreamConfig {
resolution: best.resolution,
framerate: best.framerate_range.max as u32,
pixel_format: best.pixel_format,
})?;
}
}
Err(e) => eprintln!("Error: {}", e),
Ok(camera) => { /* ... */ }
}
```
--------------------------------
### Example of an Unknown Device Server Header
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/discover.md
Illustrates the structure of the HostUnmatched event when an unknown vendor's RTSP Server header is encountered. This format is used to report devices that do not match known vendor profiles.
```rust
HostUnmatched {
host: 192.168.1.100,
server_header: "Vendor-RTSP-Server/v1.2.3"
}
```
--------------------------------
### Background Camera Pump with Pause/Resume
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/README.md
This example demonstrates using a background pump to continuously stream frames from a camera. It shows how to pause and resume the stream without closing the camera, and ensures a deterministic shutdown.
```rust
use cameras::{self, PixelFormat, Resolution, StreamConfig};
use cameras::pump;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
fn main() -> Result<(), cameras::Error> {
let devices = cameras::devices()?;
let device = devices.first().expect("no cameras");
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open(device, config)?;
let frame_count = Arc::new(AtomicBool::new(false));
let count = frame_count.clone();
let pump = pump::spawn(camera, move |frame| {
println!("Got frame: {}x{}", frame.width, frame.height);
count.store(true, Ordering::Relaxed);
});
// Stream for 5 seconds
thread::sleep(Duration::from_secs(5));
// Pause without closing
pump::set_active(&pump, false);
println!("Paused");
// Resume
pump::set_active(&pump, true);
println!("Resumed");
// Deterministic shutdown
pump::stop_and_join(pump);
Ok(())
}
```
--------------------------------
### Read Frames with Timeout
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/camera.md
This example demonstrates how to open a camera, configure its stream, and then read a sequence of frames using a blocking API with a specified timeout. It also shows how to convert the captured frame to RGB8 format. Ensure a camera device is available and accessible.
```rust
use cameras::{self, PixelFormat, Resolution, StreamConfig};
use std::time::Duration;
fn main() -> Result<(), cameras::Error> {
let devices = cameras::devices()?;
let device = devices.first().expect("no cameras");
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open(device, config)?;
// Read 30 frames (1 second at 30 fps)
for i in 0..30 {
match cameras::next_frame(&camera, Duration::from_secs(2)) {
Ok(frame) => {
println!("Frame {}: {}x{}, {} bytes", i,
frame.width, frame.height, frame.plane_primary.len());
// Convert to RGB8
let rgb = cameras::to_rgb8(&frame)?;
println!(" Converted to {} bytes RGB", rgb.len());
}
Err(e) => {
eprintln!("Frame error: {}", e);
break;
}
}
}
Ok(())
}
```
--------------------------------
### Discover Axis RTSP cameras on a subnet
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Scan a given IPv4 subnet for Axis RTSP cameras. This example demonstrates iterating through discovery events like CameraFound, Progress, and Done. Requires the 'discover' feature.
```rust
use std::time::Duration;
use cameras::discover::{self, DiscoverConfig, DiscoverEvent};
let net: ipnet::IpNet = "192.168.1.0/24".parse().unwrap();
let discovery = discover::discover(DiscoverConfig {
subnets: vec![net],
..Default::default()
})?;
loop {
match discover::next_event(&discovery, Duration::from_millis(500)) {
Ok(DiscoverEvent::CameraFound(camera)) => println!(..."{:?}", camera),
Ok(DiscoverEvent::Progress { scanned, total }) => eprintln!(..."{scanned}/{total}"),
Ok(DiscoverEvent::Done) => break,
_ => continue,
}
}
```
--------------------------------
### Get or Create Frame Sink
Source: https://github.com/matthewjberger/cameras/blob/main/crates/dioxus-cameras/README.md
The `get_or_create_sink` function retrieves the `LatestFrame` slot for a given stream ID from the registry. This is useful when managing your own camera pump.
```rust
get_or_create_sink(®istry, id)
```
--------------------------------
### monitor
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/cameras-core.md
Starts a hotplug monitor to detect when cameras are added or removed. It returns a `DeviceMonitor` that emits events for device changes, allowing for dynamic camera management.
```APIDOC
## Function: monitor
### Description
Starts a hotplug monitor to detect when cameras are added or removed. It returns a `DeviceMonitor` that emits events for device changes, allowing for dynamic camera management.
### Signature
```rust
pub fn monitor() -> Result
```
### Parameters
(none)
### Return type
- `Result` — Running hotplug monitor
### Throws/Errors
- `Error::Backend`: Platform-specific monitor setup failures
### Behavior
- Returns a [`DeviceMonitor`] that emits [`DeviceEvent::Added`] and [`DeviceEvent::Removed`] as cameras appear/disappear
- Initial events are emitted for every device already present when the monitor starts
- Dropping the monitor joins its polling worker and stops event delivery
### Example
```rust
use cameras::{self, DeviceEvent};
use std::time::Duration;
fn main() -> Result<(), cameras::Error> {
let monitor = cameras::monitor()?;
loop {
match cameras::next_event(&monitor, Duration::from_secs(1)) {
Ok(DeviceEvent::Added(device)) => {
println!("Camera added: {}", device.name);
}
Ok(DeviceEvent::Removed(id)) => {
println!("Camera removed: {}", id.0);
}
Err(cameras::Error::Timeout) => continue,
Err(e) => return Err(e),
}
}
}
```
```
--------------------------------
### register_with()
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/dioxus-cameras.md
Registers the preview server with a Dioxus application builder, enabling integration with the Dioxus event loop.
```APIDOC
## Function: register_with()
### Description
Registers the `PreviewServer` with a Dioxus `LaunchBuilder`. This function integrates the camera preview server into the Dioxus application lifecycle, allowing it to run alongside the Dioxus UI.
### Signature
```rust
pub fn register_with(
server: &PreviewServer,
builder: dioxus::LaunchBuilder,
) -> dioxus::LaunchBuilder
```
### Parameters
#### Path Parameters
- **server** (`&PreviewServer`) - Required - A handle to the `PreviewServer` obtained from `start_preview_server()`.
- **builder** (`dioxus::LaunchBuilder`) - Required - The Dioxus application builder to which the server will be registered.
### Return type
- Modified `dioxus::LaunchBuilder` that is ready to launch the Dioxus application with the integrated camera server.
```
--------------------------------
### Run egui Demo
Source: https://github.com/matthewjberger/cameras/blob/main/README.md
Executes the egui/eframe application demo, featuring a single-stream viewer with pause and snapshot capabilities.
```bash
just run-egui # egui / eframe app: single-stream viewer + snapshot
```
--------------------------------
### Basic camera usage in Rust
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Demonstrates enumerating devices, probing capabilities, opening a stream, and capturing a frame. Ensure a camera is connected and accessible.
```rust
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)?;
let config = cameras::StreamConfig {
resolution: cameras::Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: cameras::PixelFormat::Bgra8,
};
let camera = cameras::open(device, config)?;
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(())
}
```
--------------------------------
### Spawn Default egui-cameras Stream
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/egui-cameras.md
Use this function to spawn a camera pump that feeds a default-named egui texture. The pump starts active, and the texture is initially None until the first frame arrives.
```rust
use egui_cameras;
use cameras::{self, PixelFormat, Resolution, StreamConfig};
let device = cameras::devices()?
.into_iter().next()
.expect("no cameras");
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open(&device, config)?;
let mut stream = egui_cameras::spawn(camera);
```
--------------------------------
### Run Dioxus Demo
Source: https://github.com/matthewjberger/cameras/blob/main/README.md
Executes the Dioxus desktop application demo, which showcases multi-stream grids and USB/RTSP sources.
```bash
just run-dioxus # Dioxus desktop app: multi-stream grid, USB + RTSP
```
--------------------------------
### Register Preview Server with Dioxus
Source: https://github.com/matthewjberger/cameras/blob/main/crates/dioxus-cameras/README.md
The `register_with` function injects the preview server's registry and port into the Dioxus context, making them accessible to downstream components and hooks.
```rust
register_with(&server, dioxus::LaunchBuilder::desktop()).launch(App);
```
--------------------------------
### spawn
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/egui-cameras.md
Spawns a camera pump that feeds a fresh Stream, backed by a default-named egui texture. The pump starts in an active state, and the texture is initially None until the first frame arrives. The default texture name used is 'cameras-frame'.
```APIDOC
## spawn(camera: Camera) -> Stream
### Description
Spawn a pump that feeds a fresh [`Stream`](#type-stream) backed by a default-named texture.
### Method
`spawn`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Parameters
- **camera** (`Camera`) - Required - Open camera from [`cameras::open()`](api-reference/cameras-core.md#function-opendevice-device-config-streamconfig)
### Return type
- `Stream` – Populated with pump, sink, and texture name
### Behavior
- Pump starts in active state (streaming)
- Texture is `None` until the first frame arrives
- Default texture name is `"cameras-frame"`
### Example
```rust
use egui_cameras;
use cameras::{self, PixelFormat, Resolution, StreamConfig};
let device = cameras::devices()?
.into_iter().next()
.expect("no cameras");
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open(&device, config)?;
let mut stream = egui_cameras::spawn(camera);
```
```
--------------------------------
### Handle Camera Opening Errors
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/README.md
Demonstrates how to match on different error variants when opening a camera device. It shows specific error handling for unsupported formats, device in use, and permission denied, with a fallback for other errors.
```rust
match cameras::open(device, config) {
Ok(camera) => { /* ... */ }
Err(cameras::Error::FormatNotSupported) => {
// Use best_format to find an alternative
let caps = cameras::probe(device)?;
let best = cameras::best_format(&caps, &config).expect("no formats");
// Try again with best format
}
Err(cameras::Error::DeviceInUse) => {
eprintln!("Camera is in use by another app");
}
Err(cameras::Error::PermissionDenied) => {
eprintln!("Camera permission denied");
}
Err(e) => eprintln!("Error: {}", e),
}
```
--------------------------------
### Enumerate Camera Devices
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/cameras-core.md
Use this function to get a list of all video capture devices currently recognized by the system. It returns a vector of `Device` objects, each containing the camera's name and ID. Be aware that on macOS, the first call might trigger a system permission prompt if camera access has not been granted.
```rust
use cameras;
fn main() -> Result<(), cameras::Error> {
let devices = cameras::devices()?;
for device in devices {
println!("Camera: {} (id: {})", device.name, device.id.0);
}
Ok(())
}
```
--------------------------------
### Dioxus Desktop App with Camera Preview
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/README.md
Integrates camera streaming into a Dioxus desktop application. Allows users to select cameras and control the stream.
```rust
use dioxus_cameras::cameras::{self, CameraSource, PixelFormat, Resolution, StreamConfig};
use dioxus::prelude::*;
use dioxus_cameras::{PreviewScript, StreamPreview, use_camera_stream, use_devices};
fn main() {
let server = dioxus_cameras::start_preview_server()
.expect("server");
dioxus_cameras::register_with(&server, dioxus::LaunchBuilder::desktop())
.launch(app);
}
fn app() -> Element {
rsx! {
PreviewScript {}
CameraGrid {}
}
}
#[component]
fn CameraGrid() -> Element {
let devices_sig = use_devices();
let devices = devices_sig.read();
let source = use_signal::>(|| None);
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let stream = use_camera_stream(0, source, config);
rsx! {
div {
StreamPreview { id: 0 }
p { "{stream.status}" }
select {
onchange: move |evt| {
if let Some(dev) = devices.devices.iter()
.find(|d| d.name == evt.value())
{
source.set(Some(CameraSource::Usb(dev.clone())));
}
},
for device in &devices.devices {
option { "{device.name}" }
}
}
button {
onclick: move |_| stream.active.set(!*stream.active.read()),
"Pause/Resume"
}
}
}
}
```
--------------------------------
### Core Library Architecture
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/README.md
Illustrates the layered architecture of the core 'cameras' crate, showing the interaction between platform backends, core functions, and higher-level primitives.
```text
┌─────────────────────────────────────────┐
│ Platform Backend (compile-time) │
├─────────────────────────────────────────┤
│ macOS (AVFoundation) │
│ Windows (Media Foundation) │
│ Linux (V4L2) │
└─────────────────────────────────────────┘
↓ ↑
┌─────────────────────────────────────────┐
│ Core Types & Functions │
├─────────────────────────────────────────┤
│ devices() → [Device] │
│ probe(device) → Capabilities │
│ open(device, config) → Camera │
│ monitor() → DeviceMonitor │
│ next_frame(camera, timeout) → Frame │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Higher-Level Primitives │
├─────────────────────────────────────────┤
│ Pump (background worker) │
│ CameraSource (USB + RTSP) │
│ Pixel conversion (RGB8, RGBA8) │
│ [Feature: analysis] Sharpness metrics │
│ [Feature: controls] Camera controls │
│ [Feature: rtsp] RTSP backend │
│ [Feature: discover] RTSP discovery │
└─────────────────────────────────────────┘
```
--------------------------------
### Measure Frame Sharpness using Blur Variance
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/analysis.md
Calculate the blur variance of a frame to get a relative sharpness score. Higher scores indicate sharper frames. This function converts the frame to grayscale, applies a Laplacian kernel, and computes the variance of the response. Scores are relative and require calibration per camera and lighting condition. Returns 0.0 for degenerate frames.
```rust
pub fn blur_variance(frame: &Frame) -> f32
```
--------------------------------
### Publish egui Cameras Crate
Source: https://github.com/matthewjberger/cameras/blob/main/README.md
Publishes the 'egui-cameras' integration crate to crates.io.
```bash
just publish-egui # egui-cameras
```
--------------------------------
### Unified RTSP Opening with CameraSource
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/rtsp.md
Shows how to use CameraSource::Rtsp to uniformly open RTSP streams alongside other camera types. This approach is useful for polymorphic code that needs to handle different camera sources.
```rust
use cameras::{self, CameraSource, PixelFormat, Resolution, StreamConfig};
let source = CameraSource::Rtsp {
url: "rtsp://example.com/stream".to_string(),
credentials: None,
};
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let camera = cameras::open_source(source, config)?;
```
--------------------------------
### Add egui-cameras Dependency
Source: https://github.com/matthewjberger/cameras/blob/main/crates/egui-cameras/README.md
Add the egui-cameras crate to your project's Cargo.toml file.
```toml
[dependencies]
egui-cameras = "0.1"
eframe = "0.32"
```
--------------------------------
### Select best available camera format
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Finds the best matching format from capabilities for a desired stream configuration. Returns an error if no suitable format is found.
```rust
let picked = cameras::best_format(&capabilities, &config).expect("no fallback");
```
--------------------------------
### Testing RTSP Locally
Source: https://github.com/matthewjberger/cameras/blob/main/crates/cameras/README.md
Instructions for testing RTSP streams locally using `mediamtx` and `ffmpeg`.
```APIDOC
## Testing RTSP Locally
The `demo/` app can view RTSP streams on macOS and Windows. To exercise the full path without a real IP camera, serve a local MP4 as an RTSP stream using [`mediamtx`](https://github.com/bluenviron/mediamtx) and `ffmpeg` (both on `PATH`):
### Request Example
```bash
# terminal 1: start mediamtx with the repo's mediamtx.yml
just rtsp-host
# terminal 2: publish an MP4 file as an RTSP stream on rtsp://127.0.0.1:8554/live
just rtsp-publish path/to/some.mp4
# terminal 3: launch the demo app
just run
```
In the demo window, switch the source toggle to **RTSP**, paste `rtsp://127.0.0.1:8554/live` into the URL field, and press **Connect**. On macOS and Windows, H.264/H.265 streams are hardware-decoded (VideoToolbox / Media Foundation); MJPEG streams are delivered verbatim and decoded via `zune-jpeg` on demand.
```
--------------------------------
### Constructing Controls Instance
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/api-reference/controls.md
Demonstrates how to create and modify a `Controls` struct instance. Use `Some(value)` to apply a setting and `None` to leave it unchanged.
```rust
let mut controls = cameras::Controls::default();
controls.focus = Some(0.5);
controls.brightness = Some(50.0);
```
--------------------------------
### RTSP Network Camera with Reconnection Policy
Source: https://github.com/matthewjberger/cameras/blob/main/_autodocs/README.md
This snippet illustrates connecting to an RTSP network camera, including handling authentication and configuring a reconnection policy. It also sets up a status listener to track connection events like connecting, reconnecting, and giving up.
```rust
use cameras::{self, CameraSource, PixelFormat, Resolution, StreamConfig};
use cameras::pump::{self, ReconnectPolicy};
use std::sync::mpsc;
use std::time::Duration;
fn main() -> Result<(), cameras::Error> {
let source = CameraSource::Rtsp {
url: "rtsp://example.com:554/stream".to_string(),
credentials: Some(cameras::Credentials {
username: "admin".to_string(),
password: "secret".to_string(),
}),
};
let config = StreamConfig {
resolution: Resolution { width: 1280, height: 720 },
framerate: 30,
pixel_format: PixelFormat::Bgra8,
};
let policy = Some(ReconnectPolicy {
initial_backoff: Duration::from_secs(1),
max_backoff: Duration::from_secs(30),
max_attempts: Some(10),
jitter: 0.2,
stall_timeout: Duration::from_secs(15),
});
let (status_tx, status_rx) = mpsc::sync_channel(10);
let pump = pump::spawn_with_policy(
source,
config,
|frame| println!("Frame: {}x{}", frame.width, frame.height),
policy,
Some(status_tx),
)?;
std::thread::spawn(move || {
while let Ok(status) = status_rx.recv() {
match status {
pump::PumpStatus::Connected => println!("Connected"),
pump::PumpStatus::Reconnecting { attempt, .. } => {
println!("Reconnecting (attempt {})", attempt);
}
pump::PumpStatus::GaveUp { .. } => {
println!("Failed to reconnect");
break;
}
_ => {}
}
}
});
std::thread::sleep(Duration::from_secs(60));
pump::stop_and_join(pump);
Ok(())
}
```