1use crate::registry::ServiceRegistry;
25use cranpose_core::{rememberEventStream, EventStream, State};
26use std::sync::atomic::{AtomicU64, Ordering};
27use std::sync::{Arc, Mutex, OnceLock};
28
29#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
31pub enum FrameFormat {
32 #[default]
34 Rgba8,
35 Rgb8,
41 Nv12,
48}
49
50impl FrameFormat {
51 pub fn byte_len(self, width: u32, height: u32) -> Option<usize> {
54 let pixels = (width as usize).checked_mul(height as usize)?;
55 match self {
56 FrameFormat::Rgba8 => pixels.checked_mul(4),
57 FrameFormat::Rgb8 => pixels.checked_mul(3),
58 FrameFormat::Nv12 => {
61 if !width.is_multiple_of(2) || !height.is_multiple_of(2) {
62 return None;
63 }
64 pixels.checked_add(pixels / 2)
65 }
66 }
67 }
68}
69
70#[derive(Clone, Debug, PartialEq, Eq)]
72pub struct CameraFrame {
73 pub width: u32,
74 pub height: u32,
75 pub format: FrameFormat,
76 pub rotation_degrees: u16,
79 pub sequence: u64,
82 pub bytes: Vec<u8>,
84}
85
86impl CameraFrame {
87 pub fn new(
91 width: u32,
92 height: u32,
93 format: FrameFormat,
94 rotation_degrees: u16,
95 sequence: u64,
96 bytes: Vec<u8>,
97 ) -> Option<Self> {
98 if format.byte_len(width, height)? != bytes.len() {
99 return None;
100 }
101 Some(Self {
102 width,
103 height,
104 format,
105 rotation_degrees: rotation_degrees % 360,
106 sequence,
107 bytes,
108 })
109 }
110
111 pub fn to_rgba8(&self) -> Vec<u8> {
117 match self.format {
118 FrameFormat::Rgba8 => self.bytes.clone(),
119 FrameFormat::Rgb8 => rgb8_to_rgba8(&self.bytes),
120 FrameFormat::Nv12 => nv12_to_rgba8(self.width, self.height, &self.bytes),
121 }
122 }
123}
124
125fn rgb8_to_rgba8(bytes: &[u8]) -> Vec<u8> {
127 let mut rgba = Vec::with_capacity(bytes.len() / 3 * 4);
128 for [red, green, blue] in bytes.as_chunks::<3>().0 {
131 rgba.extend_from_slice(&[*red, *green, *blue, 255]);
132 }
133 rgba
134}
135
136fn nv12_to_rgba8(width: u32, height: u32, bytes: &[u8]) -> Vec<u8> {
143 let (width, height) = (width as usize, height as usize);
144 let pixels = width * height;
145 let mut rgba = vec![0u8; pixels * 4];
146 if bytes.len() < pixels + pixels / 2 || width == 0 || height == 0 {
147 return rgba;
148 }
149 let (luma, chroma) = bytes.split_at(pixels);
150
151 for y in 0..height {
152 let luma_row = &luma[y * width..(y + 1) * width];
153 let chroma_row = &chroma[(y / 2) * width..(y / 2 + 1) * width];
154 let out_row = &mut rgba[y * width * 4..(y + 1) * width * 4];
155 for x in 0..width {
156 let luminance = luma_row[x] as i32;
157 let blue_difference = chroma_row[x & !1] as i32 - 128;
158 let red_difference = chroma_row[(x & !1) + 1] as i32 - 128;
159 let out = &mut out_row[x * 4..x * 4 + 4];
160 out[0] = clamp_byte(luminance + ((91881 * red_difference) >> 16));
161 out[1] =
162 clamp_byte(luminance - ((22554 * blue_difference + 46802 * red_difference) >> 16));
163 out[2] = clamp_byte(luminance + ((116130 * blue_difference) >> 16));
164 out[3] = 255;
165 }
166 }
167 rgba
168}
169
170fn clamp_byte(value: i32) -> u8 {
171 value.clamp(0, 255) as u8
172}
173
174#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct CameraStill {
182 pub jpeg: Vec<u8>,
183}
184
185#[derive(Clone, Debug, PartialEq, Eq)]
192pub struct CameraLens {
193 pub id: String,
194 pub name: String,
195}
196
197#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
203pub enum FlashMode {
204 #[default]
205 Off,
206 Auto,
207 On,
208}
209
210#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
211pub enum CameraError {
212 #[error("live camera capture is not supported here")]
214 Unsupported,
215 #[error("camera permission denied")]
217 PermissionDenied,
218 #[error("the camera is not running")]
220 NotRunning,
221 #[error("{0}")]
223 Failed(String),
224}
225
226#[derive(Clone, Debug, Default, PartialEq, Eq)]
228pub enum CameraState {
229 #[default]
231 Idle,
232 Starting,
235 Running {
237 device: String,
239 },
240 Stopped,
242 Failed(CameraError),
244}
245
246impl CameraState {
247 pub fn is_running(&self) -> bool {
249 matches!(self, CameraState::Running { .. })
250 }
251
252 pub fn is_active(&self) -> bool {
255 matches!(self, CameraState::Starting | CameraState::Running { .. })
256 }
257
258 pub fn failure(&self) -> Option<&CameraError> {
260 match self {
261 CameraState::Failed(error) => Some(error),
262 _ => None,
263 }
264 }
265}
266
267pub trait Camera: Send + Sync {
273 fn start(&self) -> Result<(), CameraError>;
279
280 fn stop(&self);
282
283 fn request_still(&self) -> Result<(), CameraError> {
289 Err(CameraError::Unsupported)
290 }
291
292 fn set_torch(&self, _on: bool) -> bool {
298 false
299 }
300
301 fn lenses(&self) -> Vec<CameraLens> {
307 Vec::new()
308 }
309
310 fn lens(&self) -> Option<String> {
313 None
314 }
315
316 fn use_lens(&self, _id: &str) -> bool {
319 false
320 }
321
322 fn has_flash(&self) -> bool {
324 false
325 }
326
327 fn set_flash(&self, _mode: FlashMode) -> bool {
330 false
331 }
332}
333
334pub type CameraRef = Arc<dyn Camera>;
336
337static PLATFORM_CAMERA: ServiceRegistry<dyn Camera> = ServiceRegistry::new();
338
339pub fn set_platform_camera(camera: CameraRef) {
341 PLATFORM_CAMERA.set(camera);
342}
343
344pub fn clear_platform_camera() {
347 PLATFORM_CAMERA.clear();
348 if let Ok(mut observers) = frame_observers().lock() {
349 observers.clear();
350 }
351 if let Ok(mut observers) = state_observers().lock() {
352 observers.clear();
353 }
354 if let Ok(mut observers) = still_observers().lock() {
355 observers.clear();
356 }
357 if let Ok(mut latest) = latest_frame_slot().lock() {
358 *latest = None;
359 }
360 if let Ok(mut state) = state_slot().lock() {
361 *state = CameraState::Idle;
362 }
363 DROPPED_FRAMES.store(0, Ordering::Release);
364}
365
366pub fn camera() -> Option<CameraRef> {
369 PLATFORM_CAMERA.get()
370}
371
372pub fn camera_supported() -> bool {
374 PLATFORM_CAMERA.get().is_some()
375}
376
377fn state_slot() -> &'static Mutex<CameraState> {
378 static SLOT: OnceLock<Mutex<CameraState>> = OnceLock::new();
379 SLOT.get_or_init(|| Mutex::new(CameraState::Idle))
380}
381
382fn latest_frame_slot() -> &'static Mutex<Option<CameraFrame>> {
383 static SLOT: OnceLock<Mutex<Option<CameraFrame>>> = OnceLock::new();
384 SLOT.get_or_init(|| Mutex::new(None))
385}
386
387static DROPPED_FRAMES: AtomicU64 = AtomicU64::new(0);
393
394type FrameObserver = Arc<dyn Fn(CameraFrame) + Send + Sync>;
395type StateObserver = Arc<dyn Fn(CameraState) + Send + Sync>;
396type StillObserver = Arc<dyn Fn(Result<CameraStill, CameraError>) + Send + Sync>;
397
398fn frame_observers() -> &'static Mutex<Vec<(u64, FrameObserver)>> {
399 static SLOT: OnceLock<Mutex<Vec<(u64, FrameObserver)>>> = OnceLock::new();
400 SLOT.get_or_init(|| Mutex::new(Vec::new()))
401}
402
403fn state_observers() -> &'static Mutex<Vec<(u64, StateObserver)>> {
404 static SLOT: OnceLock<Mutex<Vec<(u64, StateObserver)>>> = OnceLock::new();
405 SLOT.get_or_init(|| Mutex::new(Vec::new()))
406}
407
408fn still_observers() -> &'static Mutex<Vec<(u64, StillObserver)>> {
409 static SLOT: OnceLock<Mutex<Vec<(u64, StillObserver)>>> = OnceLock::new();
410 SLOT.get_or_init(|| Mutex::new(Vec::new()))
411}
412
413static NEXT_OBSERVER: AtomicU64 = AtomicU64::new(1);
414
415pub struct CameraObserver {
417 id: u64,
418 kind: ObserverKind,
419}
420
421#[derive(Clone, Copy)]
422enum ObserverKind {
423 Frame,
424 State,
425 Still,
426}
427
428impl Drop for CameraObserver {
429 fn drop(&mut self) {
430 match self.kind {
431 ObserverKind::Frame => retain_without(frame_observers(), self.id),
432 ObserverKind::State => retain_without(state_observers(), self.id),
433 ObserverKind::Still => retain_without(still_observers(), self.id),
434 }
435 }
436}
437
438fn retain_without<T>(slot: &'static Mutex<Vec<(u64, T)>>, id: u64) {
439 if let Ok(mut observers) = slot.lock() {
440 observers.retain(|(observer, _)| *observer != id);
441 }
442}
443
444fn snapshot<T: Clone>(slot: &'static Mutex<Vec<(u64, T)>>) -> Vec<T> {
445 slot.lock()
446 .map(|observers| {
447 observers
448 .iter()
449 .map(|(_, observer)| observer.clone())
450 .collect()
451 })
452 .unwrap_or_default()
453}
454
455pub fn latest_camera_frame() -> Option<CameraFrame> {
460 latest_frame_slot()
461 .lock()
462 .map(|frame| frame.clone())
463 .unwrap_or(None)
464}
465
466pub fn camera_state() -> CameraState {
468 state_slot()
469 .lock()
470 .map(|state| state.clone())
471 .unwrap_or_default()
472}
473
474pub fn dropped_camera_frames() -> u64 {
476 DROPPED_FRAMES.load(Ordering::Acquire)
477}
478
479pub fn publish_camera_frame(frame: CameraFrame) {
486 if let Ok(mut latest) = latest_frame_slot().lock() {
487 *latest = Some(frame.clone());
488 }
489 let observers = snapshot(frame_observers());
490 if observers.is_empty() {
491 return;
492 }
493 for observer in observers {
494 observer(frame.clone());
495 }
496}
497
498pub fn record_dropped_camera_frame() {
503 DROPPED_FRAMES.fetch_add(1, Ordering::AcqRel);
504}
505
506pub fn publish_camera_state(state: CameraState) {
508 {
509 let Ok(mut current) = state_slot().lock() else {
510 return;
511 };
512 if *current == state {
513 return;
514 }
515 *current = state.clone();
516 }
517 if matches!(state, CameraState::Idle | CameraState::Starting) {
518 DROPPED_FRAMES.store(0, Ordering::Release);
519 if let Ok(mut latest) = latest_frame_slot().lock() {
520 *latest = None;
521 }
522 }
523 for observer in snapshot(state_observers()) {
524 observer(state.clone());
525 }
526}
527
528pub fn publish_camera_still(still: Result<CameraStill, CameraError>) {
530 for observer in snapshot(still_observers()) {
531 observer(still.clone());
532 }
533}
534
535pub fn observe_camera_frames(
538 observer: impl Fn(CameraFrame) + Send + Sync + 'static,
539) -> CameraObserver {
540 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
541 if let Ok(mut observers) = frame_observers().lock() {
542 observers.push((id, Arc::new(observer)));
543 }
544 CameraObserver {
545 id,
546 kind: ObserverKind::Frame,
547 }
548}
549
550pub fn observe_camera_state(
554 observer: impl Fn(CameraState) + Send + Sync + 'static,
555) -> CameraObserver {
556 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
557 let observer: StateObserver = Arc::new(observer);
558 if let Ok(mut observers) = state_observers().lock() {
559 observers.push((id, Arc::clone(&observer)));
560 }
561 observer(camera_state());
562 CameraObserver {
563 id,
564 kind: ObserverKind::State,
565 }
566}
567
568pub fn observe_camera_stills(
570 observer: impl Fn(Result<CameraStill, CameraError>) + Send + Sync + 'static,
571) -> CameraObserver {
572 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
573 if let Ok(mut observers) = still_observers().lock() {
574 observers.push((id, Arc::new(observer)));
575 }
576 CameraObserver {
577 id,
578 kind: ObserverKind::Still,
579 }
580}
581
582#[allow(non_snake_case)]
585pub fn rememberCameraState() -> State<CameraState> {
586 let updates = rememberEventStream((), |sender| {
587 observe_camera_state(move |state| sender.send(state))
588 });
589 cranpose_core::collectAsState(updates, (), camera_state())
590}
591
592#[allow(non_snake_case)]
599pub fn rememberCameraFrames() -> EventStream<CameraFrame> {
600 rememberEventStream((), |sender| {
601 observe_camera_frames(move |frame| sender.send(frame))
602 })
603}
604
605#[allow(non_snake_case)]
607pub fn rememberCameraStills() -> EventStream<Result<CameraStill, CameraError>> {
608 rememberEventStream((), |sender| {
609 observe_camera_stills(move |still| sender.send(still))
610 })
611}
612
613pub fn start_camera() -> Result<(), CameraError> {
616 let Some(camera) = camera() else {
617 publish_camera_state(CameraState::Failed(CameraError::Unsupported));
618 return Err(CameraError::Unsupported);
619 };
620 publish_camera_state(CameraState::Starting);
621 camera.start().inspect_err(|error| {
622 publish_camera_state(CameraState::Failed(error.clone()));
623 })
624}
625
626pub fn stop_camera() {
628 if let Some(camera) = camera() {
629 camera.stop();
630 }
631 publish_camera_state(CameraState::Stopped);
632}
633
634pub fn request_camera_still() -> Result<(), CameraError> {
637 let Some(camera) = camera() else {
638 return Err(CameraError::Unsupported);
639 };
640 if !camera_state().is_running() {
641 return Err(CameraError::NotRunning);
642 }
643 camera.request_still()
644}
645
646pub async fn capture_camera_still() -> Result<CameraStill, CameraError> {
656 let signal = crate::async_io::Signal::new();
657 let deliver = signal.clone();
658 let observer = observe_camera_stills(move |result| deliver.set(result));
659 request_camera_still()?;
660 let arrived = signal.wait().await;
661 drop(observer);
662 arrived.unwrap_or(Err(CameraError::NotRunning))
665}
666
667#[cfg(test)]
668mod tests {
669 use super::*;
670
671 struct FakeCamera {
674 started: AtomicU64,
675 stopped: AtomicU64,
676 stills: AtomicU64,
677 fails: bool,
678 }
679
680 impl FakeCamera {
681 fn new() -> Arc<Self> {
682 Arc::new(Self {
683 started: AtomicU64::new(0),
684 stopped: AtomicU64::new(0),
685 stills: AtomicU64::new(0),
686 fails: false,
687 })
688 }
689
690 fn failing() -> Arc<Self> {
691 Arc::new(Self {
692 started: AtomicU64::new(0),
693 stopped: AtomicU64::new(0),
694 stills: AtomicU64::new(0),
695 fails: true,
696 })
697 }
698 }
699
700 impl Camera for FakeCamera {
701 fn start(&self) -> Result<(), CameraError> {
702 self.started.fetch_add(1, Ordering::Relaxed);
703 if self.fails {
704 return Err(CameraError::PermissionDenied);
705 }
706 publish_camera_state(CameraState::Running {
707 device: "fake".to_string(),
708 });
709 Ok(())
710 }
711
712 fn stop(&self) {
713 self.stopped.fetch_add(1, Ordering::Relaxed);
714 }
715
716 fn request_still(&self) -> Result<(), CameraError> {
717 self.stills.fetch_add(1, Ordering::Relaxed);
718 publish_camera_still(Ok(CameraStill {
719 jpeg: vec![0xff, 0xd8],
720 }));
721 Ok(())
722 }
723 }
724
725 fn rgba_frame(sequence: u64) -> CameraFrame {
726 CameraFrame::new(2, 2, FrameFormat::Rgba8, 90, sequence, vec![7; 16])
727 .expect("a well-formed frame")
728 }
729
730 #[test]
731 fn a_frame_size_is_the_one_its_format_implies() {
732 assert_eq!(FrameFormat::Rgba8.byte_len(4, 2), Some(32));
733 assert_eq!(FrameFormat::Nv12.byte_len(4, 2), Some(12));
734 assert_eq!(
735 FrameFormat::Nv12.byte_len(3, 2),
736 None,
737 "NV12 has no half column for an odd width"
738 );
739 assert_eq!(FrameFormat::Nv12.byte_len(4, 3), None);
740 }
741
742 #[test]
745 fn a_frame_that_does_not_match_its_size_is_refused() {
746 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 15]).is_none());
747 assert!(CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, vec![0; 5]).is_none());
748 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 16]).is_some());
749 }
750
751 #[test]
752 fn a_rotation_is_kept_inside_one_turn() {
753 let frame = CameraFrame::new(2, 2, FrameFormat::Rgba8, 450, 0, vec![0; 16])
754 .expect("a well-formed frame");
755 assert_eq!(frame.rotation_degrees, 90);
756 }
757
758 #[test]
759 fn an_rgba_frame_is_handed_over_unchanged() {
760 let frame = rgba_frame(0);
761 assert_eq!(frame.to_rgba8(), frame.bytes);
762 }
763
764 #[test]
768 fn nv12_black_white_and_primaries_convert_to_the_expected_colours() {
769 fn convert(luma: u8, blue: u8, red: u8) -> [u8; 4] {
770 let bytes = vec![luma, luma, luma, luma, blue, red];
771 let frame = CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, bytes)
772 .expect("a well-formed frame");
773 let rgba = frame.to_rgba8();
774 [rgba[0], rgba[1], rgba[2], rgba[3]]
775 }
776
777 assert_eq!(convert(0, 128, 128), [0, 0, 0, 255], "black");
778 assert_eq!(convert(255, 128, 128), [255, 255, 255, 255], "white");
779
780 let red = convert(76, 84, 255);
781 assert!(
782 red[0] > 240 && red[1] < 20 && red[2] < 20,
783 "red, got {red:?}"
784 );
785 let blue = convert(29, 255, 107);
786 assert!(
787 blue[2] > 240 && blue[0] < 20 && blue[1] < 20,
788 "blue, got {blue:?}"
789 );
790 assert!(
791 convert(128, 128, 128).iter().all(|value| *value > 0),
792 "a grey frame must not clamp to black"
793 );
794 }
795
796 #[test]
797 fn a_short_nv12_frame_converts_to_black_rather_than_reading_past_its_end() {
798 let rgba = nv12_to_rgba8(4, 4, &[0u8; 3]);
799 assert_eq!(rgba.len(), 4 * 4 * 4);
800 assert!(rgba.iter().all(|value| *value == 0));
801 }
802
803 #[test]
804 fn a_platform_without_a_camera_says_so_rather_than_pretending() {
805 let _guard = crate::registry::test_service_guard();
806 clear_platform_camera();
807 assert!(!camera_supported());
808 assert_eq!(start_camera(), Err(CameraError::Unsupported));
809 assert_eq!(
810 camera_state(),
811 CameraState::Failed(CameraError::Unsupported)
812 );
813 assert_eq!(request_camera_still(), Err(CameraError::Unsupported));
814 clear_platform_camera();
815 }
816
817 #[test]
820 fn the_session_reports_starting_before_it_reports_running() {
821 let _guard = crate::registry::test_service_guard();
822 clear_platform_camera();
823 let seen = Arc::new(Mutex::new(Vec::new()));
824 let recorder = Arc::clone(&seen);
825 let observer = observe_camera_state(move |state| {
826 recorder
827 .lock()
828 .unwrap_or_else(|error| error.into_inner())
829 .push(state)
830 });
831 set_platform_camera(FakeCamera::new());
832 start_camera().expect("the session starts");
833
834 assert_eq!(
835 *seen.lock().unwrap_or_else(|error| error.into_inner()),
836 vec![
837 CameraState::Idle,
838 CameraState::Starting,
839 CameraState::Running {
840 device: "fake".to_string()
841 }
842 ]
843 );
844 assert!(camera_state().is_running());
845 assert!(camera_state().is_active());
846 drop(observer);
847 clear_platform_camera();
848 }
849
850 #[test]
851 fn a_session_that_cannot_open_reports_why() {
852 let _guard = crate::registry::test_service_guard();
853 clear_platform_camera();
854 set_platform_camera(FakeCamera::failing());
855 assert_eq!(start_camera(), Err(CameraError::PermissionDenied));
856 assert_eq!(
857 camera_state().failure(),
858 Some(&CameraError::PermissionDenied)
859 );
860 assert!(!camera_state().is_active());
861 clear_platform_camera();
862 }
863
864 #[test]
865 fn stopping_releases_the_device_and_says_so() {
866 let _guard = crate::registry::test_service_guard();
867 clear_platform_camera();
868 let backend = FakeCamera::new();
869 set_platform_camera(backend.clone());
870 start_camera().expect("the session starts");
871 stop_camera();
872 assert_eq!(backend.stopped.load(Ordering::Relaxed), 1);
873 assert_eq!(camera_state(), CameraState::Stopped);
874 clear_platform_camera();
875 }
876
877 #[test]
880 fn the_stored_frame_is_always_the_newest_one() {
881 let _guard = crate::registry::test_service_guard();
882 clear_platform_camera();
883 assert_eq!(latest_camera_frame(), None);
884 publish_camera_frame(rgba_frame(1));
885 publish_camera_frame(rgba_frame(2));
886 publish_camera_frame(rgba_frame(3));
887 assert_eq!(latest_camera_frame().map(|frame| frame.sequence), Some(3));
888 clear_platform_camera();
889 }
890
891 #[test]
892 fn frame_observers_see_frames_and_stop_when_dropped() {
893 let _guard = crate::registry::test_service_guard();
894 clear_platform_camera();
895 let seen = Arc::new(Mutex::new(Vec::new()));
896 let recorder = Arc::clone(&seen);
897 let observer = observe_camera_frames(move |frame| {
898 recorder
899 .lock()
900 .unwrap_or_else(|error| error.into_inner())
901 .push(frame.sequence)
902 });
903 publish_camera_frame(rgba_frame(1));
904 publish_camera_frame(rgba_frame(2));
905 drop(observer);
906 publish_camera_frame(rgba_frame(3));
907 assert_eq!(
908 *seen.lock().unwrap_or_else(|error| error.into_inner()),
909 vec![1, 2]
910 );
911 clear_platform_camera();
912 }
913
914 #[test]
917 fn frames_the_platform_could_not_deliver_are_counted_rather_than_queued() {
918 let _guard = crate::registry::test_service_guard();
919 clear_platform_camera();
920 assert_eq!(dropped_camera_frames(), 0);
921 record_dropped_camera_frame();
922 record_dropped_camera_frame();
923 assert_eq!(dropped_camera_frames(), 2);
924 publish_camera_state(CameraState::Starting);
927 assert_eq!(dropped_camera_frames(), 0);
928 assert_eq!(latest_camera_frame(), None);
929 clear_platform_camera();
930 }
931
932 #[test]
935 fn a_still_is_asked_for_and_arrives_separately() {
936 let _guard = crate::registry::test_service_guard();
937 clear_platform_camera();
938 let backend = FakeCamera::new();
939 set_platform_camera(backend.clone());
940
941 assert_eq!(
942 request_camera_still(),
943 Err(CameraError::NotRunning),
944 "nothing is running, so there is nothing to photograph"
945 );
946
947 let seen = Arc::new(Mutex::new(Vec::new()));
948 let recorder = Arc::clone(&seen);
949 let observer = observe_camera_stills(move |still| {
950 recorder
951 .lock()
952 .unwrap_or_else(|error| error.into_inner())
953 .push(still)
954 });
955 start_camera().expect("the session starts");
956 request_camera_still().expect("a still is asked for");
957 assert_eq!(backend.stills.load(Ordering::Relaxed), 1);
958 assert_eq!(
959 *seen.lock().unwrap_or_else(|error| error.into_inner()),
960 vec![Ok(CameraStill {
961 jpeg: vec![0xff, 0xd8]
962 })]
963 );
964 drop(observer);
965 clear_platform_camera();
966 }
967
968 #[test]
969 fn a_backend_that_lists_no_lens_and_no_flash_says_so() {
970 let _guard = crate::registry::test_service_guard();
971 clear_platform_camera();
972 set_platform_camera(FakeCamera::new());
973 let backend = camera().expect("registered");
974 assert!(backend.lenses().is_empty());
975 assert_eq!(backend.lens(), None);
976 assert!(!backend.use_lens("0"));
977 assert!(!backend.has_flash());
978 assert!(!backend.set_flash(FlashMode::On));
979 assert!(!backend.set_torch(true));
980 clear_platform_camera();
981 }
982
983 #[test]
984 fn a_backend_that_lists_two_lenses_hands_them_over_in_order() {
985 let _guard = crate::registry::test_service_guard();
986 clear_platform_camera();
987 struct TwoLenses;
988 impl Camera for TwoLenses {
989 fn start(&self) -> Result<(), CameraError> {
990 Ok(())
991 }
992 fn stop(&self) {}
993 fn lenses(&self) -> Vec<CameraLens> {
994 vec![
995 CameraLens {
996 id: "u".into(),
997 name: "Ultra wide".into(),
998 },
999 CameraLens {
1000 id: "w".into(),
1001 name: "Wide".into(),
1002 },
1003 ]
1004 }
1005 fn lens(&self) -> Option<String> {
1006 Some("w".into())
1007 }
1008 fn use_lens(&self, id: &str) -> bool {
1009 id == "u" || id == "w"
1010 }
1011 }
1012 set_platform_camera(Arc::new(TwoLenses));
1013 let backend = camera().expect("registered");
1014 let lenses = backend.lenses();
1015 assert_eq!(lenses.len(), 2);
1016 assert_eq!(lenses[0].name, "Ultra wide");
1017 assert_eq!(backend.lens().as_deref(), Some("w"));
1018 assert!(backend.use_lens("u"));
1019 assert!(!backend.use_lens("tele"));
1020 clear_platform_camera();
1021 }
1022}