egui-cameras 0.3.1

egui / eframe integration for the cameras crate. Wraps a cameras::pump::Pump into an egui TextureHandle so you can render live camera frames as an egui::Image with a few lines of glue.
Documentation
<h1 align="center">egui-cameras</h1>

<p align="center">
  <a href="https://github.com/matthewjberger/cameras"><img alt="github" src="https://img.shields.io/badge/github-matthewjberger/cameras-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20"></a>
  <a href="https://crates.io/crates/egui-cameras"><img alt="crates.io" src="https://img.shields.io/crates/v/egui-cameras.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20"></a>
  <a href="https://docs.rs/egui-cameras"><img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-egui--cameras-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20"></a>
  <a href="https://github.com/matthewjberger/cameras/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 live camera streams into your egui / eframe app.</strong></p>

<p align="center">
  <code>cargo add egui-cameras</code>
</p>

`egui-cameras` is the egui integration for [`cameras`](https://crates.io/crates/cameras), a cross-platform camera library for Rust. It owns the thin glue between a running `cameras::pump::Pump` and an `egui::TextureHandle`, so you can render live camera frames as an `egui::Image` with a few lines of code.

Every camera-side primitive (pause / resume pump, single-frame capture, unified `CameraSource`, hotplug monitor) lives upstream in `cameras` itself and is re-exported from this crate as `egui_cameras::cameras`, so a single dependency is enough.

## Quick Start

```toml
[dependencies]
egui-cameras = "0.1"
eframe = "0.32"
```

```rust
use egui_cameras::cameras::{self, PixelFormat, Resolution, StreamConfig};
use eframe::egui;

struct App {
    stream: egui_cameras::Stream,
}

impl App {
    fn new() -> Result<Self, cameras::Error> {
        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();
    }
}
```

## What's in the box

| Item | Purpose |
|------|---------|
| `Stream` | Bundle of a `Pump` + `Sink` + `TextureHandle`. Holds everything one live camera needs. |
| `Sink` | Shared slot the pump writes each frame into. Cheap to clone. |
| `spawn(camera) -> Stream` | Convenience: spawn a pump and wire it to a fresh `Stream` with a default texture name. |
| `spawn_named(camera, name) -> Stream` | Like `spawn`, but lets you name the texture (useful for multi-camera apps). |
| `spawn_pump(camera, sink) -> Pump` | Lower-level: spawn a pump that writes into your own `Sink`. |
| `publish_frame(sink, frame)` | Write a frame into a sink (for custom pump code). |
| `take_frame(sink) -> Option<Frame>` | Pull the latest frame out of a sink. |
| `frame_to_color_image(frame)` | Convert a cameras `Frame` into an `egui::ColorImage`. |
| `update_texture(stream, ctx)` | Upload the latest frame to the stream's texture. Call each `update` tick. |
| `show(stream, ui)` | Draw the texture as an `egui::Image` scaled to fit the available area. |

Pump controls (`set_active`, `capture_frame`, `stop_and_join`) are re-exported directly from `cameras::pump`.

## Pause + snapshot

Pause the pump when the user isn't looking, grab fresh frames on demand without closing the camera.

```rust
use egui_cameras::{capture_frame, set_active};

// Park the pump (no per-frame Rust work; camera stays open):
set_active(&stream.pump, false);

// Grab a fresh snapshot regardless of pause state:
let frame = capture_frame(&stream.pump);
```

## Features

| Feature | Default | Description |
|---------|:-------:|-------------|
| `rtsp` | off | Forwards to `cameras/rtsp`; enables `CameraSource::Rtsp` on macOS and Windows. |
| `discover` | off | Forwards to `cameras/discover`; enables the `DiscoveryWidget` for scanning a subnet for Axis RTSP cameras. |

## Discovery

With the `discover` feature, `start_discovery(config)` returns a `DiscoverySession`. Call `poll_discovery(&mut session)` each frame; it is non-blocking and safe to call infrequently (the underlying channel is unbounded). Render results with either the bundled `show_discovery(&session, ui)`, or the split helpers `show_discovery_status` + `show_discovery_results` when you want the status and result list in different UI locations. A click yields a `DiscoveredCamera` that can be passed to `cameras::open_source`. Drop the session (or call `cancel_discovery(session)`) to cancel the scan.

`DiscoverySession`'s observable fields are public: `cameras`, `unmatched_hosts: Vec<(IpAddr, String)>`, `scanned`, `total`, `done`. The underlying `Discovery` handle is intentionally private so callers cannot accidentally steal events from `poll_discovery`'s drain path.

Configs mix CIDR `subnets` and explicit `host:port` `endpoints`, so port-forwarded tunnels work alongside a LAN scan. The `apps/egui-demo` Discover panel accepts comma-separated CIDRs and/or `host:port` entries in its Targets field.

## Versioning

`egui-cameras` pins a specific minor version of `cameras` via `pub use cameras;`, so installing `egui-cameras = "0.1"` gives you a compatible `cameras` automatically.

## Example

A runnable example lives at [`apps/egui-demo`](https://github.com/matthewjberger/cameras/tree/main/apps/egui-demo) in the repository: live preview, pause toggle, take-picture button.

## License

Dual-licensed under MIT or Apache-2.0 at your option.