1use std::sync::{
4 Arc,
5 Mutex,
6 Weak,
7};
8
9use blocking::unblock;
10use bytes::Bytes;
11use freya_core::notify::ArcNotify;
12use nokhwa::{
13 Camera,
14 pixel_format::RgbAFormat,
15 utils::{
16 CameraFormat as NokhwaCameraFormat,
17 FrameFormat,
18 RequestedFormat,
19 RequestedFormatType,
20 Resolution,
21 },
22};
23
24use crate::camera::{
25 CameraConfig,
26 CameraError,
27 CameraFormat,
28 StreamInfo,
29};
30
31#[derive(Clone)]
33pub struct CameraFrame {
34 pub width: u32,
35 pub height: u32,
36 pub data: Bytes,
37}
38
39#[derive(Default)]
41pub struct CaptureState {
42 pub frame: Option<CameraFrame>,
43 pub info: Option<StreamInfo>,
44 pub error: Option<CameraError>,
45}
46
47pub struct CaptureHandle {
49 pub state: Arc<Mutex<CaptureState>>,
50 pub wake: ArcNotify,
51}
52
53struct CaptureProducer {
55 state: Weak<Mutex<CaptureState>>,
56 wake: ArcNotify,
57}
58
59impl CaptureProducer {
60 fn publish(&self, update: impl FnOnce(&mut CaptureState)) -> bool {
62 let Some(slot) = self.state.upgrade() else {
63 return false;
64 };
65 update(&mut slot.lock().unwrap());
66 self.wake.notify();
67 true
68 }
69}
70
71pub fn spawn_capture(config: CameraConfig) -> CaptureHandle {
73 let handle = CaptureHandle {
74 state: Arc::new(Mutex::new(CaptureState::default())),
75 wake: ArcNotify::new(),
76 };
77
78 let producer = CaptureProducer {
79 state: Arc::downgrade(&handle.state),
80 wake: handle.wake.clone(),
81 };
82
83 unblock(move || {
84 if let Err(err) = run_capture(config, &producer) {
85 producer.publish(|slot| slot.error = Some(err));
86 }
87 })
88 .detach();
89
90 handle
91}
92
93fn run_capture(config: CameraConfig, producer: &CaptureProducer) -> Result<(), CameraError> {
94 let requested = RequestedFormat::new::<RgbAFormat>(config.format.into());
95 let mut camera = Camera::new(config.device, requested)?;
96 camera.open_stream()?;
97
98 let resolution = camera.resolution();
99 let info = StreamInfo {
100 width: resolution.width(),
101 height: resolution.height(),
102 frame_rate: camera.frame_rate(),
103 };
104
105 if !producer.publish(|slot| slot.info = Some(info)) {
106 return Ok(());
107 }
108
109 loop {
110 let buffer = camera.frame()?;
111 let decoded = buffer.decode_image::<RgbAFormat>()?;
112 let new_frame = CameraFrame {
113 width: decoded.width(),
114 height: decoded.height(),
115 data: Bytes::from(decoded.into_raw()),
116 };
117
118 if !producer.publish(|slot| slot.frame = Some(new_frame)) {
119 break;
120 }
121 }
122
123 Ok(())
124}
125
126impl From<CameraFormat> for RequestedFormatType {
127 fn from(format: CameraFormat) -> Self {
128 match format {
129 CameraFormat::HighestFrameRate => Self::AbsoluteHighestFrameRate,
130 CameraFormat::HighestResolution => Self::AbsoluteHighestResolution,
131 CameraFormat::Resolution { width, height } => {
132 Self::HighestResolution(Resolution::new(width, height))
133 }
134 CameraFormat::Exact {
135 width,
136 height,
137 frame_rate,
138 } => Self::Closest(NokhwaCameraFormat::new(
139 Resolution::new(width, height),
140 FrameFormat::MJPEG,
141 frame_rate,
142 )),
143 }
144 }
145}