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(|frame| frame.clone())
605 .unwrap_or(None)
606}
607
608pub fn camera_state() -> CameraState {
610 state_slot()
611 .lock()
612 .map(|state| state.clone())
613 .unwrap_or_default()
614}
615
616pub fn dropped_camera_frames() -> u64 {
618 DROPPED_FRAMES.load(Ordering::Acquire)
619}
620
621pub fn publish_camera_frame(frame: CameraFrame) {
628 if let Ok(mut latest) = latest_frame_slot().lock() {
629 *latest = Some(frame.clone());
630 }
631 let observers = snapshot(frame_observers());
632 if observers.is_empty() {
633 return;
634 }
635 for observer in observers {
636 observer(frame.clone());
637 }
638}
639
640pub fn record_dropped_camera_frame() {
645 DROPPED_FRAMES.fetch_add(1, Ordering::AcqRel);
646}
647
648pub fn publish_camera_state(state: CameraState) {
650 {
651 let Ok(mut current) = state_slot().lock() else {
652 return;
653 };
654 if *current == state {
655 return;
656 }
657 *current = state.clone();
658 }
659 if matches!(state, CameraState::Idle | CameraState::Starting) {
660 DROPPED_FRAMES.store(0, Ordering::Release);
661 if let Ok(mut latest) = latest_frame_slot().lock() {
662 *latest = None;
663 }
664 }
665 for observer in snapshot(state_observers()) {
666 observer(state.clone());
667 }
668}
669
670pub fn publish_camera_still(still: Result<CameraStill, CameraError>) {
672 for observer in snapshot(still_observers()) {
673 observer(still.clone());
674 }
675}
676
677pub fn publish_camera_lenses(lenses: CameraLenses) {
680 {
681 let Ok(mut current) = lenses_slot().lock() else {
682 return;
683 };
684 if *current == lenses {
685 return;
686 }
687 *current = lenses.clone();
688 }
689 for observer in snapshot(lens_observers()) {
690 observer(lenses.clone());
691 }
692}
693
694pub fn camera_lenses() -> CameraLenses {
697 lenses_slot()
698 .lock()
699 .map(|lenses| lenses.clone())
700 .unwrap_or_default()
701}
702
703pub fn observe_camera_frames(
706 observer: impl Fn(CameraFrame) + Send + Sync + 'static,
707) -> CameraObserver {
708 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
709 if let Ok(mut observers) = frame_observers().lock() {
710 observers.push((id, Arc::new(observer)));
711 }
712 CameraObserver {
713 id,
714 kind: ObserverKind::Frame,
715 }
716}
717
718pub fn observe_camera_state(
722 observer: impl Fn(CameraState) + Send + Sync + 'static,
723) -> CameraObserver {
724 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
725 let observer: StateObserver = Arc::new(observer);
726 if let Ok(mut observers) = state_observers().lock() {
727 observers.push((id, Arc::clone(&observer)));
728 }
729 observer(camera_state());
730 CameraObserver {
731 id,
732 kind: ObserverKind::State,
733 }
734}
735
736pub fn observe_camera_stills(
738 observer: impl Fn(Result<CameraStill, CameraError>) + Send + Sync + 'static,
739) -> CameraObserver {
740 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
741 if let Ok(mut observers) = still_observers().lock() {
742 observers.push((id, Arc::new(observer)));
743 }
744 CameraObserver {
745 id,
746 kind: ObserverKind::Still,
747 }
748}
749
750pub fn observe_camera_lenses(
754 observer: impl Fn(CameraLenses) + Send + Sync + 'static,
755) -> CameraObserver {
756 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
757 let observer: LensObserver = Arc::new(observer);
758 if let Ok(mut observers) = lens_observers().lock() {
759 observers.push((id, Arc::clone(&observer)));
760 }
761 observer(camera_lenses());
762 CameraObserver {
763 id,
764 kind: ObserverKind::Lenses,
765 }
766}
767
768#[allow(non_snake_case)]
771#[track_caller]
772pub fn rememberCameraState() -> State<CameraState> {
773 let updates = rememberEventStream((), |sender| {
774 observe_camera_state(move |state| sender.send(state))
775 });
776 cranpose_core::collectAsState(updates, (), camera_state())
777}
778
779#[allow(non_snake_case)]
786#[track_caller]
787pub fn rememberCameraFrames() -> EventStream<CameraFrame> {
788 rememberEventStream((), |sender| {
789 observe_camera_frames(move |frame| sender.send(frame))
790 })
791}
792
793#[allow(non_snake_case)]
795#[track_caller]
796pub fn rememberCameraStills() -> EventStream<Result<CameraStill, CameraError>> {
797 rememberEventStream((), |sender| {
798 observe_camera_stills(move |still| sender.send(still))
799 })
800}
801
802#[allow(non_snake_case)]
805#[track_caller]
806pub fn rememberCameraLenses() -> State<CameraLenses> {
807 let updates = rememberEventStream((), |sender| {
808 observe_camera_lenses(move |lenses| sender.send(lenses))
809 });
810 cranpose_core::collectAsState(updates, (), camera_lenses())
811}
812
813pub fn start_camera() -> Result<(), CameraError> {
816 let Some(camera) = camera() else {
817 publish_camera_state(CameraState::Failed(CameraError::Unsupported));
818 return Err(CameraError::Unsupported);
819 };
820 publish_camera_state(CameraState::Starting);
821 camera.start().inspect_err(|error| {
822 publish_camera_state(CameraState::Failed(error.clone()));
823 })
824}
825
826pub fn stop_camera() {
828 if let Some(camera) = camera() {
829 camera.stop();
830 }
831 publish_camera_state(CameraState::Stopped);
832}
833
834pub fn request_camera_still() -> Result<(), CameraError> {
837 let Some(camera) = camera() else {
838 return Err(CameraError::Unsupported);
839 };
840 if !camera_state().is_running() {
841 return Err(CameraError::NotRunning);
842 }
843 camera.request_still()
844}
845
846pub async fn capture_camera_still() -> Result<CameraStill, CameraError> {
856 let signal = crate::async_io::Signal::new();
857 let deliver = signal.clone();
858 let observer = observe_camera_stills(move |result| deliver.set(result));
859 request_camera_still()?;
860 let arrived = signal.wait().await;
861 drop(observer);
862 arrived.unwrap_or(Err(CameraError::NotRunning))
863}
864
865#[cfg(test)]
866mod tests {
867 use super::*;
868
869 struct FakeCamera {
870 started: AtomicU64,
871 stopped: AtomicU64,
872 stills: AtomicU64,
873 fails: bool,
874 }
875
876 impl FakeCamera {
877 fn new() -> Arc<Self> {
878 Arc::new(Self {
879 started: AtomicU64::new(0),
880 stopped: AtomicU64::new(0),
881 stills: AtomicU64::new(0),
882 fails: false,
883 })
884 }
885
886 fn failing() -> Arc<Self> {
887 Arc::new(Self {
888 started: AtomicU64::new(0),
889 stopped: AtomicU64::new(0),
890 stills: AtomicU64::new(0),
891 fails: true,
892 })
893 }
894 }
895
896 impl Camera for FakeCamera {
897 fn start(&self) -> Result<(), CameraError> {
898 self.started.fetch_add(1, Ordering::Relaxed);
899 if self.fails {
900 return Err(CameraError::PermissionDenied);
901 }
902 publish_camera_state(CameraState::Running {
903 device: "fake".to_string(),
904 });
905 Ok(())
906 }
907
908 fn stop(&self) {
909 self.stopped.fetch_add(1, Ordering::Relaxed);
910 }
911
912 fn request_still(&self) -> Result<(), CameraError> {
913 self.stills.fetch_add(1, Ordering::Relaxed);
914 publish_camera_still(Ok(CameraStill {
915 jpeg: vec![0xff, 0xd8],
916 }));
917 Ok(())
918 }
919 }
920
921 fn rgba_frame(sequence: u64) -> CameraFrame {
922 CameraFrame::new(2, 2, FrameFormat::Rgba8, 90, sequence, vec![7; 16])
923 .expect("a well-formed frame")
924 }
925
926 #[test]
927 fn a_frame_size_is_the_one_its_format_implies() {
928 assert_eq!(FrameFormat::Rgba8.byte_len(4, 2), Some(32));
929 assert_eq!(FrameFormat::Nv12.byte_len(4, 2), Some(12));
930 assert_eq!(
931 FrameFormat::Nv12.byte_len(3, 2),
932 None,
933 "NV12 has no half column for an odd width"
934 );
935 assert_eq!(FrameFormat::Nv12.byte_len(4, 3), None);
936 }
937
938 #[test]
939 fn a_frame_that_does_not_match_its_size_is_refused() {
940 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 15]).is_none());
941 assert!(CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, vec![0; 5]).is_none());
942 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 16]).is_some());
943 }
944
945 #[test]
946 fn a_rotation_is_kept_inside_one_turn() {
947 let frame = CameraFrame::new(2, 2, FrameFormat::Rgba8, 450, 0, vec![0; 16])
948 .expect("a well-formed frame");
949 assert_eq!(frame.rotation_degrees, 90);
950 }
951
952 #[test]
953 fn an_rgba_frame_is_handed_over_unchanged() {
954 let frame = rgba_frame(0);
955 assert_eq!(frame.to_rgba8(), frame.bytes);
956 }
957
958 #[test]
959 fn nv12_black_white_and_primaries_convert_to_the_expected_colours() {
960 fn convert(luma: u8, blue: u8, red: u8) -> [u8; 4] {
961 let bytes = vec![luma, luma, luma, luma, blue, red];
962 let frame = CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, bytes)
963 .expect("a well-formed frame");
964 let rgba = frame.to_rgba8();
965 [rgba[0], rgba[1], rgba[2], rgba[3]]
966 }
967
968 assert_eq!(convert(0, 128, 128), [0, 0, 0, 255], "black");
969 assert_eq!(convert(255, 128, 128), [255, 255, 255, 255], "white");
970
971 let red = convert(76, 84, 255);
972 assert!(
973 red[0] > 240 && red[1] < 20 && red[2] < 20,
974 "red, got {red:?}"
975 );
976 let blue = convert(29, 255, 107);
977 assert!(
978 blue[2] > 240 && blue[0] < 20 && blue[1] < 20,
979 "blue, got {blue:?}"
980 );
981 assert!(
982 convert(128, 128, 128).iter().all(|value| *value > 0),
983 "a grey frame must not clamp to black"
984 );
985 }
986
987 #[test]
988 fn a_short_nv12_frame_converts_to_black_rather_than_reading_past_its_end() {
989 let rgba = nv12_to_rgba8(4, 4, &[0u8; 3]);
990 assert_eq!(rgba.len(), 4 * 4 * 4);
991 assert!(rgba.iter().all(|value| *value == 0));
992 }
993
994 #[test]
995 fn a_platform_without_a_camera_says_so_rather_than_pretending() {
996 let _guard = crate::registry::test_service_guard();
997 clear_platform_camera();
998 assert!(!camera_supported());
999 assert_eq!(start_camera(), Err(CameraError::Unsupported));
1000 assert_eq!(
1001 camera_state(),
1002 CameraState::Failed(CameraError::Unsupported)
1003 );
1004 assert_eq!(request_camera_still(), Err(CameraError::Unsupported));
1005 clear_platform_camera();
1006 }
1007
1008 #[test]
1009 fn the_session_reports_starting_before_it_reports_running() {
1010 let _guard = crate::registry::test_service_guard();
1011 clear_platform_camera();
1012 let seen = Arc::new(Mutex::new(Vec::new()));
1013 let recorder = Arc::clone(&seen);
1014 let observer = observe_camera_state(move |state| {
1015 recorder
1016 .lock()
1017 .unwrap_or_else(|error| error.into_inner())
1018 .push(state)
1019 });
1020 set_platform_camera(FakeCamera::new());
1021 start_camera().expect("the session starts");
1022
1023 assert_eq!(
1024 *seen.lock().unwrap_or_else(|error| error.into_inner()),
1025 vec![
1026 CameraState::Idle,
1027 CameraState::Starting,
1028 CameraState::Running {
1029 device: "fake".to_string()
1030 }
1031 ]
1032 );
1033 assert!(camera_state().is_running());
1034 assert!(camera_state().is_active());
1035 drop(observer);
1036 clear_platform_camera();
1037 }
1038
1039 #[test]
1040 fn a_session_that_cannot_open_reports_why() {
1041 let _guard = crate::registry::test_service_guard();
1042 clear_platform_camera();
1043 set_platform_camera(FakeCamera::failing());
1044 assert_eq!(start_camera(), Err(CameraError::PermissionDenied));
1045 assert_eq!(
1046 camera_state().failure(),
1047 Some(&CameraError::PermissionDenied)
1048 );
1049 assert!(!camera_state().is_active());
1050 clear_platform_camera();
1051 }
1052
1053 #[test]
1054 fn stopping_releases_the_device_and_says_so() {
1055 let _guard = crate::registry::test_service_guard();
1056 clear_platform_camera();
1057 let backend = FakeCamera::new();
1058 set_platform_camera(backend.clone());
1059 start_camera().expect("the session starts");
1060 stop_camera();
1061 assert_eq!(backend.stopped.load(Ordering::Relaxed), 1);
1062 assert_eq!(camera_state(), CameraState::Stopped);
1063 clear_platform_camera();
1064 }
1065
1066 #[test]
1067 fn the_stored_frame_is_always_the_newest_one() {
1068 let _guard = crate::registry::test_service_guard();
1069 clear_platform_camera();
1070 assert_eq!(latest_camera_frame(), None);
1071 publish_camera_frame(rgba_frame(1));
1072 publish_camera_frame(rgba_frame(2));
1073 publish_camera_frame(rgba_frame(3));
1074 assert_eq!(latest_camera_frame().map(|frame| frame.sequence), Some(3));
1075 clear_platform_camera();
1076 }
1077
1078 #[test]
1079 fn frame_observers_see_frames_and_stop_when_dropped() {
1080 let _guard = crate::registry::test_service_guard();
1081 clear_platform_camera();
1082 let seen = Arc::new(Mutex::new(Vec::new()));
1083 let recorder = Arc::clone(&seen);
1084 let observer = observe_camera_frames(move |frame| {
1085 recorder
1086 .lock()
1087 .unwrap_or_else(|error| error.into_inner())
1088 .push(frame.sequence)
1089 });
1090 publish_camera_frame(rgba_frame(1));
1091 publish_camera_frame(rgba_frame(2));
1092 drop(observer);
1093 publish_camera_frame(rgba_frame(3));
1094 assert_eq!(
1095 *seen.lock().unwrap_or_else(|error| error.into_inner()),
1096 vec![1, 2]
1097 );
1098 clear_platform_camera();
1099 }
1100
1101 #[test]
1102 fn frames_the_platform_could_not_deliver_are_counted_rather_than_queued() {
1103 let _guard = crate::registry::test_service_guard();
1104 clear_platform_camera();
1105 assert_eq!(dropped_camera_frames(), 0);
1106 record_dropped_camera_frame();
1107 record_dropped_camera_frame();
1108 assert_eq!(dropped_camera_frames(), 2);
1109 publish_camera_state(CameraState::Starting);
1110 assert_eq!(dropped_camera_frames(), 0);
1111 assert_eq!(latest_camera_frame(), None);
1112 clear_platform_camera();
1113 }
1114
1115 #[test]
1116 fn a_still_is_asked_for_and_arrives_separately() {
1117 let _guard = crate::registry::test_service_guard();
1118 clear_platform_camera();
1119 let backend = FakeCamera::new();
1120 set_platform_camera(backend.clone());
1121
1122 assert_eq!(
1123 request_camera_still(),
1124 Err(CameraError::NotRunning),
1125 "nothing is running, so there is nothing to photograph"
1126 );
1127
1128 let seen = Arc::new(Mutex::new(Vec::new()));
1129 let recorder = Arc::clone(&seen);
1130 let observer = observe_camera_stills(move |still| {
1131 recorder
1132 .lock()
1133 .unwrap_or_else(|error| error.into_inner())
1134 .push(still)
1135 });
1136 start_camera().expect("the session starts");
1137 request_camera_still().expect("a still is asked for");
1138 assert_eq!(backend.stills.load(Ordering::Relaxed), 1);
1139 assert_eq!(
1140 *seen.lock().unwrap_or_else(|error| error.into_inner()),
1141 vec![Ok(CameraStill {
1142 jpeg: vec![0xff, 0xd8]
1143 })]
1144 );
1145 drop(observer);
1146 clear_platform_camera();
1147 }
1148
1149 #[test]
1150 fn a_backend_that_lists_no_lens_and_no_flash_says_so() {
1151 let _guard = crate::registry::test_service_guard();
1152 clear_platform_camera();
1153 set_platform_camera(FakeCamera::new());
1154 let backend = camera().expect("registered");
1155 assert!(backend.lenses().is_empty());
1156 assert_eq!(backend.lens(), None);
1157 assert!(!backend.use_lens("0"));
1158 assert!(!backend.has_flash());
1159 assert!(!backend.set_flash(FlashMode::On));
1160 assert!(!backend.set_torch(true));
1161 clear_platform_camera();
1162 }
1163
1164 #[test]
1165 fn a_backend_that_lists_two_lenses_hands_them_over_in_order() {
1166 let _guard = crate::registry::test_service_guard();
1167 clear_platform_camera();
1168 struct TwoLenses;
1169 impl Camera for TwoLenses {
1170 fn start(&self) -> Result<(), CameraError> {
1171 Ok(())
1172 }
1173 fn stop(&self) {}
1174 fn lenses(&self) -> Vec<CameraLens> {
1175 vec![
1176 CameraLens {
1177 id: "u".into(),
1178 name: "Ultra wide".into(),
1179 facing: LensFacing::Back,
1180 },
1181 CameraLens {
1182 id: "w".into(),
1183 name: "Wide".into(),
1184 facing: LensFacing::Back,
1185 },
1186 ]
1187 }
1188 fn lens(&self) -> Option<String> {
1189 Some("w".into())
1190 }
1191 fn use_lens(&self, id: &str) -> bool {
1192 id == "u" || id == "w"
1193 }
1194 }
1195 set_platform_camera(Arc::new(TwoLenses));
1196 let backend = camera().expect("registered");
1197 let lenses = backend.lenses();
1198 assert_eq!(lenses.len(), 2);
1199 assert_eq!(lenses[0].name, "Ultra wide");
1200 assert_eq!(backend.lens().as_deref(), Some("w"));
1201 assert!(backend.use_lens("u"));
1202 assert!(!backend.use_lens("tele"));
1203 clear_platform_camera();
1204 }
1205
1206 fn two_pixel_frame(rotation: u16) -> CameraFrame {
1207 let mut bytes = vec![10u8, 10, 10, 255];
1208 bytes.extend_from_slice(&[20, 20, 20, 255]);
1209 CameraFrame::new(2, 1, FrameFormat::Rgba8, rotation, 0, bytes).expect("a well-formed frame")
1210 }
1211
1212 fn pixel_values(image: &UprightRgba) -> Vec<u8> {
1213 image.rgba.iter().step_by(4).copied().collect()
1214 }
1215
1216 #[test]
1217 fn a_frame_turns_upright_by_its_rotation() {
1218 let unturned = two_pixel_frame(0).upright_rgba8();
1219 assert_eq!((unturned.width, unturned.height), (2, 1));
1220 assert_eq!(pixel_values(&unturned), vec![10, 20]);
1221
1222 let quarter = two_pixel_frame(90).upright_rgba8();
1223 assert_eq!((quarter.width, quarter.height), (1, 2));
1224 assert_eq!(pixel_values(&quarter), vec![10, 20]);
1225
1226 let half = two_pixel_frame(180).upright_rgba8();
1227 assert_eq!((half.width, half.height), (2, 1));
1228 assert_eq!(pixel_values(&half), vec![20, 10]);
1229
1230 let three_quarters = two_pixel_frame(270).upright_rgba8();
1231 assert_eq!((three_quarters.width, three_quarters.height), (1, 2));
1232 assert_eq!(pixel_values(&three_quarters), vec![20, 10]);
1233 }
1234
1235 #[test]
1236 fn an_nv12_frame_turns_and_converts_in_one_pass() {
1237 let bytes = vec![0, 255, 0, 0, 128, 128];
1238 let frame =
1239 CameraFrame::new(2, 2, FrameFormat::Nv12, 90, 0, bytes).expect("a well-formed frame");
1240 let upright = frame.upright_rgba8();
1241 assert_eq!((upright.width, upright.height), (2, 2));
1242 let values = pixel_values(&upright);
1243 assert_eq!(values[0], 0, "top-left stays dark");
1244 assert_eq!(
1245 values[3], 255,
1246 "the bright top-right pixel lands bottom-right"
1247 );
1248 }
1249
1250 #[test]
1251 fn an_rgb8_frame_turns_upright_with_a_full_alpha() {
1252 let frame = CameraFrame::new(
1253 2,
1254 1,
1255 FrameFormat::Rgb8,
1256 180,
1257 0,
1258 vec![10, 10, 10, 20, 20, 20],
1259 )
1260 .expect("a well-formed frame");
1261 let upright = frame.upright_rgba8();
1262 assert_eq!(pixel_values(&upright), vec![20, 10]);
1263 assert!(upright.rgba.iter().skip(3).step_by(4).all(|a| *a == 255));
1264 }
1265
1266 #[test]
1267 fn a_turn_that_is_not_a_quarter_is_left_alone() {
1268 let frame = CameraFrame::new(2, 1, FrameFormat::Rgba8, 45, 0, vec![7; 8])
1269 .expect("a well-formed frame");
1270 let upright = frame.upright_rgba8();
1271 assert_eq!((upright.width, upright.height), (2, 1));
1272 assert_eq!(upright.rgba, frame.to_rgba8());
1273 }
1274
1275 #[test]
1276 fn the_lens_list_is_published_and_observed() {
1277 let _guard = crate::registry::test_service_guard();
1278 clear_platform_camera();
1279 assert_eq!(camera_lenses(), CameraLenses::default());
1280
1281 let seen = Arc::new(Mutex::new(Vec::new()));
1282 let recorder = Arc::clone(&seen);
1283 let observer = observe_camera_lenses(move |lenses| {
1284 recorder
1285 .lock()
1286 .unwrap_or_else(|error| error.into_inner())
1287 .push(lenses)
1288 });
1289
1290 let published = CameraLenses {
1291 lenses: vec![CameraLens {
1292 id: "0".into(),
1293 name: "Back".into(),
1294 facing: LensFacing::Back,
1295 }],
1296 active: Some("0".into()),
1297 };
1298 publish_camera_lenses(published.clone());
1299 publish_camera_lenses(published.clone());
1300 assert_eq!(camera_lenses(), published);
1301 assert_eq!(
1302 *seen.lock().unwrap_or_else(|error| error.into_inner()),
1303 vec![CameraLenses::default(), published.clone()],
1304 "the current list arrives at once, and a repeat is not re-delivered"
1305 );
1306
1307 drop(observer);
1308 publish_camera_lenses(CameraLenses::default());
1309 assert_eq!(
1310 seen.lock().unwrap_or_else(|error| error.into_inner()).len(),
1311 2,
1312 "a dropped observer hears nothing more"
1313 );
1314 clear_platform_camera();
1315 assert_eq!(camera_lenses(), CameraLenses::default());
1316 }
1317}