Skip to main content

dioxus_cameras/
lib.rs

1//! Dioxus integration for the [`cameras`] crate.
2//!
3//! This crate owns only the Dioxus-specific glue, the HTTP preview server,
4//! the [`Registry`] backing it, the `<canvas>`-side WebGL2 renderer, and the
5//! hooks consumers plug into their components. Every other primitive
6//! (single-frame capture, pause/resume pump, source abstraction) lives
7//! upstream in [`cameras`] itself so non-Dioxus callers can use it too.
8//!
9//! # What's here
10//!
11//! - [`PreviewServer`] + [`start_preview_server`] + [`register_with`], a
12//!   loopback HTTP server that publishes the latest [`Frame`](cameras::Frame)
13//!   for each stream id over `/preview/{id}.bin`. The listener thread is torn
14//!   down when the last [`PreviewServer`] clone drops.
15//! - [`Registry`] + [`LatestFrame`], shared map of stream id → latest frame.
16//!   The server reads from it; pumps publish to it. Cleaned up automatically
17//!   when the owning component unmounts.
18//! - [`use_camera_stream`], high-level hook returning [`UseCameraStream`]:
19//!   a status signal, an active/paused toggle, and a single-frame `capture_frame`
20//!   callback. Wraps [`cameras::pump`] under the hood.
21//! - [`use_devices`] / [`use_streams`], hooks for the camera list and
22//!   multi-stream id management.
23//! - [`PreviewScript`] + [`StreamPreview`], components that render live
24//!   frames into a `<canvas>` via WebGL2 (NV12, BGRA, or RGBA shaders).
25//!
26//! # Wiring a Dioxus app
27//!
28//! ```no_run
29//! use dioxus_cameras::cameras::{self, CameraSource, PixelFormat, Resolution, StreamConfig};
30//! use dioxus::prelude::*;
31//! use dioxus_cameras::{PreviewScript, StreamPreview, register_with, start_preview_server, use_camera_stream};
32//!
33//! fn main() {
34//!     let server = start_preview_server().expect("preview server");
35//!     register_with(&server, dioxus::LaunchBuilder::desktop()).launch(app);
36//! }
37//!
38//! fn app() -> Element {
39//!     let source = use_signal::<Option<CameraSource>>(|| None);
40//!     let config = StreamConfig {
41//!         resolution: Resolution { width: 1280, height: 720 },
42//!         framerate: 30,
43//!         pixel_format: PixelFormat::Bgra8,
44//!     };
45//!     let stream = use_camera_stream(0, source, config);
46//!     rsx! {
47//!         StreamPreview { id: 0 }
48//!         p { "{stream.status}" }
49//!         button {
50//!             onclick: move |_| stream.active.clone().set(!*stream.active.read()),
51//!             "Toggle preview"
52//!         }
53//!         button {
54//!             onclick: move |_| { let _ = stream.capture_frame.call(()); },
55//!             "Take picture"
56//!         }
57//!         PreviewScript {}
58//!     }
59//! }
60//! ```
61
62#![warn(missing_docs)]
63
64pub use cameras;
65
66mod camera_stream;
67mod channel;
68mod component;
69mod devices;
70mod poison;
71mod registry;
72mod server;
73mod streams;
74
75#[cfg(all(feature = "discover", any(target_os = "macos", target_os = "windows")))]
76mod discover;
77
78pub use camera_stream::{StreamStatus, UseCameraStream, use_camera_stream};
79pub use component::{PreviewScript, StreamPreview};
80pub use devices::{UseDevices, use_devices};
81pub use registry::{LatestFrame, Registry, get_or_create_sink, publish_frame, remove_sink};
82pub use server::{PreviewServer, register_with, start_preview_server};
83pub use streams::{UseStreams, use_streams};
84
85#[cfg(all(feature = "discover", any(target_os = "macos", target_os = "windows")))]
86#[cfg_attr(
87    docsrs,
88    doc(cfg(all(feature = "discover", any(target_os = "macos", target_os = "windows"))))
89)]
90pub use discover::{UseDiscovery, use_discovery};
91
92/// The JavaScript blob that drives the WebGL2 preview renderer.
93///
94/// Usually you want the [`PreviewScript`] component instead, it injects this
95/// blob into a `<script>` tag with the right attributes. This raw constant is
96/// exposed for users rendering outside a Dioxus component (for example, in a
97/// custom server-rendered template).
98///
99/// The script scans the DOM for `canvas[data-stream-id]` elements and binds
100/// each one to the URL in its `data-preview-url` attribute. The
101/// [`StreamPreview`] component emits canvases that match this contract.
102pub const PREVIEW_JS: &str = include_str!("../assets/preview.js");