<h1 align="center">dioxus-chimeras</h1>
<p align="center">
<a href="https://github.com/matthewjberger/chimeras"><img alt="github" src="https://img.shields.io/badge/github-matthewjberger/chimeras-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20"></a>
<a href="https://crates.io/crates/dioxus-chimeras"><img alt="crates.io" src="https://img.shields.io/crates/v/dioxus-chimeras.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20"></a>
<a href="https://docs.rs/dioxus-chimeras"><img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-dioxus--chimeras-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20"></a>
<a href="https://github.com/matthewjberger/chimeras/blob/main/LICENSE-MIT"><img alt="license" src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue?style=for-the-badge&labelColor=555555" height="20"></a>
</p>
<p align="center"><strong>Drop camera streams into your Dioxus desktop app.</strong></p>
<p align="center">
<code>cargo add dioxus-chimeras</code>
</p>
`dioxus-chimeras` is the Dioxus integration for the [`chimeras`](../) cross-platform camera library. It owns the glue between a `chimeras::Camera` and a `<canvas>` element: a loopback HTTP preview server, a WebGL2 renderer, a `Registry` that shares frames between the camera pump and the webview, and a handful of hooks that expose the stream lifecycle as Dioxus signals.
Every generic primitive (pause/resume pump, single-frame capture, unified `CameraSource`) lives upstream in `chimeras` itself so non-Dioxus callers can use it too.
## Quick Start
```toml
[dependencies]
dioxus = { version = "0.7", features = ["desktop"] }
dioxus-chimeras = "0.2"
chimeras = "0.2"
```
`dioxus-chimeras` and `chimeras` ship in lockstep: both crates share the same major + minor version (`0.2.x`). Use matching versions in your `Cargo.toml`.
```rust
use chimeras::{CameraSource, PixelFormat, Resolution, StreamConfig};
use dioxus::prelude::*;
use dioxus_chimeras::{
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::<Option<CameraSource>>(|| 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 {}
}
}
```
`register_with` injects the preview server's `Registry` and port into the Dioxus context so every `StreamPreview` and `use_camera_stream` call downstream can find them.
## What's in the box
### Hooks
| `use_camera_stream(id, source, config)` | `UseCameraStream { status, active, capture_frame }` | Opens the camera, runs the pump, publishes frames to the preview, surfaces lifecycle as signals. |
| `use_devices()` | `UseDevices { devices, ready, refresh }` | Keeps a `Signal<Vec<Device>>` populated off a worker thread; refresh on demand. |
| `use_streams()` | `UseStreams { ids, add, remove }` | Manages a dynamic list of stream ids for multi-stream apps; monotonic, safe as Dioxus keys. |
All returned handles are `Copy + Clone + PartialEq` data structs with public fields (no methods, no hidden state).
### Components
| `StreamPreview { id }` | A `<canvas>` bound to the preview server's `/preview/{id}.bin` URL; WebGL2-renders the live stream. |
| `PreviewScript {}` | Injects the WebGL2 driver script once per page. Render it as the last child of your root. |
### Helpers
| `start_preview_server()` | Binds a loopback HTTP server on an ephemeral port; returns a `PreviewServer`. |
| `register_with(&server, launch)` | Injects registry + port + keep-alive into a `LaunchBuilder`. |
| `get_or_create_sink(®istry, id)` | Returns the `LatestFrame` slot for `id`; used when running your own pump. |
| `remove_sink(®istry, id)` | Drops the slot for `id`. Normally handled automatically on unmount. |
| `publish_frame(&sink, frame)` | Writes a frame into a `LatestFrame`; used when running your own pump. |
## Live preview + take-picture
The hook returns both operations on one handle.
```rust
let stream = use_camera_stream(id, source, config);
// Pause the pump while the user is on another tab (no per-frame Rust work;
// camera stays open so capture stays fast):
stream.active.clone().set(false);
// Grab a fresh frame whether streaming or paused:
if let Some(frame) = stream.capture_frame.call(()) {
// save, toast, upload, etc.
}
// Resume streaming:
stream.active.clone().set(true);
```
`capture_frame` is sync-blocking on the calling thread until the pump worker replies (typically one frame interval, plus up to 20ms of wake latency if paused). That's fine from an `onclick`; for rapid back-to-back captures, dispatch from a `std::thread::spawn`'d worker or a Dioxus `spawn` task.
## Versioning
`dioxus-chimeras` is released in lockstep with `chimeras`: both crates share the same major + minor version (for example, `chimeras 0.2.x` pairs with `dioxus-chimeras 0.2.x`). Patch numbers can drift between the two; every `0.2.x` of dioxus-chimeras will work with any `0.2.y` of chimeras.
## Features
| `rtsp` | on | Forwards to `chimeras/rtsp`; enables `CameraSource::Rtsp` on macOS and Windows. |
Disable with `dioxus-chimeras = { version = "0.1", default-features = false }`.
## Source separation
The crate deliberately splits responsibilities:
- **`chimeras`** owns the camera-side primitives: `Camera`, `CameraSource`, `pump::Pump`, etc. No Dioxus dependency.
- **`dioxus-chimeras`** owns the UI-side glue: the preview server, `Registry`, hooks, and components. Depends on `dioxus` and `chimeras`.
If you're integrating chimeras into a non-Dioxus app (a CLI, a Tauri shell, a custom renderer), you do not need this crate. Use `chimeras::pump` directly for the pause + snapshot pattern.
## Demo
The [`demo/`](../demo/) app in the parent repo exercises the full API: multi-stream grid, per-cell USB or RTSP source, live-preview toggle, take-picture button, add/remove streams, device refresh.
```bash
just run # hot-reloading dev build
just run-release # release build
```
## Dependency surface
- `dioxus` 0.7 with `desktop` feature
- `chimeras` 0.1
- `futures-timer` 3 (runtime-agnostic timer; keeps the crate from pinning tokio)
- `bytes` 1
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
- MIT license ([LICENSE-MIT](LICENSE-MIT))
at your option.