### 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(()) } ```