Skip to main content

freya_camera/
use_camera.rs

1//! [`use_camera`] hook and the [`Camera`] handle.
2
3use freya_core::{
4    elements::image::ImageHandle,
5    prelude::*,
6};
7use freya_engine::prelude::AlphaType;
8
9use crate::{
10    camera::{
11        CameraConfig,
12        CameraError,
13        StreamInfo,
14    },
15    capture::{
16        CameraFrame,
17        CaptureHandle,
18        CaptureState,
19        spawn_capture,
20    },
21};
22
23/// Handle to a running camera. Closed when its owning scope is dropped.
24#[derive(Clone, Copy, PartialEq)]
25pub struct Camera {
26    /// The latest frame produced by the camera.
27    pub frame: State<Option<ImageHandle>>,
28    /// The resolution and frame rate negotiated with the device.
29    pub info: State<Option<StreamInfo>>,
30    /// The most recent error, if any.
31    pub error: State<Option<CameraError>>,
32}
33
34impl Camera {
35    /// Open a camera and start streaming frames into reactive state.
36    pub fn create(config: CameraConfig) -> Self {
37        let mut frame: State<Option<ImageHandle>> = State::create(None);
38        let mut info: State<Option<StreamInfo>> = State::create(None);
39        let mut error: State<Option<CameraError>> = State::create(None);
40
41        let CaptureHandle { state, wake } = spawn_capture(config);
42
43        spawn(async move {
44            loop {
45                wake.notified().await;
46
47                let CaptureState {
48                    frame: latest_frame,
49                    info: latest_info,
50                    error: latest_error,
51                } = std::mem::take(&mut *state.lock().unwrap());
52
53                if let Some(stream_info) = latest_info {
54                    *info.write() = Some(stream_info);
55                }
56                if let Some(capture_error) = latest_error {
57                    tracing::warn!("freya-camera: {capture_error}");
58                    *error.write() = Some(capture_error);
59                }
60                if let Some(camera_frame) = latest_frame {
61                    match build_handle(camera_frame) {
62                        Ok(handle) => *frame.write() = Some(handle),
63                        Err(build_error) => {
64                            tracing::warn!("freya-camera: {build_error}");
65                            *error.write() = Some(build_error);
66                        }
67                    }
68                }
69            }
70        });
71
72        Self { frame, info, error }
73    }
74}
75
76/// Open a camera and return a [`Camera`] handle. `init` runs once on mount.
77///
78/// # Example
79///
80/// ```rust, no_run
81/// use freya::{
82///     camera::*,
83///     prelude::*,
84/// };
85///
86/// fn app() -> impl IntoElement {
87///     let camera = use_camera(CameraConfig::default);
88///
89///     rect().center().expanded().child(CameraViewer::new(camera))
90/// }
91/// ```
92pub fn use_camera(init: impl FnOnce() -> CameraConfig) -> Camera {
93    use_hook(|| Camera::create(init()))
94}
95
96/// Build an [`ImageHandle`] from a raw `RGBA8` camera frame.
97fn build_handle(frame: CameraFrame) -> Result<ImageHandle, CameraError> {
98    let CameraFrame {
99        width,
100        height,
101        data,
102    } = frame;
103
104    ImageHandle::from_rgba(width, height, data, AlphaType::Opaque)
105        .ok_or_else(|| CameraError::GeneralError("failed to create raster image".to_string()))
106}