1use std::sync::{
25 atomic::{AtomicU64, Ordering},
26 Arc, Mutex, OnceLock,
27};
28
29use cranpose_core::{rememberEventStream, EventStream, State};
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 => {
65 if !width.is_multiple_of(2) || !height.is_multiple_of(2) {
66 return None;
67 }
68 pixels.checked_add(pixels / 2)
69 }
70 }
71 }
72}
73
74#[derive(Clone, Debug, PartialEq, Eq)]
76pub struct CameraFrame {
77 pub width: u32,
78 pub height: u32,
79 pub format: FrameFormat,
80 pub rotation_degrees: u16,
83 pub sequence: u64,
86 pub bytes: Vec<u8>,
88}
89
90impl CameraFrame {
91 pub fn new(
95 width: u32,
96 height: u32,
97 format: FrameFormat,
98 rotation_degrees: u16,
99 sequence: u64,
100 bytes: Vec<u8>,
101 ) -> Option<Self> {
102 if format.byte_len(width, height)? != bytes.len() {
103 return None;
104 }
105 Some(Self {
106 width,
107 height,
108 format,
109 rotation_degrees: rotation_degrees % 360,
110 sequence,
111 bytes,
112 })
113 }
114
115 pub fn to_rgba8(&self) -> Vec<u8> {
121 match self.format {
122 FrameFormat::Rgba8 => self.bytes.clone(),
123 FrameFormat::Rgb8 => rgb8_to_rgba8(&self.bytes),
124 FrameFormat::Nv12 => nv12_to_rgba8(self.width, self.height, &self.bytes),
125 }
126 }
127}
128
129fn rgb8_to_rgba8(bytes: &[u8]) -> Vec<u8> {
131 let mut rgba = Vec::with_capacity(bytes.len() / 3 * 4);
132 for [red, green, blue] in bytes.as_chunks::<3>().0 {
135 rgba.extend_from_slice(&[*red, *green, *blue, 255]);
136 }
137 rgba
138}
139
140fn nv12_to_rgba8(width: u32, height: u32, bytes: &[u8]) -> Vec<u8> {
147 let (width, height) = (width as usize, height as usize);
148 let pixels = width * height;
149 let mut rgba = vec![0u8; pixels * 4];
150 if bytes.len() < pixels + pixels / 2 || width == 0 || height == 0 {
151 return rgba;
152 }
153 let (luma, chroma) = bytes.split_at(pixels);
154
155 for y in 0..height {
156 let luma_row = &luma[y * width..(y + 1) * width];
157 let chroma_row = &chroma[(y / 2) * width..(y / 2 + 1) * width];
158 let out_row = &mut rgba[y * width * 4..(y + 1) * width * 4];
159 for x in 0..width {
160 let luminance = luma_row[x] as i32;
161 let blue_difference = chroma_row[x & !1] as i32 - 128;
162 let red_difference = chroma_row[(x & !1) + 1] as i32 - 128;
163 let out = &mut out_row[x * 4..x * 4 + 4];
164 out[0] = clamp_byte(luminance + ((91881 * red_difference) >> 16));
165 out[1] =
166 clamp_byte(luminance - ((22554 * blue_difference + 46802 * red_difference) >> 16));
167 out[2] = clamp_byte(luminance + ((116130 * blue_difference) >> 16));
168 out[3] = 255;
169 }
170 }
171 rgba
172}
173
174fn clamp_byte(value: i32) -> u8 {
175 value.clamp(0, 255) as u8
176}
177
178#[derive(Clone, Debug, PartialEq, Eq)]
185pub struct CameraStill {
186 pub jpeg: Vec<u8>,
187}
188
189#[derive(Clone, Debug, PartialEq, Eq)]
196pub struct CameraLens {
197 pub id: String,
198 pub name: String,
199}
200
201#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
207pub enum FlashMode {
208 #[default]
209 Off,
210 Auto,
211 On,
212}
213
214#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
215pub enum CameraError {
216 #[error("live camera capture is not supported here")]
218 Unsupported,
219 #[error("camera permission denied")]
221 PermissionDenied,
222 #[error("the camera is not running")]
224 NotRunning,
225 #[error("{0}")]
227 Failed(String),
228}
229
230#[derive(Clone, Debug, Default, PartialEq, Eq)]
232pub enum CameraState {
233 #[default]
235 Idle,
236 Starting,
239 Running {
241 device: String,
243 },
244 Stopped,
246 Failed(CameraError),
248}
249
250impl CameraState {
251 pub fn is_running(&self) -> bool {
253 matches!(self, CameraState::Running { .. })
254 }
255
256 pub fn is_active(&self) -> bool {
259 matches!(self, CameraState::Starting | CameraState::Running { .. })
260 }
261
262 pub fn failure(&self) -> Option<&CameraError> {
264 match self {
265 CameraState::Failed(error) => Some(error),
266 _ => None,
267 }
268 }
269}
270
271pub trait Camera: Send + Sync {
277 fn start(&self) -> Result<(), CameraError>;
283
284 fn stop(&self);
286
287 fn request_still(&self) -> Result<(), CameraError> {
293 Err(CameraError::Unsupported)
294 }
295
296 fn set_torch(&self, _on: bool) -> bool {
302 false
303 }
304
305 fn lenses(&self) -> Vec<CameraLens> {
311 Vec::new()
312 }
313
314 fn lens(&self) -> Option<String> {
317 None
318 }
319
320 fn use_lens(&self, _id: &str) -> bool {
323 false
324 }
325
326 fn has_flash(&self) -> bool {
328 false
329 }
330
331 fn set_flash(&self, _mode: FlashMode) -> bool {
334 false
335 }
336}
337
338pub type CameraRef = Arc<dyn Camera>;
340
341static PLATFORM_CAMERA: ServiceRegistry<dyn Camera> = ServiceRegistry::new();
342
343pub fn set_platform_camera(camera: CameraRef) {
345 PLATFORM_CAMERA.set(camera);
346}
347
348pub fn clear_platform_camera() {
351 PLATFORM_CAMERA.clear();
352 if let Ok(mut observers) = frame_observers().lock() {
353 observers.clear();
354 }
355 if let Ok(mut observers) = state_observers().lock() {
356 observers.clear();
357 }
358 if let Ok(mut observers) = still_observers().lock() {
359 observers.clear();
360 }
361 if let Ok(mut latest) = latest_frame_slot().lock() {
362 *latest = None;
363 }
364 if let Ok(mut state) = state_slot().lock() {
365 *state = CameraState::Idle;
366 }
367 DROPPED_FRAMES.store(0, Ordering::Release);
368}
369
370pub fn camera() -> Option<CameraRef> {
373 PLATFORM_CAMERA.get()
374}
375
376pub fn camera_supported() -> bool {
378 PLATFORM_CAMERA.get().is_some()
379}
380
381fn state_slot() -> &'static Mutex<CameraState> {
382 static SLOT: OnceLock<Mutex<CameraState>> = OnceLock::new();
383 SLOT.get_or_init(|| Mutex::new(CameraState::Idle))
384}
385
386fn latest_frame_slot() -> &'static Mutex<Option<CameraFrame>> {
387 static SLOT: OnceLock<Mutex<Option<CameraFrame>>> = OnceLock::new();
388 SLOT.get_or_init(|| Mutex::new(None))
389}
390
391static DROPPED_FRAMES: AtomicU64 = AtomicU64::new(0);
397
398type FrameObserver = Arc<dyn Fn(CameraFrame) + Send + Sync>;
399type StateObserver = Arc<dyn Fn(CameraState) + Send + Sync>;
400type StillObserver = Arc<dyn Fn(Result<CameraStill, CameraError>) + Send + Sync>;
401
402fn frame_observers() -> &'static Mutex<Vec<(u64, FrameObserver)>> {
403 static SLOT: OnceLock<Mutex<Vec<(u64, FrameObserver)>>> = OnceLock::new();
404 SLOT.get_or_init(|| Mutex::new(Vec::new()))
405}
406
407fn state_observers() -> &'static Mutex<Vec<(u64, StateObserver)>> {
408 static SLOT: OnceLock<Mutex<Vec<(u64, StateObserver)>>> = OnceLock::new();
409 SLOT.get_or_init(|| Mutex::new(Vec::new()))
410}
411
412fn still_observers() -> &'static Mutex<Vec<(u64, StillObserver)>> {
413 static SLOT: OnceLock<Mutex<Vec<(u64, StillObserver)>>> = OnceLock::new();
414 SLOT.get_or_init(|| Mutex::new(Vec::new()))
415}
416
417static NEXT_OBSERVER: AtomicU64 = AtomicU64::new(1);
418
419pub struct CameraObserver {
421 id: u64,
422 kind: ObserverKind,
423}
424
425#[derive(Clone, Copy)]
426enum ObserverKind {
427 Frame,
428 State,
429 Still,
430}
431
432impl Drop for CameraObserver {
433 fn drop(&mut self) {
434 match self.kind {
435 ObserverKind::Frame => retain_without(frame_observers(), self.id),
436 ObserverKind::State => retain_without(state_observers(), self.id),
437 ObserverKind::Still => retain_without(still_observers(), self.id),
438 }
439 }
440}
441
442fn retain_without<T>(slot: &'static Mutex<Vec<(u64, T)>>, id: u64) {
443 if let Ok(mut observers) = slot.lock() {
444 observers.retain(|(observer, _)| *observer != id);
445 }
446}
447
448fn snapshot<T: Clone>(slot: &'static Mutex<Vec<(u64, T)>>) -> Vec<T> {
449 slot.lock()
450 .map(|observers| {
451 observers
452 .iter()
453 .map(|(_, observer)| observer.clone())
454 .collect()
455 })
456 .unwrap_or_default()
457}
458
459pub fn latest_camera_frame() -> Option<CameraFrame> {
464 latest_frame_slot()
465 .lock()
466 .map(|frame| frame.clone())
467 .unwrap_or(None)
468}
469
470pub fn camera_state() -> CameraState {
472 state_slot()
473 .lock()
474 .map(|state| state.clone())
475 .unwrap_or_default()
476}
477
478pub fn dropped_camera_frames() -> u64 {
480 DROPPED_FRAMES.load(Ordering::Acquire)
481}
482
483pub fn publish_camera_frame(frame: CameraFrame) {
490 if let Ok(mut latest) = latest_frame_slot().lock() {
491 *latest = Some(frame.clone());
492 }
493 let observers = snapshot(frame_observers());
494 if observers.is_empty() {
495 return;
496 }
497 for observer in observers {
498 observer(frame.clone());
499 }
500}
501
502pub fn record_dropped_camera_frame() {
507 DROPPED_FRAMES.fetch_add(1, Ordering::AcqRel);
508}
509
510pub fn publish_camera_state(state: CameraState) {
512 {
513 let Ok(mut current) = state_slot().lock() else {
514 return;
515 };
516 if *current == state {
517 return;
518 }
519 *current = state.clone();
520 }
521 if matches!(state, CameraState::Idle | CameraState::Starting) {
522 DROPPED_FRAMES.store(0, Ordering::Release);
523 if let Ok(mut latest) = latest_frame_slot().lock() {
524 *latest = None;
525 }
526 }
527 for observer in snapshot(state_observers()) {
528 observer(state.clone());
529 }
530}
531
532pub fn publish_camera_still(still: Result<CameraStill, CameraError>) {
534 for observer in snapshot(still_observers()) {
535 observer(still.clone());
536 }
537}
538
539pub fn observe_camera_frames(
542 observer: impl Fn(CameraFrame) + Send + Sync + 'static,
543) -> CameraObserver {
544 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
545 if let Ok(mut observers) = frame_observers().lock() {
546 observers.push((id, Arc::new(observer)));
547 }
548 CameraObserver {
549 id,
550 kind: ObserverKind::Frame,
551 }
552}
553
554pub fn observe_camera_state(
558 observer: impl Fn(CameraState) + Send + Sync + 'static,
559) -> CameraObserver {
560 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
561 let observer: StateObserver = Arc::new(observer);
562 if let Ok(mut observers) = state_observers().lock() {
563 observers.push((id, Arc::clone(&observer)));
564 }
565 observer(camera_state());
566 CameraObserver {
567 id,
568 kind: ObserverKind::State,
569 }
570}
571
572pub fn observe_camera_stills(
574 observer: impl Fn(Result<CameraStill, CameraError>) + Send + Sync + 'static,
575) -> CameraObserver {
576 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
577 if let Ok(mut observers) = still_observers().lock() {
578 observers.push((id, Arc::new(observer)));
579 }
580 CameraObserver {
581 id,
582 kind: ObserverKind::Still,
583 }
584}
585
586#[allow(non_snake_case)]
589pub fn rememberCameraState() -> State<CameraState> {
590 let updates = rememberEventStream((), |sender| {
591 observe_camera_state(move |state| sender.send(state))
592 });
593 cranpose_core::collectAsState(updates, (), camera_state())
594}
595
596#[allow(non_snake_case)]
603pub fn rememberCameraFrames() -> EventStream<CameraFrame> {
604 rememberEventStream((), |sender| {
605 observe_camera_frames(move |frame| sender.send(frame))
606 })
607}
608
609#[allow(non_snake_case)]
611pub fn rememberCameraStills() -> EventStream<Result<CameraStill, CameraError>> {
612 rememberEventStream((), |sender| {
613 observe_camera_stills(move |still| sender.send(still))
614 })
615}
616
617pub fn start_camera() -> Result<(), CameraError> {
620 let Some(camera) = camera() else {
621 publish_camera_state(CameraState::Failed(CameraError::Unsupported));
622 return Err(CameraError::Unsupported);
623 };
624 publish_camera_state(CameraState::Starting);
625 camera.start().inspect_err(|error| {
626 publish_camera_state(CameraState::Failed(error.clone()));
627 })
628}
629
630pub fn stop_camera() {
632 if let Some(camera) = camera() {
633 camera.stop();
634 }
635 publish_camera_state(CameraState::Stopped);
636}
637
638pub fn request_camera_still() -> Result<(), CameraError> {
641 let Some(camera) = camera() else {
642 return Err(CameraError::Unsupported);
643 };
644 if !camera_state().is_running() {
645 return Err(CameraError::NotRunning);
646 }
647 camera.request_still()
648}
649
650pub async fn capture_camera_still() -> Result<CameraStill, CameraError> {
660 let signal = crate::async_io::Signal::new();
661 let deliver = signal.clone();
662 let observer = observe_camera_stills(move |result| deliver.set(result));
663 request_camera_still()?;
664 let arrived = signal.wait().await;
665 drop(observer);
666 arrived.unwrap_or(Err(CameraError::NotRunning))
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674
675 struct FakeCamera {
678 started: AtomicU64,
679 stopped: AtomicU64,
680 stills: AtomicU64,
681 fails: bool,
682 }
683
684 impl FakeCamera {
685 fn new() -> Arc<Self> {
686 Arc::new(Self {
687 started: AtomicU64::new(0),
688 stopped: AtomicU64::new(0),
689 stills: AtomicU64::new(0),
690 fails: false,
691 })
692 }
693
694 fn failing() -> Arc<Self> {
695 Arc::new(Self {
696 started: AtomicU64::new(0),
697 stopped: AtomicU64::new(0),
698 stills: AtomicU64::new(0),
699 fails: true,
700 })
701 }
702 }
703
704 impl Camera for FakeCamera {
705 fn start(&self) -> Result<(), CameraError> {
706 self.started.fetch_add(1, Ordering::Relaxed);
707 if self.fails {
708 return Err(CameraError::PermissionDenied);
709 }
710 publish_camera_state(CameraState::Running {
711 device: "fake".to_string(),
712 });
713 Ok(())
714 }
715
716 fn stop(&self) {
717 self.stopped.fetch_add(1, Ordering::Relaxed);
718 }
719
720 fn request_still(&self) -> Result<(), CameraError> {
721 self.stills.fetch_add(1, Ordering::Relaxed);
722 publish_camera_still(Ok(CameraStill {
723 jpeg: vec![0xff, 0xd8],
724 }));
725 Ok(())
726 }
727 }
728
729 fn rgba_frame(sequence: u64) -> CameraFrame {
730 CameraFrame::new(2, 2, FrameFormat::Rgba8, 90, sequence, vec![7; 16])
731 .expect("a well-formed frame")
732 }
733
734 #[test]
735 fn a_frame_size_is_the_one_its_format_implies() {
736 assert_eq!(FrameFormat::Rgba8.byte_len(4, 2), Some(32));
737 assert_eq!(FrameFormat::Nv12.byte_len(4, 2), Some(12));
738 assert_eq!(
739 FrameFormat::Nv12.byte_len(3, 2),
740 None,
741 "NV12 has no half column for an odd width"
742 );
743 assert_eq!(FrameFormat::Nv12.byte_len(4, 3), None);
744 }
745
746 #[test]
749 fn a_frame_that_does_not_match_its_size_is_refused() {
750 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 15]).is_none());
751 assert!(CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, vec![0; 5]).is_none());
752 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 16]).is_some());
753 }
754
755 #[test]
756 fn a_rotation_is_kept_inside_one_turn() {
757 let frame = CameraFrame::new(2, 2, FrameFormat::Rgba8, 450, 0, vec![0; 16])
758 .expect("a well-formed frame");
759 assert_eq!(frame.rotation_degrees, 90);
760 }
761
762 #[test]
763 fn an_rgba_frame_is_handed_over_unchanged() {
764 let frame = rgba_frame(0);
765 assert_eq!(frame.to_rgba8(), frame.bytes);
766 }
767
768 #[test]
772 fn nv12_black_white_and_primaries_convert_to_the_expected_colours() {
773 fn convert(luma: u8, blue: u8, red: u8) -> [u8; 4] {
774 let bytes = vec![luma, luma, luma, luma, blue, red];
775 let frame = CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, bytes)
776 .expect("a well-formed frame");
777 let rgba = frame.to_rgba8();
778 [rgba[0], rgba[1], rgba[2], rgba[3]]
779 }
780
781 assert_eq!(convert(0, 128, 128), [0, 0, 0, 255], "black");
782 assert_eq!(convert(255, 128, 128), [255, 255, 255, 255], "white");
783
784 let red = convert(76, 84, 255);
785 assert!(
786 red[0] > 240 && red[1] < 20 && red[2] < 20,
787 "red, got {red:?}"
788 );
789 let blue = convert(29, 255, 107);
790 assert!(
791 blue[2] > 240 && blue[0] < 20 && blue[1] < 20,
792 "blue, got {blue:?}"
793 );
794 assert!(
795 convert(128, 128, 128).iter().all(|value| *value > 0),
796 "a grey frame must not clamp to black"
797 );
798 }
799
800 #[test]
801 fn a_short_nv12_frame_converts_to_black_rather_than_reading_past_its_end() {
802 let rgba = nv12_to_rgba8(4, 4, &[0u8; 3]);
803 assert_eq!(rgba.len(), 4 * 4 * 4);
804 assert!(rgba.iter().all(|value| *value == 0));
805 }
806
807 #[test]
808 fn a_platform_without_a_camera_says_so_rather_than_pretending() {
809 let _guard = crate::registry::test_service_guard();
810 clear_platform_camera();
811 assert!(!camera_supported());
812 assert_eq!(start_camera(), Err(CameraError::Unsupported));
813 assert_eq!(
814 camera_state(),
815 CameraState::Failed(CameraError::Unsupported)
816 );
817 assert_eq!(request_camera_still(), Err(CameraError::Unsupported));
818 clear_platform_camera();
819 }
820
821 #[test]
824 fn the_session_reports_starting_before_it_reports_running() {
825 let _guard = crate::registry::test_service_guard();
826 clear_platform_camera();
827 let seen = Arc::new(Mutex::new(Vec::new()));
828 let recorder = Arc::clone(&seen);
829 let observer = observe_camera_state(move |state| {
830 recorder
831 .lock()
832 .unwrap_or_else(|error| error.into_inner())
833 .push(state)
834 });
835 set_platform_camera(FakeCamera::new());
836 start_camera().expect("the session starts");
837
838 assert_eq!(
839 *seen.lock().unwrap_or_else(|error| error.into_inner()),
840 vec![
841 CameraState::Idle,
842 CameraState::Starting,
843 CameraState::Running {
844 device: "fake".to_string()
845 }
846 ]
847 );
848 assert!(camera_state().is_running());
849 assert!(camera_state().is_active());
850 drop(observer);
851 clear_platform_camera();
852 }
853
854 #[test]
855 fn a_session_that_cannot_open_reports_why() {
856 let _guard = crate::registry::test_service_guard();
857 clear_platform_camera();
858 set_platform_camera(FakeCamera::failing());
859 assert_eq!(start_camera(), Err(CameraError::PermissionDenied));
860 assert_eq!(
861 camera_state().failure(),
862 Some(&CameraError::PermissionDenied)
863 );
864 assert!(!camera_state().is_active());
865 clear_platform_camera();
866 }
867
868 #[test]
869 fn stopping_releases_the_device_and_says_so() {
870 let _guard = crate::registry::test_service_guard();
871 clear_platform_camera();
872 let backend = FakeCamera::new();
873 set_platform_camera(backend.clone());
874 start_camera().expect("the session starts");
875 stop_camera();
876 assert_eq!(backend.stopped.load(Ordering::Relaxed), 1);
877 assert_eq!(camera_state(), CameraState::Stopped);
878 clear_platform_camera();
879 }
880
881 #[test]
884 fn the_stored_frame_is_always_the_newest_one() {
885 let _guard = crate::registry::test_service_guard();
886 clear_platform_camera();
887 assert_eq!(latest_camera_frame(), None);
888 publish_camera_frame(rgba_frame(1));
889 publish_camera_frame(rgba_frame(2));
890 publish_camera_frame(rgba_frame(3));
891 assert_eq!(latest_camera_frame().map(|frame| frame.sequence), Some(3));
892 clear_platform_camera();
893 }
894
895 #[test]
896 fn frame_observers_see_frames_and_stop_when_dropped() {
897 let _guard = crate::registry::test_service_guard();
898 clear_platform_camera();
899 let seen = Arc::new(Mutex::new(Vec::new()));
900 let recorder = Arc::clone(&seen);
901 let observer = observe_camera_frames(move |frame| {
902 recorder
903 .lock()
904 .unwrap_or_else(|error| error.into_inner())
905 .push(frame.sequence)
906 });
907 publish_camera_frame(rgba_frame(1));
908 publish_camera_frame(rgba_frame(2));
909 drop(observer);
910 publish_camera_frame(rgba_frame(3));
911 assert_eq!(
912 *seen.lock().unwrap_or_else(|error| error.into_inner()),
913 vec![1, 2]
914 );
915 clear_platform_camera();
916 }
917
918 #[test]
921 fn frames_the_platform_could_not_deliver_are_counted_rather_than_queued() {
922 let _guard = crate::registry::test_service_guard();
923 clear_platform_camera();
924 assert_eq!(dropped_camera_frames(), 0);
925 record_dropped_camera_frame();
926 record_dropped_camera_frame();
927 assert_eq!(dropped_camera_frames(), 2);
928 publish_camera_state(CameraState::Starting);
931 assert_eq!(dropped_camera_frames(), 0);
932 assert_eq!(latest_camera_frame(), None);
933 clear_platform_camera();
934 }
935
936 #[test]
939 fn a_still_is_asked_for_and_arrives_separately() {
940 let _guard = crate::registry::test_service_guard();
941 clear_platform_camera();
942 let backend = FakeCamera::new();
943 set_platform_camera(backend.clone());
944
945 assert_eq!(
946 request_camera_still(),
947 Err(CameraError::NotRunning),
948 "nothing is running, so there is nothing to photograph"
949 );
950
951 let seen = Arc::new(Mutex::new(Vec::new()));
952 let recorder = Arc::clone(&seen);
953 let observer = observe_camera_stills(move |still| {
954 recorder
955 .lock()
956 .unwrap_or_else(|error| error.into_inner())
957 .push(still)
958 });
959 start_camera().expect("the session starts");
960 request_camera_still().expect("a still is asked for");
961 assert_eq!(backend.stills.load(Ordering::Relaxed), 1);
962 assert_eq!(
963 *seen.lock().unwrap_or_else(|error| error.into_inner()),
964 vec![Ok(CameraStill {
965 jpeg: vec![0xff, 0xd8]
966 })]
967 );
968 drop(observer);
969 clear_platform_camera();
970 }
971
972 #[test]
973 fn a_backend_that_lists_no_lens_and_no_flash_says_so() {
974 let _guard = crate::registry::test_service_guard();
975 clear_platform_camera();
976 set_platform_camera(FakeCamera::new());
977 let backend = camera().expect("registered");
978 assert!(backend.lenses().is_empty());
979 assert_eq!(backend.lens(), None);
980 assert!(!backend.use_lens("0"));
981 assert!(!backend.has_flash());
982 assert!(!backend.set_flash(FlashMode::On));
983 assert!(!backend.set_torch(true));
984 clear_platform_camera();
985 }
986
987 #[test]
988 fn a_backend_that_lists_two_lenses_hands_them_over_in_order() {
989 let _guard = crate::registry::test_service_guard();
990 clear_platform_camera();
991 struct TwoLenses;
992 impl Camera for TwoLenses {
993 fn start(&self) -> Result<(), CameraError> {
994 Ok(())
995 }
996 fn stop(&self) {}
997 fn lenses(&self) -> Vec<CameraLens> {
998 vec![
999 CameraLens {
1000 id: "u".into(),
1001 name: "Ultra wide".into(),
1002 },
1003 CameraLens {
1004 id: "w".into(),
1005 name: "Wide".into(),
1006 },
1007 ]
1008 }
1009 fn lens(&self) -> Option<String> {
1010 Some("w".into())
1011 }
1012 fn use_lens(&self, id: &str) -> bool {
1013 id == "u" || id == "w"
1014 }
1015 }
1016 set_platform_camera(Arc::new(TwoLenses));
1017 let backend = camera().expect("registered");
1018 let lenses = backend.lenses();
1019 assert_eq!(lenses.len(), 2);
1020 assert_eq!(lenses[0].name, "Ultra wide");
1021 assert_eq!(backend.lens().as_deref(), Some("w"));
1022 assert!(backend.use_lens("u"));
1023 assert!(!backend.use_lens("tele"));
1024 clear_platform_camera();
1025 }
1026}