1use std::sync::{
25 Arc, Mutex, OnceLock,
26 atomic::{AtomicU64, Ordering},
27};
28
29use cranpose_core::{EventStream, State, rememberEventStream};
30
31use crate::registry::ServiceRegistry;
32
33#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
35pub enum FrameFormat {
36 #[default]
38 Rgba8,
39 Rgb8,
45 Nv12,
52}
53
54impl FrameFormat {
55 pub fn byte_len(self, width: u32, height: u32) -> Option<usize> {
58 let pixels = (width as usize).checked_mul(height as usize)?;
59 match self {
60 FrameFormat::Rgba8 => pixels.checked_mul(4),
61 FrameFormat::Rgb8 => pixels.checked_mul(3),
62 FrameFormat::Nv12 => {
63 if !width.is_multiple_of(2) || !height.is_multiple_of(2) {
64 return None;
65 }
66 pixels.checked_add(pixels / 2)
67 }
68 }
69 }
70}
71
72#[derive(Clone, Debug, PartialEq, Eq)]
74pub struct CameraFrame {
75 pub width: u32,
76 pub height: u32,
77 pub format: FrameFormat,
78 pub rotation_degrees: u16,
81 pub sequence: u64,
84 pub bytes: Vec<u8>,
86}
87
88impl CameraFrame {
89 pub fn new(
93 width: u32,
94 height: u32,
95 format: FrameFormat,
96 rotation_degrees: u16,
97 sequence: u64,
98 bytes: Vec<u8>,
99 ) -> Option<Self> {
100 if format.byte_len(width, height)? != bytes.len() {
101 return None;
102 }
103 Some(Self {
104 width,
105 height,
106 format,
107 rotation_degrees: rotation_degrees % 360,
108 sequence,
109 bytes,
110 })
111 }
112
113 pub fn to_rgba8(&self) -> Vec<u8> {
119 match self.format {
120 FrameFormat::Rgba8 => self.bytes.clone(),
121 FrameFormat::Rgb8 => rgb8_to_rgba8(&self.bytes),
122 FrameFormat::Nv12 => nv12_to_rgba8(self.width, self.height, &self.bytes),
123 }
124 }
125
126 pub fn upright_rgba8(&self) -> UprightRgba {
135 let rotation = self.rotation_degrees;
136 if !matches!(rotation, 90 | 180 | 270) {
137 return UprightRgba {
138 width: self.width,
139 height: self.height,
140 rgba: self.to_rgba8(),
141 };
142 }
143 let (width, height) = (self.width as usize, self.height as usize);
144 let (out_width, out_height) = match rotation {
145 90 | 270 => (self.height, self.width),
146 _ => (self.width, self.height),
147 };
148 let mut rgba = vec![0u8; width * height * 4];
149 match self.format {
150 FrameFormat::Rgba8 => {
151 for y in 0..height {
152 let row = &self.bytes[y * width * 4..(y + 1) * width * 4];
153 for x in 0..width {
154 let src = &row[x * 4..x * 4 + 4];
155 let dst = turned_index(rotation, width, height, x, y) * 4;
156 rgba[dst..dst + 4].copy_from_slice(src);
157 }
158 }
159 }
160 FrameFormat::Rgb8 => {
161 for y in 0..height {
162 let row = &self.bytes[y * width * 3..(y + 1) * width * 3];
163 for x in 0..width {
164 let src = &row[x * 3..x * 3 + 3];
165 let dst = turned_index(rotation, width, height, x, y) * 4;
166 rgba[dst..dst + 3].copy_from_slice(src);
167 rgba[dst + 3] = 255;
168 }
169 }
170 }
171 FrameFormat::Nv12 => {
172 let pixels = width * height;
173 if self.bytes.len() < pixels + pixels / 2 || width == 0 || height == 0 {
174 return UprightRgba {
175 width: out_width,
176 height: out_height,
177 rgba,
178 };
179 }
180 let (luma, chroma) = self.bytes.split_at(pixels);
181 for y in 0..height {
182 let luma_row = &luma[y * width..(y + 1) * width];
183 let chroma_row = &chroma[(y / 2) * width..(y / 2 + 1) * width];
184 for x in 0..width {
185 let luminance = luma_row[x] as i32;
186 let blue_difference = chroma_row[x & !1] as i32 - 128;
187 let red_difference = chroma_row[(x & !1) + 1] as i32 - 128;
188 let dst = turned_index(rotation, width, height, x, y) * 4;
189 rgba[dst] = clamp_byte(luminance + ((91881 * red_difference) >> 16));
190 rgba[dst + 1] = clamp_byte(
191 luminance - ((22554 * blue_difference + 46802 * red_difference) >> 16),
192 );
193 rgba[dst + 2] = clamp_byte(luminance + ((116130 * blue_difference) >> 16));
194 rgba[dst + 3] = 255;
195 }
196 }
197 }
198 }
199 UprightRgba {
200 width: out_width,
201 height: out_height,
202 rgba,
203 }
204 }
205}
206
207#[derive(Clone, Debug, PartialEq, Eq)]
212pub struct UprightRgba {
213 pub width: u32,
214 pub height: u32,
215 pub rgba: Vec<u8>,
216}
217
218#[inline]
219fn turned_index(rotation: u16, width: usize, height: usize, x: usize, y: usize) -> usize {
220 match rotation {
221 90 => x * height + (height - 1 - y),
222 180 => (height - 1 - y) * width + (width - 1 - x),
223 270 => (width - 1 - x) * height + y,
224 _ => y * width + x,
225 }
226}
227
228fn rgb8_to_rgba8(bytes: &[u8]) -> Vec<u8> {
229 let mut rgba = Vec::with_capacity(bytes.len() / 3 * 4);
230 for [red, green, blue] in bytes.as_chunks::<3>().0 {
231 rgba.extend_from_slice(&[*red, *green, *blue, 255]);
232 }
233 rgba
234}
235
236fn nv12_to_rgba8(width: u32, height: u32, bytes: &[u8]) -> Vec<u8> {
237 let (width, height) = (width as usize, height as usize);
238 let pixels = width * height;
239 let mut rgba = vec![0u8; pixels * 4];
240 if bytes.len() < pixels + pixels / 2 || width == 0 || height == 0 {
241 return rgba;
242 }
243 let (luma, chroma) = bytes.split_at(pixels);
244
245 for y in 0..height {
246 let luma_row = &luma[y * width..(y + 1) * width];
247 let chroma_row = &chroma[(y / 2) * width..(y / 2 + 1) * width];
248 let out_row = &mut rgba[y * width * 4..(y + 1) * width * 4];
249 for x in 0..width {
250 let luminance = luma_row[x] as i32;
251 let blue_difference = chroma_row[x & !1] as i32 - 128;
252 let red_difference = chroma_row[(x & !1) + 1] as i32 - 128;
253 let out = &mut out_row[x * 4..x * 4 + 4];
254 out[0] = clamp_byte(luminance + ((91881 * red_difference) >> 16));
255 out[1] =
256 clamp_byte(luminance - ((22554 * blue_difference + 46802 * red_difference) >> 16));
257 out[2] = clamp_byte(luminance + ((116130 * blue_difference) >> 16));
258 out[3] = 255;
259 }
260 }
261 rgba
262}
263
264fn clamp_byte(value: i32) -> u8 {
265 value.clamp(0, 255) as u8
266}
267
268#[derive(Clone, Debug, PartialEq, Eq)]
275pub struct CameraStill {
276 pub jpeg: Vec<u8>,
277}
278
279#[derive(Clone, Debug, PartialEq, Eq)]
286pub struct CameraLens {
287 pub id: String,
288 pub name: String,
289 pub facing: LensFacing,
292}
293
294#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
296pub enum LensFacing {
297 #[default]
299 Back,
300 Front,
302 External,
304}
305
306#[derive(Clone, Debug, Default, PartialEq, Eq)]
314pub struct CameraLenses {
315 pub lenses: Vec<CameraLens>,
317 pub active: Option<String>,
320}
321
322#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
328pub enum FlashMode {
329 #[default]
330 Off,
331 Auto,
332 On,
333}
334
335#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
336pub enum CameraError {
337 #[error("live camera capture is not supported here")]
339 Unsupported,
340 #[error("camera permission denied")]
342 PermissionDenied,
343 #[error("the camera is not running")]
345 NotRunning,
346 #[error("{0}")]
348 Failed(String),
349}
350
351#[derive(Clone, Debug, Default, PartialEq, Eq)]
353pub enum CameraState {
354 #[default]
356 Idle,
357 Starting,
360 Running {
362 device: String,
364 },
365 Stopped,
367 Failed(CameraError),
369}
370
371impl CameraState {
372 pub fn is_running(&self) -> bool {
374 matches!(self, CameraState::Running { .. })
375 }
376
377 pub fn is_active(&self) -> bool {
380 matches!(self, CameraState::Starting | CameraState::Running { .. })
381 }
382
383 pub fn failure(&self) -> Option<&CameraError> {
385 match self {
386 CameraState::Failed(error) => Some(error),
387 _ => None,
388 }
389 }
390}
391
392pub trait Camera: Send + Sync {
398 fn start(&self) -> Result<(), CameraError>;
404
405 fn stop(&self);
407
408 fn request_still(&self) -> Result<(), CameraError> {
414 Err(CameraError::Unsupported)
415 }
416
417 fn set_torch(&self, _on: bool) -> bool {
423 false
424 }
425
426 fn lenses(&self) -> Vec<CameraLens> {
435 Vec::new()
436 }
437
438 fn lens(&self) -> Option<String> {
441 None
442 }
443
444 fn use_lens(&self, _id: &str) -> bool {
447 false
448 }
449
450 fn has_flash(&self) -> bool {
452 false
453 }
454
455 fn set_flash(&self, _mode: FlashMode) -> bool {
458 false
459 }
460}
461
462pub type CameraRef = Arc<dyn Camera>;
464
465static PLATFORM_CAMERA: ServiceRegistry<dyn Camera> = ServiceRegistry::new();
466
467pub fn set_platform_camera(camera: CameraRef) {
469 PLATFORM_CAMERA.set(camera);
470}
471
472pub fn clear_platform_camera() {
475 PLATFORM_CAMERA.clear();
476 if let Ok(mut observers) = frame_observers().lock() {
477 observers.clear();
478 }
479 if let Ok(mut observers) = state_observers().lock() {
480 observers.clear();
481 }
482 if let Ok(mut observers) = still_observers().lock() {
483 observers.clear();
484 }
485 if let Ok(mut observers) = lens_observers().lock() {
486 observers.clear();
487 }
488 if let Ok(mut latest) = latest_frame_slot().lock() {
489 *latest = None;
490 }
491 if let Ok(mut state) = state_slot().lock() {
492 *state = CameraState::Idle;
493 }
494 if let Ok(mut lenses) = lenses_slot().lock() {
495 *lenses = CameraLenses::default();
496 }
497 DROPPED_FRAMES.store(0, Ordering::Release);
498}
499
500pub fn camera() -> Option<CameraRef> {
503 PLATFORM_CAMERA.get()
504}
505
506pub fn camera_supported() -> bool {
508 PLATFORM_CAMERA.get().is_some()
509}
510
511fn state_slot() -> &'static Mutex<CameraState> {
512 static SLOT: OnceLock<Mutex<CameraState>> = OnceLock::new();
513 SLOT.get_or_init(|| Mutex::new(CameraState::Idle))
514}
515
516fn latest_frame_slot() -> &'static Mutex<Option<CameraFrame>> {
517 static SLOT: OnceLock<Mutex<Option<CameraFrame>>> = OnceLock::new();
518 SLOT.get_or_init(|| Mutex::new(None))
519}
520
521fn lenses_slot() -> &'static Mutex<CameraLenses> {
522 static SLOT: OnceLock<Mutex<CameraLenses>> = OnceLock::new();
523 SLOT.get_or_init(|| Mutex::new(CameraLenses::default()))
524}
525
526static DROPPED_FRAMES: AtomicU64 = AtomicU64::new(0);
527
528type FrameObserver = Arc<dyn Fn(CameraFrame) + Send + Sync>;
529type StateObserver = Arc<dyn Fn(CameraState) + Send + Sync>;
530type StillObserver = Arc<dyn Fn(Result<CameraStill, CameraError>) + Send + Sync>;
531type LensObserver = Arc<dyn Fn(CameraLenses) + Send + Sync>;
532
533fn frame_observers() -> &'static Mutex<Vec<(u64, FrameObserver)>> {
534 static SLOT: OnceLock<Mutex<Vec<(u64, FrameObserver)>>> = OnceLock::new();
535 SLOT.get_or_init(|| Mutex::new(Vec::new()))
536}
537
538fn state_observers() -> &'static Mutex<Vec<(u64, StateObserver)>> {
539 static SLOT: OnceLock<Mutex<Vec<(u64, StateObserver)>>> = OnceLock::new();
540 SLOT.get_or_init(|| Mutex::new(Vec::new()))
541}
542
543fn still_observers() -> &'static Mutex<Vec<(u64, StillObserver)>> {
544 static SLOT: OnceLock<Mutex<Vec<(u64, StillObserver)>>> = OnceLock::new();
545 SLOT.get_or_init(|| Mutex::new(Vec::new()))
546}
547
548fn lens_observers() -> &'static Mutex<Vec<(u64, LensObserver)>> {
549 static SLOT: OnceLock<Mutex<Vec<(u64, LensObserver)>>> = OnceLock::new();
550 SLOT.get_or_init(|| Mutex::new(Vec::new()))
551}
552
553static NEXT_OBSERVER: AtomicU64 = AtomicU64::new(1);
554
555pub struct CameraObserver {
557 id: u64,
558 kind: ObserverKind,
559}
560
561#[derive(Clone, Copy)]
562enum ObserverKind {
563 Frame,
564 State,
565 Still,
566 Lenses,
567}
568
569impl Drop for CameraObserver {
570 fn drop(&mut self) {
571 match self.kind {
572 ObserverKind::Frame => retain_without(frame_observers(), self.id),
573 ObserverKind::State => retain_without(state_observers(), self.id),
574 ObserverKind::Still => retain_without(still_observers(), self.id),
575 ObserverKind::Lenses => retain_without(lens_observers(), self.id),
576 }
577 }
578}
579
580fn retain_without<T>(slot: &'static Mutex<Vec<(u64, T)>>, id: u64) {
581 if let Ok(mut observers) = slot.lock() {
582 observers.retain(|(observer, _)| *observer != id);
583 }
584}
585
586fn snapshot<T: Clone>(slot: &'static Mutex<Vec<(u64, T)>>) -> Vec<T> {
587 slot.lock()
588 .map(|observers| {
589 observers
590 .iter()
591 .map(|(_, observer)| observer.clone())
592 .collect()
593 })
594 .unwrap_or_default()
595}
596
597pub fn latest_camera_frame() -> Option<CameraFrame> {
602 latest_frame_slot()
603 .lock()
604 .map_or(None, |frame| frame.clone())
605}
606
607pub fn camera_state() -> CameraState {
609 state_slot()
610 .lock()
611 .map(|state| state.clone())
612 .unwrap_or_default()
613}
614
615pub fn dropped_camera_frames() -> u64 {
617 DROPPED_FRAMES.load(Ordering::Acquire)
618}
619
620pub fn publish_camera_frame(frame: CameraFrame) {
627 if let Ok(mut latest) = latest_frame_slot().lock() {
628 *latest = Some(frame.clone());
629 }
630 let observers = snapshot(frame_observers());
631 if observers.is_empty() {
632 return;
633 }
634 for observer in observers {
635 observer(frame.clone());
636 }
637}
638
639pub fn record_dropped_camera_frame() {
644 DROPPED_FRAMES.fetch_add(1, Ordering::AcqRel);
645}
646
647pub fn publish_camera_state(state: CameraState) {
649 {
650 let Ok(mut current) = state_slot().lock() else {
651 return;
652 };
653 if *current == state {
654 return;
655 }
656 *current = state.clone();
657 }
658 if matches!(state, CameraState::Idle | CameraState::Starting) {
659 DROPPED_FRAMES.store(0, Ordering::Release);
660 if let Ok(mut latest) = latest_frame_slot().lock() {
661 *latest = None;
662 }
663 }
664 for observer in snapshot(state_observers()) {
665 observer(state.clone());
666 }
667}
668
669pub fn publish_camera_still(still: Result<CameraStill, CameraError>) {
671 for observer in snapshot(still_observers()) {
672 observer(still.clone());
673 }
674}
675
676pub fn publish_camera_lenses(lenses: CameraLenses) {
679 {
680 let Ok(mut current) = lenses_slot().lock() else {
681 return;
682 };
683 if *current == lenses {
684 return;
685 }
686 *current = lenses.clone();
687 }
688 for observer in snapshot(lens_observers()) {
689 observer(lenses.clone());
690 }
691}
692
693pub fn camera_lenses() -> CameraLenses {
696 lenses_slot()
697 .lock()
698 .map(|lenses| lenses.clone())
699 .unwrap_or_default()
700}
701
702pub fn observe_camera_frames(
705 observer: impl Fn(CameraFrame) + Send + Sync + 'static,
706) -> CameraObserver {
707 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
708 if let Ok(mut observers) = frame_observers().lock() {
709 observers.push((id, Arc::new(observer)));
710 }
711 CameraObserver {
712 id,
713 kind: ObserverKind::Frame,
714 }
715}
716
717pub fn observe_camera_state(
721 observer: impl Fn(CameraState) + Send + Sync + 'static,
722) -> CameraObserver {
723 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
724 let observer: StateObserver = Arc::new(observer);
725 if let Ok(mut observers) = state_observers().lock() {
726 observers.push((id, Arc::clone(&observer)));
727 }
728 observer(camera_state());
729 CameraObserver {
730 id,
731 kind: ObserverKind::State,
732 }
733}
734
735pub fn observe_camera_stills(
737 observer: impl Fn(Result<CameraStill, CameraError>) + Send + Sync + 'static,
738) -> CameraObserver {
739 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
740 if let Ok(mut observers) = still_observers().lock() {
741 observers.push((id, Arc::new(observer)));
742 }
743 CameraObserver {
744 id,
745 kind: ObserverKind::Still,
746 }
747}
748
749pub fn observe_camera_lenses(
753 observer: impl Fn(CameraLenses) + Send + Sync + 'static,
754) -> CameraObserver {
755 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
756 let observer: LensObserver = Arc::new(observer);
757 if let Ok(mut observers) = lens_observers().lock() {
758 observers.push((id, Arc::clone(&observer)));
759 }
760 observer(camera_lenses());
761 CameraObserver {
762 id,
763 kind: ObserverKind::Lenses,
764 }
765}
766
767#[allow(non_snake_case)]
770#[track_caller]
771pub fn rememberCameraState() -> State<CameraState> {
772 let updates = rememberEventStream((), |sender| {
773 observe_camera_state(move |state| sender.send(state))
774 });
775 cranpose_core::collectAsState(updates, (), camera_state())
776}
777
778#[allow(non_snake_case)]
785#[track_caller]
786pub fn rememberCameraFrames() -> EventStream<CameraFrame> {
787 rememberEventStream((), |sender| {
788 observe_camera_frames(move |frame| sender.send(frame))
789 })
790}
791
792#[allow(non_snake_case)]
794#[track_caller]
795pub fn rememberCameraStills() -> EventStream<Result<CameraStill, CameraError>> {
796 rememberEventStream((), |sender| {
797 observe_camera_stills(move |still| sender.send(still))
798 })
799}
800
801#[allow(non_snake_case)]
804#[track_caller]
805pub fn rememberCameraLenses() -> State<CameraLenses> {
806 let updates = rememberEventStream((), |sender| {
807 observe_camera_lenses(move |lenses| sender.send(lenses))
808 });
809 cranpose_core::collectAsState(updates, (), camera_lenses())
810}
811
812pub fn start_camera() -> Result<(), CameraError> {
815 let Some(camera) = camera() else {
816 publish_camera_state(CameraState::Failed(CameraError::Unsupported));
817 return Err(CameraError::Unsupported);
818 };
819 publish_camera_state(CameraState::Starting);
820 camera.start().inspect_err(|error| {
821 publish_camera_state(CameraState::Failed(error.clone()));
822 })
823}
824
825pub fn stop_camera() {
827 if let Some(camera) = camera() {
828 camera.stop();
829 }
830 publish_camera_state(CameraState::Stopped);
831}
832
833pub fn request_camera_still() -> Result<(), CameraError> {
836 let Some(camera) = camera() else {
837 return Err(CameraError::Unsupported);
838 };
839 if !camera_state().is_running() {
840 return Err(CameraError::NotRunning);
841 }
842 camera.request_still()
843}
844
845pub async fn capture_camera_still() -> Result<CameraStill, CameraError> {
855 let signal = crate::async_io::Signal::new();
856 let deliver = signal.clone();
857 let observer = observe_camera_stills(move |result| deliver.set(result));
858 request_camera_still()?;
859 let arrived = signal.wait().await;
860 drop(observer);
861 arrived.unwrap_or(Err(CameraError::NotRunning))
862}
863
864#[cfg(test)]
865mod tests {
866 use std::sync::PoisonError;
867
868 use super::*;
869
870 struct FakeCamera {
871 started: AtomicU64,
872 stopped: AtomicU64,
873 stills: AtomicU64,
874 fails: bool,
875 }
876
877 impl FakeCamera {
878 fn new() -> Arc<Self> {
879 Arc::new(Self {
880 started: AtomicU64::new(0),
881 stopped: AtomicU64::new(0),
882 stills: AtomicU64::new(0),
883 fails: false,
884 })
885 }
886
887 fn failing() -> Arc<Self> {
888 Arc::new(Self {
889 started: AtomicU64::new(0),
890 stopped: AtomicU64::new(0),
891 stills: AtomicU64::new(0),
892 fails: true,
893 })
894 }
895 }
896
897 impl Camera for FakeCamera {
898 fn start(&self) -> Result<(), CameraError> {
899 self.started.fetch_add(1, Ordering::Relaxed);
900 if self.fails {
901 return Err(CameraError::PermissionDenied);
902 }
903 publish_camera_state(CameraState::Running {
904 device: "fake".to_string(),
905 });
906 Ok(())
907 }
908
909 fn stop(&self) {
910 self.stopped.fetch_add(1, Ordering::Relaxed);
911 }
912
913 fn request_still(&self) -> Result<(), CameraError> {
914 self.stills.fetch_add(1, Ordering::Relaxed);
915 publish_camera_still(Ok(CameraStill {
916 jpeg: vec![0xff, 0xd8],
917 }));
918 Ok(())
919 }
920 }
921
922 fn rgba_frame(sequence: u64) -> CameraFrame {
923 CameraFrame::new(2, 2, FrameFormat::Rgba8, 90, sequence, vec![7; 16])
924 .expect("a well-formed frame")
925 }
926
927 #[test]
928 fn a_frame_size_is_the_one_its_format_implies() {
929 assert_eq!(FrameFormat::Rgba8.byte_len(4, 2), Some(32));
930 assert_eq!(FrameFormat::Nv12.byte_len(4, 2), Some(12));
931 assert_eq!(
932 FrameFormat::Nv12.byte_len(3, 2),
933 None,
934 "NV12 has no half column for an odd width"
935 );
936 assert_eq!(FrameFormat::Nv12.byte_len(4, 3), None);
937 }
938
939 #[test]
940 fn a_frame_that_does_not_match_its_size_is_refused() {
941 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 15]).is_none());
942 assert!(CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, vec![0; 5]).is_none());
943 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 16]).is_some());
944 }
945
946 #[test]
947 fn a_rotation_is_kept_inside_one_turn() {
948 let frame = CameraFrame::new(2, 2, FrameFormat::Rgba8, 450, 0, vec![0; 16])
949 .expect("a well-formed frame");
950 assert_eq!(frame.rotation_degrees, 90);
951 }
952
953 #[test]
954 fn an_rgba_frame_is_handed_over_unchanged() {
955 let frame = rgba_frame(0);
956 assert_eq!(frame.to_rgba8(), frame.bytes);
957 }
958
959 #[test]
960 fn nv12_black_white_and_primaries_convert_to_the_expected_colours() {
961 fn convert(luma: u8, blue: u8, red: u8) -> [u8; 4] {
962 let bytes = vec![luma, luma, luma, luma, blue, red];
963 let frame = CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, bytes)
964 .expect("a well-formed frame");
965 let rgba = frame.to_rgba8();
966 [rgba[0], rgba[1], rgba[2], rgba[3]]
967 }
968
969 assert_eq!(convert(0, 128, 128), [0, 0, 0, 255], "black");
970 assert_eq!(convert(255, 128, 128), [255, 255, 255, 255], "white");
971
972 let red = convert(76, 84, 255);
973 assert!(
974 red[0] > 240 && red[1] < 20 && red[2] < 20,
975 "red, got {red:?}"
976 );
977 let blue = convert(29, 255, 107);
978 assert!(
979 blue[2] > 240 && blue[0] < 20 && blue[1] < 20,
980 "blue, got {blue:?}"
981 );
982 assert!(
983 convert(128, 128, 128).iter().all(|value| *value > 0),
984 "a grey frame must not clamp to black"
985 );
986 }
987
988 #[test]
989 fn a_short_nv12_frame_converts_to_black_rather_than_reading_past_its_end() {
990 let rgba = nv12_to_rgba8(4, 4, &[0u8; 3]);
991 assert_eq!(rgba.len(), 4 * 4 * 4);
992 assert!(rgba.iter().all(|value| *value == 0));
993 }
994
995 #[test]
996 fn a_platform_without_a_camera_says_so_rather_than_pretending() {
997 let _guard = crate::registry::test_service_guard();
998 clear_platform_camera();
999 assert!(!camera_supported());
1000 assert_eq!(start_camera(), Err(CameraError::Unsupported));
1001 assert_eq!(
1002 camera_state(),
1003 CameraState::Failed(CameraError::Unsupported)
1004 );
1005 assert_eq!(request_camera_still(), Err(CameraError::Unsupported));
1006 clear_platform_camera();
1007 }
1008
1009 #[test]
1010 fn the_session_reports_starting_before_it_reports_running() {
1011 let _guard = crate::registry::test_service_guard();
1012 clear_platform_camera();
1013 let seen = Arc::new(Mutex::new(Vec::new()));
1014 let recorder = Arc::clone(&seen);
1015 let observer = observe_camera_state(move |state| {
1016 recorder
1017 .lock()
1018 .unwrap_or_else(PoisonError::into_inner)
1019 .push(state);
1020 });
1021 set_platform_camera(FakeCamera::new());
1022 start_camera().expect("the session starts");
1023
1024 assert_eq!(
1025 *seen.lock().unwrap_or_else(PoisonError::into_inner),
1026 vec![
1027 CameraState::Idle,
1028 CameraState::Starting,
1029 CameraState::Running {
1030 device: "fake".to_string()
1031 }
1032 ]
1033 );
1034 assert!(camera_state().is_running());
1035 assert!(camera_state().is_active());
1036 drop(observer);
1037 clear_platform_camera();
1038 }
1039
1040 #[test]
1041 fn a_session_that_cannot_open_reports_why() {
1042 let _guard = crate::registry::test_service_guard();
1043 clear_platform_camera();
1044 set_platform_camera(FakeCamera::failing());
1045 assert_eq!(start_camera(), Err(CameraError::PermissionDenied));
1046 assert_eq!(
1047 camera_state().failure(),
1048 Some(&CameraError::PermissionDenied)
1049 );
1050 assert!(!camera_state().is_active());
1051 clear_platform_camera();
1052 }
1053
1054 #[test]
1055 fn stopping_releases_the_device_and_says_so() {
1056 let _guard = crate::registry::test_service_guard();
1057 clear_platform_camera();
1058 let backend = FakeCamera::new();
1059 set_platform_camera(backend.clone());
1060 start_camera().expect("the session starts");
1061 stop_camera();
1062 assert_eq!(backend.stopped.load(Ordering::Relaxed), 1);
1063 assert_eq!(camera_state(), CameraState::Stopped);
1064 clear_platform_camera();
1065 }
1066
1067 #[test]
1068 fn the_stored_frame_is_always_the_newest_one() {
1069 let _guard = crate::registry::test_service_guard();
1070 clear_platform_camera();
1071 assert_eq!(latest_camera_frame(), None);
1072 publish_camera_frame(rgba_frame(1));
1073 publish_camera_frame(rgba_frame(2));
1074 publish_camera_frame(rgba_frame(3));
1075 assert_eq!(latest_camera_frame().map(|frame| frame.sequence), Some(3));
1076 clear_platform_camera();
1077 }
1078
1079 #[test]
1080 fn frame_observers_see_frames_and_stop_when_dropped() {
1081 let _guard = crate::registry::test_service_guard();
1082 clear_platform_camera();
1083 let seen = Arc::new(Mutex::new(Vec::new()));
1084 let recorder = Arc::clone(&seen);
1085 let observer = observe_camera_frames(move |frame| {
1086 recorder
1087 .lock()
1088 .unwrap_or_else(PoisonError::into_inner)
1089 .push(frame.sequence);
1090 });
1091 publish_camera_frame(rgba_frame(1));
1092 publish_camera_frame(rgba_frame(2));
1093 drop(observer);
1094 publish_camera_frame(rgba_frame(3));
1095 assert_eq!(
1096 *seen.lock().unwrap_or_else(PoisonError::into_inner),
1097 vec![1, 2]
1098 );
1099 clear_platform_camera();
1100 }
1101
1102 #[test]
1103 fn frames_the_platform_could_not_deliver_are_counted_rather_than_queued() {
1104 let _guard = crate::registry::test_service_guard();
1105 clear_platform_camera();
1106 assert_eq!(dropped_camera_frames(), 0);
1107 record_dropped_camera_frame();
1108 record_dropped_camera_frame();
1109 assert_eq!(dropped_camera_frames(), 2);
1110 publish_camera_state(CameraState::Starting);
1111 assert_eq!(dropped_camera_frames(), 0);
1112 assert_eq!(latest_camera_frame(), None);
1113 clear_platform_camera();
1114 }
1115
1116 #[test]
1117 fn a_still_is_asked_for_and_arrives_separately() {
1118 let _guard = crate::registry::test_service_guard();
1119 clear_platform_camera();
1120 let backend = FakeCamera::new();
1121 set_platform_camera(backend.clone());
1122
1123 assert_eq!(
1124 request_camera_still(),
1125 Err(CameraError::NotRunning),
1126 "nothing is running, so there is nothing to photograph"
1127 );
1128
1129 let seen = Arc::new(Mutex::new(Vec::new()));
1130 let recorder = Arc::clone(&seen);
1131 let observer = observe_camera_stills(move |still| {
1132 recorder
1133 .lock()
1134 .unwrap_or_else(PoisonError::into_inner)
1135 .push(still);
1136 });
1137 start_camera().expect("the session starts");
1138 request_camera_still().expect("a still is asked for");
1139 assert_eq!(backend.stills.load(Ordering::Relaxed), 1);
1140 assert_eq!(
1141 *seen.lock().unwrap_or_else(PoisonError::into_inner),
1142 vec![Ok(CameraStill {
1143 jpeg: vec![0xff, 0xd8]
1144 })]
1145 );
1146 drop(observer);
1147 clear_platform_camera();
1148 }
1149
1150 #[test]
1151 fn a_backend_that_lists_no_lens_and_no_flash_says_so() {
1152 let _guard = crate::registry::test_service_guard();
1153 clear_platform_camera();
1154 set_platform_camera(FakeCamera::new());
1155 let backend = camera().expect("registered");
1156 assert!(backend.lenses().is_empty());
1157 assert_eq!(backend.lens(), None);
1158 assert!(!backend.use_lens("0"));
1159 assert!(!backend.has_flash());
1160 assert!(!backend.set_flash(FlashMode::On));
1161 assert!(!backend.set_torch(true));
1162 clear_platform_camera();
1163 }
1164
1165 #[test]
1166 fn a_backend_that_lists_two_lenses_hands_them_over_in_order() {
1167 let _guard = crate::registry::test_service_guard();
1168 clear_platform_camera();
1169 struct TwoLenses;
1170 impl Camera for TwoLenses {
1171 fn start(&self) -> Result<(), CameraError> {
1172 Ok(())
1173 }
1174 fn stop(&self) {}
1175 fn lenses(&self) -> Vec<CameraLens> {
1176 vec![
1177 CameraLens {
1178 id: "u".into(),
1179 name: "Ultra wide".into(),
1180 facing: LensFacing::Back,
1181 },
1182 CameraLens {
1183 id: "w".into(),
1184 name: "Wide".into(),
1185 facing: LensFacing::Back,
1186 },
1187 ]
1188 }
1189 fn lens(&self) -> Option<String> {
1190 Some("w".into())
1191 }
1192 fn use_lens(&self, id: &str) -> bool {
1193 id == "u" || id == "w"
1194 }
1195 }
1196 set_platform_camera(Arc::new(TwoLenses));
1197 let backend = camera().expect("registered");
1198 let lenses = backend.lenses();
1199 assert_eq!(lenses.len(), 2);
1200 assert_eq!(lenses[0].name, "Ultra wide");
1201 assert_eq!(backend.lens().as_deref(), Some("w"));
1202 assert!(backend.use_lens("u"));
1203 assert!(!backend.use_lens("tele"));
1204 clear_platform_camera();
1205 }
1206
1207 fn two_pixel_frame(rotation: u16) -> CameraFrame {
1208 let mut bytes = vec![10u8, 10, 10, 255];
1209 bytes.extend_from_slice(&[20, 20, 20, 255]);
1210 CameraFrame::new(2, 1, FrameFormat::Rgba8, rotation, 0, bytes).expect("a well-formed frame")
1211 }
1212
1213 fn pixel_values(image: &UprightRgba) -> Vec<u8> {
1214 image.rgba.iter().step_by(4).copied().collect()
1215 }
1216
1217 #[test]
1218 fn a_frame_turns_upright_by_its_rotation() {
1219 let unturned = two_pixel_frame(0).upright_rgba8();
1220 assert_eq!((unturned.width, unturned.height), (2, 1));
1221 assert_eq!(pixel_values(&unturned), vec![10, 20]);
1222
1223 let quarter = two_pixel_frame(90).upright_rgba8();
1224 assert_eq!((quarter.width, quarter.height), (1, 2));
1225 assert_eq!(pixel_values(&quarter), vec![10, 20]);
1226
1227 let half = two_pixel_frame(180).upright_rgba8();
1228 assert_eq!((half.width, half.height), (2, 1));
1229 assert_eq!(pixel_values(&half), vec![20, 10]);
1230
1231 let three_quarters = two_pixel_frame(270).upright_rgba8();
1232 assert_eq!((three_quarters.width, three_quarters.height), (1, 2));
1233 assert_eq!(pixel_values(&three_quarters), vec![20, 10]);
1234 }
1235
1236 #[test]
1237 fn an_nv12_frame_turns_and_converts_in_one_pass() {
1238 let bytes = vec![0, 255, 0, 0, 128, 128];
1239 let frame =
1240 CameraFrame::new(2, 2, FrameFormat::Nv12, 90, 0, bytes).expect("a well-formed frame");
1241 let upright = frame.upright_rgba8();
1242 assert_eq!((upright.width, upright.height), (2, 2));
1243 let values = pixel_values(&upright);
1244 assert_eq!(values[0], 0, "top-left stays dark");
1245 assert_eq!(
1246 values[3], 255,
1247 "the bright top-right pixel lands bottom-right"
1248 );
1249 }
1250
1251 #[test]
1252 fn an_rgb8_frame_turns_upright_with_a_full_alpha() {
1253 let frame = CameraFrame::new(
1254 2,
1255 1,
1256 FrameFormat::Rgb8,
1257 180,
1258 0,
1259 vec![10, 10, 10, 20, 20, 20],
1260 )
1261 .expect("a well-formed frame");
1262 let upright = frame.upright_rgba8();
1263 assert_eq!(pixel_values(&upright), vec![20, 10]);
1264 assert!(upright.rgba.iter().skip(3).step_by(4).all(|a| *a == 255));
1265 }
1266
1267 #[test]
1268 fn a_turn_that_is_not_a_quarter_is_left_alone() {
1269 let frame = CameraFrame::new(2, 1, FrameFormat::Rgba8, 45, 0, vec![7; 8])
1270 .expect("a well-formed frame");
1271 let upright = frame.upright_rgba8();
1272 assert_eq!((upright.width, upright.height), (2, 1));
1273 assert_eq!(upright.rgba, frame.to_rgba8());
1274 }
1275
1276 #[test]
1277 fn the_lens_list_is_published_and_observed() {
1278 let _guard = crate::registry::test_service_guard();
1279 clear_platform_camera();
1280 assert_eq!(camera_lenses(), CameraLenses::default());
1281
1282 let seen = Arc::new(Mutex::new(Vec::new()));
1283 let recorder = Arc::clone(&seen);
1284 let observer = observe_camera_lenses(move |lenses| {
1285 recorder
1286 .lock()
1287 .unwrap_or_else(PoisonError::into_inner)
1288 .push(lenses);
1289 });
1290
1291 let published = CameraLenses {
1292 lenses: vec![CameraLens {
1293 id: "0".into(),
1294 name: "Back".into(),
1295 facing: LensFacing::Back,
1296 }],
1297 active: Some("0".into()),
1298 };
1299 publish_camera_lenses(published.clone());
1300 publish_camera_lenses(published.clone());
1301 assert_eq!(camera_lenses(), published);
1302 assert_eq!(
1303 *seen.lock().unwrap_or_else(PoisonError::into_inner),
1304 vec![CameraLenses::default(), published],
1305 "the current list arrives at once, and a repeat is not re-delivered"
1306 );
1307
1308 drop(observer);
1309 publish_camera_lenses(CameraLenses::default());
1310 assert_eq!(
1311 seen.lock().unwrap_or_else(PoisonError::into_inner).len(),
1312 2,
1313 "a dropped observer hears nothing more"
1314 );
1315 clear_platform_camera();
1316 assert_eq!(camera_lenses(), CameraLenses::default());
1317 }
1318}