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 => {
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 pub fn upright_rgba8(&self) -> UprightRgba {
137 let rotation = self.rotation_degrees;
138 if !matches!(rotation, 90 | 180 | 270) {
139 return UprightRgba {
140 width: self.width,
141 height: self.height,
142 rgba: self.to_rgba8(),
143 };
144 }
145 let (width, height) = (self.width as usize, self.height as usize);
146 let (out_width, out_height) = match rotation {
147 90 | 270 => (self.height, self.width),
148 _ => (self.width, self.height),
149 };
150 let mut rgba = vec![0u8; width * height * 4];
151 match self.format {
152 FrameFormat::Rgba8 => {
153 for y in 0..height {
154 let row = &self.bytes[y * width * 4..(y + 1) * width * 4];
155 for x in 0..width {
156 let src = &row[x * 4..x * 4 + 4];
157 let dst = turned_index(rotation, width, height, x, y) * 4;
158 rgba[dst..dst + 4].copy_from_slice(src);
159 }
160 }
161 }
162 FrameFormat::Rgb8 => {
163 for y in 0..height {
164 let row = &self.bytes[y * width * 3..(y + 1) * width * 3];
165 for x in 0..width {
166 let src = &row[x * 3..x * 3 + 3];
167 let dst = turned_index(rotation, width, height, x, y) * 4;
168 rgba[dst..dst + 3].copy_from_slice(src);
169 rgba[dst + 3] = 255;
170 }
171 }
172 }
173 FrameFormat::Nv12 => {
174 let pixels = width * height;
175 if self.bytes.len() < pixels + pixels / 2 || width == 0 || height == 0 {
176 return UprightRgba {
177 width: out_width,
178 height: out_height,
179 rgba,
180 };
181 }
182 let (luma, chroma) = self.bytes.split_at(pixels);
183 for y in 0..height {
184 let luma_row = &luma[y * width..(y + 1) * width];
185 let chroma_row = &chroma[(y / 2) * width..(y / 2 + 1) * width];
186 for x in 0..width {
187 let luminance = luma_row[x] as i32;
188 let blue_difference = chroma_row[x & !1] as i32 - 128;
189 let red_difference = chroma_row[(x & !1) + 1] as i32 - 128;
190 let dst = turned_index(rotation, width, height, x, y) * 4;
191 rgba[dst] = clamp_byte(luminance + ((91881 * red_difference) >> 16));
192 rgba[dst + 1] = clamp_byte(
193 luminance - ((22554 * blue_difference + 46802 * red_difference) >> 16),
194 );
195 rgba[dst + 2] = clamp_byte(luminance + ((116130 * blue_difference) >> 16));
196 rgba[dst + 3] = 255;
197 }
198 }
199 }
200 }
201 UprightRgba {
202 width: out_width,
203 height: out_height,
204 rgba,
205 }
206 }
207}
208
209#[derive(Clone, Debug, PartialEq, Eq)]
214pub struct UprightRgba {
215 pub width: u32,
216 pub height: u32,
217 pub rgba: Vec<u8>,
218}
219
220#[inline]
223fn turned_index(rotation: u16, width: usize, height: usize, x: usize, y: usize) -> usize {
224 match rotation {
225 90 => x * height + (height - 1 - y),
226 180 => (height - 1 - y) * width + (width - 1 - x),
227 270 => (width - 1 - x) * height + y,
228 _ => y * width + x,
229 }
230}
231
232fn rgb8_to_rgba8(bytes: &[u8]) -> Vec<u8> {
234 let mut rgba = Vec::with_capacity(bytes.len() / 3 * 4);
235 for [red, green, blue] in bytes.as_chunks::<3>().0 {
238 rgba.extend_from_slice(&[*red, *green, *blue, 255]);
239 }
240 rgba
241}
242
243fn nv12_to_rgba8(width: u32, height: u32, bytes: &[u8]) -> Vec<u8> {
250 let (width, height) = (width as usize, height as usize);
251 let pixels = width * height;
252 let mut rgba = vec![0u8; pixels * 4];
253 if bytes.len() < pixels + pixels / 2 || width == 0 || height == 0 {
254 return rgba;
255 }
256 let (luma, chroma) = bytes.split_at(pixels);
257
258 for y in 0..height {
259 let luma_row = &luma[y * width..(y + 1) * width];
260 let chroma_row = &chroma[(y / 2) * width..(y / 2 + 1) * width];
261 let out_row = &mut rgba[y * width * 4..(y + 1) * width * 4];
262 for x in 0..width {
263 let luminance = luma_row[x] as i32;
264 let blue_difference = chroma_row[x & !1] as i32 - 128;
265 let red_difference = chroma_row[(x & !1) + 1] as i32 - 128;
266 let out = &mut out_row[x * 4..x * 4 + 4];
267 out[0] = clamp_byte(luminance + ((91881 * red_difference) >> 16));
268 out[1] =
269 clamp_byte(luminance - ((22554 * blue_difference + 46802 * red_difference) >> 16));
270 out[2] = clamp_byte(luminance + ((116130 * blue_difference) >> 16));
271 out[3] = 255;
272 }
273 }
274 rgba
275}
276
277fn clamp_byte(value: i32) -> u8 {
278 value.clamp(0, 255) as u8
279}
280
281#[derive(Clone, Debug, PartialEq, Eq)]
288pub struct CameraStill {
289 pub jpeg: Vec<u8>,
290}
291
292#[derive(Clone, Debug, PartialEq, Eq)]
299pub struct CameraLens {
300 pub id: String,
301 pub name: String,
302 pub facing: LensFacing,
305}
306
307#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
309pub enum LensFacing {
310 #[default]
312 Back,
313 Front,
315 External,
317}
318
319#[derive(Clone, Debug, Default, PartialEq, Eq)]
327pub struct CameraLenses {
328 pub lenses: Vec<CameraLens>,
330 pub active: Option<String>,
333}
334
335#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
341pub enum FlashMode {
342 #[default]
343 Off,
344 Auto,
345 On,
346}
347
348#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
349pub enum CameraError {
350 #[error("live camera capture is not supported here")]
352 Unsupported,
353 #[error("camera permission denied")]
355 PermissionDenied,
356 #[error("the camera is not running")]
358 NotRunning,
359 #[error("{0}")]
361 Failed(String),
362}
363
364#[derive(Clone, Debug, Default, PartialEq, Eq)]
366pub enum CameraState {
367 #[default]
369 Idle,
370 Starting,
373 Running {
375 device: String,
377 },
378 Stopped,
380 Failed(CameraError),
382}
383
384impl CameraState {
385 pub fn is_running(&self) -> bool {
387 matches!(self, CameraState::Running { .. })
388 }
389
390 pub fn is_active(&self) -> bool {
393 matches!(self, CameraState::Starting | CameraState::Running { .. })
394 }
395
396 pub fn failure(&self) -> Option<&CameraError> {
398 match self {
399 CameraState::Failed(error) => Some(error),
400 _ => None,
401 }
402 }
403}
404
405pub trait Camera: Send + Sync {
411 fn start(&self) -> Result<(), CameraError>;
417
418 fn stop(&self);
420
421 fn request_still(&self) -> Result<(), CameraError> {
427 Err(CameraError::Unsupported)
428 }
429
430 fn set_torch(&self, _on: bool) -> bool {
436 false
437 }
438
439 fn lenses(&self) -> Vec<CameraLens> {
448 Vec::new()
449 }
450
451 fn lens(&self) -> Option<String> {
454 None
455 }
456
457 fn use_lens(&self, _id: &str) -> bool {
460 false
461 }
462
463 fn has_flash(&self) -> bool {
465 false
466 }
467
468 fn set_flash(&self, _mode: FlashMode) -> bool {
471 false
472 }
473}
474
475pub type CameraRef = Arc<dyn Camera>;
477
478static PLATFORM_CAMERA: ServiceRegistry<dyn Camera> = ServiceRegistry::new();
479
480pub fn set_platform_camera(camera: CameraRef) {
482 PLATFORM_CAMERA.set(camera);
483}
484
485pub fn clear_platform_camera() {
488 PLATFORM_CAMERA.clear();
489 if let Ok(mut observers) = frame_observers().lock() {
490 observers.clear();
491 }
492 if let Ok(mut observers) = state_observers().lock() {
493 observers.clear();
494 }
495 if let Ok(mut observers) = still_observers().lock() {
496 observers.clear();
497 }
498 if let Ok(mut observers) = lens_observers().lock() {
499 observers.clear();
500 }
501 if let Ok(mut latest) = latest_frame_slot().lock() {
502 *latest = None;
503 }
504 if let Ok(mut state) = state_slot().lock() {
505 *state = CameraState::Idle;
506 }
507 if let Ok(mut lenses) = lenses_slot().lock() {
508 *lenses = CameraLenses::default();
509 }
510 DROPPED_FRAMES.store(0, Ordering::Release);
511}
512
513pub fn camera() -> Option<CameraRef> {
516 PLATFORM_CAMERA.get()
517}
518
519pub fn camera_supported() -> bool {
521 PLATFORM_CAMERA.get().is_some()
522}
523
524fn state_slot() -> &'static Mutex<CameraState> {
525 static SLOT: OnceLock<Mutex<CameraState>> = OnceLock::new();
526 SLOT.get_or_init(|| Mutex::new(CameraState::Idle))
527}
528
529fn latest_frame_slot() -> &'static Mutex<Option<CameraFrame>> {
530 static SLOT: OnceLock<Mutex<Option<CameraFrame>>> = OnceLock::new();
531 SLOT.get_or_init(|| Mutex::new(None))
532}
533
534fn lenses_slot() -> &'static Mutex<CameraLenses> {
535 static SLOT: OnceLock<Mutex<CameraLenses>> = OnceLock::new();
536 SLOT.get_or_init(|| Mutex::new(CameraLenses::default()))
537}
538
539static DROPPED_FRAMES: AtomicU64 = AtomicU64::new(0);
545
546type FrameObserver = Arc<dyn Fn(CameraFrame) + Send + Sync>;
547type StateObserver = Arc<dyn Fn(CameraState) + Send + Sync>;
548type StillObserver = Arc<dyn Fn(Result<CameraStill, CameraError>) + Send + Sync>;
549type LensObserver = Arc<dyn Fn(CameraLenses) + Send + Sync>;
550
551fn frame_observers() -> &'static Mutex<Vec<(u64, FrameObserver)>> {
552 static SLOT: OnceLock<Mutex<Vec<(u64, FrameObserver)>>> = OnceLock::new();
553 SLOT.get_or_init(|| Mutex::new(Vec::new()))
554}
555
556fn state_observers() -> &'static Mutex<Vec<(u64, StateObserver)>> {
557 static SLOT: OnceLock<Mutex<Vec<(u64, StateObserver)>>> = OnceLock::new();
558 SLOT.get_or_init(|| Mutex::new(Vec::new()))
559}
560
561fn still_observers() -> &'static Mutex<Vec<(u64, StillObserver)>> {
562 static SLOT: OnceLock<Mutex<Vec<(u64, StillObserver)>>> = OnceLock::new();
563 SLOT.get_or_init(|| Mutex::new(Vec::new()))
564}
565
566fn lens_observers() -> &'static Mutex<Vec<(u64, LensObserver)>> {
567 static SLOT: OnceLock<Mutex<Vec<(u64, LensObserver)>>> = OnceLock::new();
568 SLOT.get_or_init(|| Mutex::new(Vec::new()))
569}
570
571static NEXT_OBSERVER: AtomicU64 = AtomicU64::new(1);
572
573pub struct CameraObserver {
575 id: u64,
576 kind: ObserverKind,
577}
578
579#[derive(Clone, Copy)]
580enum ObserverKind {
581 Frame,
582 State,
583 Still,
584 Lenses,
585}
586
587impl Drop for CameraObserver {
588 fn drop(&mut self) {
589 match self.kind {
590 ObserverKind::Frame => retain_without(frame_observers(), self.id),
591 ObserverKind::State => retain_without(state_observers(), self.id),
592 ObserverKind::Still => retain_without(still_observers(), self.id),
593 ObserverKind::Lenses => retain_without(lens_observers(), self.id),
594 }
595 }
596}
597
598fn retain_without<T>(slot: &'static Mutex<Vec<(u64, T)>>, id: u64) {
599 if let Ok(mut observers) = slot.lock() {
600 observers.retain(|(observer, _)| *observer != id);
601 }
602}
603
604fn snapshot<T: Clone>(slot: &'static Mutex<Vec<(u64, T)>>) -> Vec<T> {
605 slot.lock()
606 .map(|observers| {
607 observers
608 .iter()
609 .map(|(_, observer)| observer.clone())
610 .collect()
611 })
612 .unwrap_or_default()
613}
614
615pub fn latest_camera_frame() -> Option<CameraFrame> {
620 latest_frame_slot()
621 .lock()
622 .map(|frame| frame.clone())
623 .unwrap_or(None)
624}
625
626pub fn camera_state() -> CameraState {
628 state_slot()
629 .lock()
630 .map(|state| state.clone())
631 .unwrap_or_default()
632}
633
634pub fn dropped_camera_frames() -> u64 {
636 DROPPED_FRAMES.load(Ordering::Acquire)
637}
638
639pub fn publish_camera_frame(frame: CameraFrame) {
646 if let Ok(mut latest) = latest_frame_slot().lock() {
647 *latest = Some(frame.clone());
648 }
649 let observers = snapshot(frame_observers());
650 if observers.is_empty() {
651 return;
652 }
653 for observer in observers {
654 observer(frame.clone());
655 }
656}
657
658pub fn record_dropped_camera_frame() {
663 DROPPED_FRAMES.fetch_add(1, Ordering::AcqRel);
664}
665
666pub fn publish_camera_state(state: CameraState) {
668 {
669 let Ok(mut current) = state_slot().lock() else {
670 return;
671 };
672 if *current == state {
673 return;
674 }
675 *current = state.clone();
676 }
677 if matches!(state, CameraState::Idle | CameraState::Starting) {
678 DROPPED_FRAMES.store(0, Ordering::Release);
679 if let Ok(mut latest) = latest_frame_slot().lock() {
680 *latest = None;
681 }
682 }
683 for observer in snapshot(state_observers()) {
684 observer(state.clone());
685 }
686}
687
688pub fn publish_camera_still(still: Result<CameraStill, CameraError>) {
690 for observer in snapshot(still_observers()) {
691 observer(still.clone());
692 }
693}
694
695pub fn publish_camera_lenses(lenses: CameraLenses) {
698 {
699 let Ok(mut current) = lenses_slot().lock() else {
700 return;
701 };
702 if *current == lenses {
703 return;
704 }
705 *current = lenses.clone();
706 }
707 for observer in snapshot(lens_observers()) {
708 observer(lenses.clone());
709 }
710}
711
712pub fn camera_lenses() -> CameraLenses {
715 lenses_slot()
716 .lock()
717 .map(|lenses| lenses.clone())
718 .unwrap_or_default()
719}
720
721pub fn observe_camera_frames(
724 observer: impl Fn(CameraFrame) + Send + Sync + 'static,
725) -> CameraObserver {
726 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
727 if let Ok(mut observers) = frame_observers().lock() {
728 observers.push((id, Arc::new(observer)));
729 }
730 CameraObserver {
731 id,
732 kind: ObserverKind::Frame,
733 }
734}
735
736pub fn observe_camera_state(
740 observer: impl Fn(CameraState) + Send + Sync + 'static,
741) -> CameraObserver {
742 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
743 let observer: StateObserver = Arc::new(observer);
744 if let Ok(mut observers) = state_observers().lock() {
745 observers.push((id, Arc::clone(&observer)));
746 }
747 observer(camera_state());
748 CameraObserver {
749 id,
750 kind: ObserverKind::State,
751 }
752}
753
754pub fn observe_camera_stills(
756 observer: impl Fn(Result<CameraStill, CameraError>) + Send + Sync + 'static,
757) -> CameraObserver {
758 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
759 if let Ok(mut observers) = still_observers().lock() {
760 observers.push((id, Arc::new(observer)));
761 }
762 CameraObserver {
763 id,
764 kind: ObserverKind::Still,
765 }
766}
767
768pub fn observe_camera_lenses(
772 observer: impl Fn(CameraLenses) + Send + Sync + 'static,
773) -> CameraObserver {
774 let id = NEXT_OBSERVER.fetch_add(1, Ordering::Relaxed);
775 let observer: LensObserver = Arc::new(observer);
776 if let Ok(mut observers) = lens_observers().lock() {
777 observers.push((id, Arc::clone(&observer)));
778 }
779 observer(camera_lenses());
780 CameraObserver {
781 id,
782 kind: ObserverKind::Lenses,
783 }
784}
785
786#[allow(non_snake_case)]
789#[track_caller]
790pub fn rememberCameraState() -> State<CameraState> {
791 let updates = rememberEventStream((), |sender| {
792 observe_camera_state(move |state| sender.send(state))
793 });
794 cranpose_core::collectAsState(updates, (), camera_state())
795}
796
797#[allow(non_snake_case)]
804#[track_caller]
805pub fn rememberCameraFrames() -> EventStream<CameraFrame> {
806 rememberEventStream((), |sender| {
807 observe_camera_frames(move |frame| sender.send(frame))
808 })
809}
810
811#[allow(non_snake_case)]
813#[track_caller]
814pub fn rememberCameraStills() -> EventStream<Result<CameraStill, CameraError>> {
815 rememberEventStream((), |sender| {
816 observe_camera_stills(move |still| sender.send(still))
817 })
818}
819
820#[allow(non_snake_case)]
823#[track_caller]
824pub fn rememberCameraLenses() -> State<CameraLenses> {
825 let updates = rememberEventStream((), |sender| {
826 observe_camera_lenses(move |lenses| sender.send(lenses))
827 });
828 cranpose_core::collectAsState(updates, (), camera_lenses())
829}
830
831pub fn start_camera() -> Result<(), CameraError> {
834 let Some(camera) = camera() else {
835 publish_camera_state(CameraState::Failed(CameraError::Unsupported));
836 return Err(CameraError::Unsupported);
837 };
838 publish_camera_state(CameraState::Starting);
839 camera.start().inspect_err(|error| {
840 publish_camera_state(CameraState::Failed(error.clone()));
841 })
842}
843
844pub fn stop_camera() {
846 if let Some(camera) = camera() {
847 camera.stop();
848 }
849 publish_camera_state(CameraState::Stopped);
850}
851
852pub fn request_camera_still() -> Result<(), CameraError> {
855 let Some(camera) = camera() else {
856 return Err(CameraError::Unsupported);
857 };
858 if !camera_state().is_running() {
859 return Err(CameraError::NotRunning);
860 }
861 camera.request_still()
862}
863
864pub async fn capture_camera_still() -> Result<CameraStill, CameraError> {
874 let signal = crate::async_io::Signal::new();
875 let deliver = signal.clone();
876 let observer = observe_camera_stills(move |result| deliver.set(result));
877 request_camera_still()?;
878 let arrived = signal.wait().await;
879 drop(observer);
880 arrived.unwrap_or(Err(CameraError::NotRunning))
883}
884
885#[cfg(test)]
886mod tests {
887 use super::*;
888
889 struct FakeCamera {
892 started: AtomicU64,
893 stopped: AtomicU64,
894 stills: AtomicU64,
895 fails: bool,
896 }
897
898 impl FakeCamera {
899 fn new() -> Arc<Self> {
900 Arc::new(Self {
901 started: AtomicU64::new(0),
902 stopped: AtomicU64::new(0),
903 stills: AtomicU64::new(0),
904 fails: false,
905 })
906 }
907
908 fn failing() -> Arc<Self> {
909 Arc::new(Self {
910 started: AtomicU64::new(0),
911 stopped: AtomicU64::new(0),
912 stills: AtomicU64::new(0),
913 fails: true,
914 })
915 }
916 }
917
918 impl Camera for FakeCamera {
919 fn start(&self) -> Result<(), CameraError> {
920 self.started.fetch_add(1, Ordering::Relaxed);
921 if self.fails {
922 return Err(CameraError::PermissionDenied);
923 }
924 publish_camera_state(CameraState::Running {
925 device: "fake".to_string(),
926 });
927 Ok(())
928 }
929
930 fn stop(&self) {
931 self.stopped.fetch_add(1, Ordering::Relaxed);
932 }
933
934 fn request_still(&self) -> Result<(), CameraError> {
935 self.stills.fetch_add(1, Ordering::Relaxed);
936 publish_camera_still(Ok(CameraStill {
937 jpeg: vec![0xff, 0xd8],
938 }));
939 Ok(())
940 }
941 }
942
943 fn rgba_frame(sequence: u64) -> CameraFrame {
944 CameraFrame::new(2, 2, FrameFormat::Rgba8, 90, sequence, vec![7; 16])
945 .expect("a well-formed frame")
946 }
947
948 #[test]
949 fn a_frame_size_is_the_one_its_format_implies() {
950 assert_eq!(FrameFormat::Rgba8.byte_len(4, 2), Some(32));
951 assert_eq!(FrameFormat::Nv12.byte_len(4, 2), Some(12));
952 assert_eq!(
953 FrameFormat::Nv12.byte_len(3, 2),
954 None,
955 "NV12 has no half column for an odd width"
956 );
957 assert_eq!(FrameFormat::Nv12.byte_len(4, 3), None);
958 }
959
960 #[test]
963 fn a_frame_that_does_not_match_its_size_is_refused() {
964 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 15]).is_none());
965 assert!(CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, vec![0; 5]).is_none());
966 assert!(CameraFrame::new(2, 2, FrameFormat::Rgba8, 0, 0, vec![0; 16]).is_some());
967 }
968
969 #[test]
970 fn a_rotation_is_kept_inside_one_turn() {
971 let frame = CameraFrame::new(2, 2, FrameFormat::Rgba8, 450, 0, vec![0; 16])
972 .expect("a well-formed frame");
973 assert_eq!(frame.rotation_degrees, 90);
974 }
975
976 #[test]
977 fn an_rgba_frame_is_handed_over_unchanged() {
978 let frame = rgba_frame(0);
979 assert_eq!(frame.to_rgba8(), frame.bytes);
980 }
981
982 #[test]
986 fn nv12_black_white_and_primaries_convert_to_the_expected_colours() {
987 fn convert(luma: u8, blue: u8, red: u8) -> [u8; 4] {
988 let bytes = vec![luma, luma, luma, luma, blue, red];
989 let frame = CameraFrame::new(2, 2, FrameFormat::Nv12, 0, 0, bytes)
990 .expect("a well-formed frame");
991 let rgba = frame.to_rgba8();
992 [rgba[0], rgba[1], rgba[2], rgba[3]]
993 }
994
995 assert_eq!(convert(0, 128, 128), [0, 0, 0, 255], "black");
996 assert_eq!(convert(255, 128, 128), [255, 255, 255, 255], "white");
997
998 let red = convert(76, 84, 255);
999 assert!(
1000 red[0] > 240 && red[1] < 20 && red[2] < 20,
1001 "red, got {red:?}"
1002 );
1003 let blue = convert(29, 255, 107);
1004 assert!(
1005 blue[2] > 240 && blue[0] < 20 && blue[1] < 20,
1006 "blue, got {blue:?}"
1007 );
1008 assert!(
1009 convert(128, 128, 128).iter().all(|value| *value > 0),
1010 "a grey frame must not clamp to black"
1011 );
1012 }
1013
1014 #[test]
1015 fn a_short_nv12_frame_converts_to_black_rather_than_reading_past_its_end() {
1016 let rgba = nv12_to_rgba8(4, 4, &[0u8; 3]);
1017 assert_eq!(rgba.len(), 4 * 4 * 4);
1018 assert!(rgba.iter().all(|value| *value == 0));
1019 }
1020
1021 #[test]
1022 fn a_platform_without_a_camera_says_so_rather_than_pretending() {
1023 let _guard = crate::registry::test_service_guard();
1024 clear_platform_camera();
1025 assert!(!camera_supported());
1026 assert_eq!(start_camera(), Err(CameraError::Unsupported));
1027 assert_eq!(
1028 camera_state(),
1029 CameraState::Failed(CameraError::Unsupported)
1030 );
1031 assert_eq!(request_camera_still(), Err(CameraError::Unsupported));
1032 clear_platform_camera();
1033 }
1034
1035 #[test]
1038 fn the_session_reports_starting_before_it_reports_running() {
1039 let _guard = crate::registry::test_service_guard();
1040 clear_platform_camera();
1041 let seen = Arc::new(Mutex::new(Vec::new()));
1042 let recorder = Arc::clone(&seen);
1043 let observer = observe_camera_state(move |state| {
1044 recorder
1045 .lock()
1046 .unwrap_or_else(|error| error.into_inner())
1047 .push(state)
1048 });
1049 set_platform_camera(FakeCamera::new());
1050 start_camera().expect("the session starts");
1051
1052 assert_eq!(
1053 *seen.lock().unwrap_or_else(|error| error.into_inner()),
1054 vec![
1055 CameraState::Idle,
1056 CameraState::Starting,
1057 CameraState::Running {
1058 device: "fake".to_string()
1059 }
1060 ]
1061 );
1062 assert!(camera_state().is_running());
1063 assert!(camera_state().is_active());
1064 drop(observer);
1065 clear_platform_camera();
1066 }
1067
1068 #[test]
1069 fn a_session_that_cannot_open_reports_why() {
1070 let _guard = crate::registry::test_service_guard();
1071 clear_platform_camera();
1072 set_platform_camera(FakeCamera::failing());
1073 assert_eq!(start_camera(), Err(CameraError::PermissionDenied));
1074 assert_eq!(
1075 camera_state().failure(),
1076 Some(&CameraError::PermissionDenied)
1077 );
1078 assert!(!camera_state().is_active());
1079 clear_platform_camera();
1080 }
1081
1082 #[test]
1083 fn stopping_releases_the_device_and_says_so() {
1084 let _guard = crate::registry::test_service_guard();
1085 clear_platform_camera();
1086 let backend = FakeCamera::new();
1087 set_platform_camera(backend.clone());
1088 start_camera().expect("the session starts");
1089 stop_camera();
1090 assert_eq!(backend.stopped.load(Ordering::Relaxed), 1);
1091 assert_eq!(camera_state(), CameraState::Stopped);
1092 clear_platform_camera();
1093 }
1094
1095 #[test]
1098 fn the_stored_frame_is_always_the_newest_one() {
1099 let _guard = crate::registry::test_service_guard();
1100 clear_platform_camera();
1101 assert_eq!(latest_camera_frame(), None);
1102 publish_camera_frame(rgba_frame(1));
1103 publish_camera_frame(rgba_frame(2));
1104 publish_camera_frame(rgba_frame(3));
1105 assert_eq!(latest_camera_frame().map(|frame| frame.sequence), Some(3));
1106 clear_platform_camera();
1107 }
1108
1109 #[test]
1110 fn frame_observers_see_frames_and_stop_when_dropped() {
1111 let _guard = crate::registry::test_service_guard();
1112 clear_platform_camera();
1113 let seen = Arc::new(Mutex::new(Vec::new()));
1114 let recorder = Arc::clone(&seen);
1115 let observer = observe_camera_frames(move |frame| {
1116 recorder
1117 .lock()
1118 .unwrap_or_else(|error| error.into_inner())
1119 .push(frame.sequence)
1120 });
1121 publish_camera_frame(rgba_frame(1));
1122 publish_camera_frame(rgba_frame(2));
1123 drop(observer);
1124 publish_camera_frame(rgba_frame(3));
1125 assert_eq!(
1126 *seen.lock().unwrap_or_else(|error| error.into_inner()),
1127 vec![1, 2]
1128 );
1129 clear_platform_camera();
1130 }
1131
1132 #[test]
1135 fn frames_the_platform_could_not_deliver_are_counted_rather_than_queued() {
1136 let _guard = crate::registry::test_service_guard();
1137 clear_platform_camera();
1138 assert_eq!(dropped_camera_frames(), 0);
1139 record_dropped_camera_frame();
1140 record_dropped_camera_frame();
1141 assert_eq!(dropped_camera_frames(), 2);
1142 publish_camera_state(CameraState::Starting);
1145 assert_eq!(dropped_camera_frames(), 0);
1146 assert_eq!(latest_camera_frame(), None);
1147 clear_platform_camera();
1148 }
1149
1150 #[test]
1153 fn a_still_is_asked_for_and_arrives_separately() {
1154 let _guard = crate::registry::test_service_guard();
1155 clear_platform_camera();
1156 let backend = FakeCamera::new();
1157 set_platform_camera(backend.clone());
1158
1159 assert_eq!(
1160 request_camera_still(),
1161 Err(CameraError::NotRunning),
1162 "nothing is running, so there is nothing to photograph"
1163 );
1164
1165 let seen = Arc::new(Mutex::new(Vec::new()));
1166 let recorder = Arc::clone(&seen);
1167 let observer = observe_camera_stills(move |still| {
1168 recorder
1169 .lock()
1170 .unwrap_or_else(|error| error.into_inner())
1171 .push(still)
1172 });
1173 start_camera().expect("the session starts");
1174 request_camera_still().expect("a still is asked for");
1175 assert_eq!(backend.stills.load(Ordering::Relaxed), 1);
1176 assert_eq!(
1177 *seen.lock().unwrap_or_else(|error| error.into_inner()),
1178 vec![Ok(CameraStill {
1179 jpeg: vec![0xff, 0xd8]
1180 })]
1181 );
1182 drop(observer);
1183 clear_platform_camera();
1184 }
1185
1186 #[test]
1187 fn a_backend_that_lists_no_lens_and_no_flash_says_so() {
1188 let _guard = crate::registry::test_service_guard();
1189 clear_platform_camera();
1190 set_platform_camera(FakeCamera::new());
1191 let backend = camera().expect("registered");
1192 assert!(backend.lenses().is_empty());
1193 assert_eq!(backend.lens(), None);
1194 assert!(!backend.use_lens("0"));
1195 assert!(!backend.has_flash());
1196 assert!(!backend.set_flash(FlashMode::On));
1197 assert!(!backend.set_torch(true));
1198 clear_platform_camera();
1199 }
1200
1201 #[test]
1202 fn a_backend_that_lists_two_lenses_hands_them_over_in_order() {
1203 let _guard = crate::registry::test_service_guard();
1204 clear_platform_camera();
1205 struct TwoLenses;
1206 impl Camera for TwoLenses {
1207 fn start(&self) -> Result<(), CameraError> {
1208 Ok(())
1209 }
1210 fn stop(&self) {}
1211 fn lenses(&self) -> Vec<CameraLens> {
1212 vec![
1213 CameraLens {
1214 id: "u".into(),
1215 name: "Ultra wide".into(),
1216 facing: LensFacing::Back,
1217 },
1218 CameraLens {
1219 id: "w".into(),
1220 name: "Wide".into(),
1221 facing: LensFacing::Back,
1222 },
1223 ]
1224 }
1225 fn lens(&self) -> Option<String> {
1226 Some("w".into())
1227 }
1228 fn use_lens(&self, id: &str) -> bool {
1229 id == "u" || id == "w"
1230 }
1231 }
1232 set_platform_camera(Arc::new(TwoLenses));
1233 let backend = camera().expect("registered");
1234 let lenses = backend.lenses();
1235 assert_eq!(lenses.len(), 2);
1236 assert_eq!(lenses[0].name, "Ultra wide");
1237 assert_eq!(backend.lens().as_deref(), Some("w"));
1238 assert!(backend.use_lens("u"));
1239 assert!(!backend.use_lens("tele"));
1240 clear_platform_camera();
1241 }
1242
1243 fn two_pixel_frame(rotation: u16) -> CameraFrame {
1244 let mut bytes = vec![10u8, 10, 10, 255];
1245 bytes.extend_from_slice(&[20, 20, 20, 255]);
1246 CameraFrame::new(2, 1, FrameFormat::Rgba8, rotation, 0, bytes).expect("a well-formed frame")
1247 }
1248
1249 fn pixel_values(image: &UprightRgba) -> Vec<u8> {
1250 image.rgba.iter().step_by(4).copied().collect()
1251 }
1252
1253 #[test]
1256 fn a_frame_turns_upright_by_its_rotation() {
1257 let unturned = two_pixel_frame(0).upright_rgba8();
1258 assert_eq!((unturned.width, unturned.height), (2, 1));
1259 assert_eq!(pixel_values(&unturned), vec![10, 20]);
1260
1261 let quarter = two_pixel_frame(90).upright_rgba8();
1262 assert_eq!((quarter.width, quarter.height), (1, 2));
1263 assert_eq!(pixel_values(&quarter), vec![10, 20]);
1264
1265 let half = two_pixel_frame(180).upright_rgba8();
1266 assert_eq!((half.width, half.height), (2, 1));
1267 assert_eq!(pixel_values(&half), vec![20, 10]);
1268
1269 let three_quarters = two_pixel_frame(270).upright_rgba8();
1270 assert_eq!((three_quarters.width, three_quarters.height), (1, 2));
1271 assert_eq!(pixel_values(&three_quarters), vec![20, 10]);
1272 }
1273
1274 #[test]
1277 fn an_nv12_frame_turns_and_converts_in_one_pass() {
1278 let bytes = vec![0, 255, 0, 0, 128, 128];
1279 let frame =
1280 CameraFrame::new(2, 2, FrameFormat::Nv12, 90, 0, bytes).expect("a well-formed frame");
1281 let upright = frame.upright_rgba8();
1282 assert_eq!((upright.width, upright.height), (2, 2));
1283 let values = pixel_values(&upright);
1284 assert_eq!(values[0], 0, "top-left stays dark");
1285 assert_eq!(
1286 values[3], 255,
1287 "the bright top-right pixel lands bottom-right"
1288 );
1289 }
1290
1291 #[test]
1292 fn an_rgb8_frame_turns_upright_with_a_full_alpha() {
1293 let frame = CameraFrame::new(
1294 2,
1295 1,
1296 FrameFormat::Rgb8,
1297 180,
1298 0,
1299 vec![10, 10, 10, 20, 20, 20],
1300 )
1301 .expect("a well-formed frame");
1302 let upright = frame.upright_rgba8();
1303 assert_eq!(pixel_values(&upright), vec![20, 10]);
1304 assert!(upright.rgba.iter().skip(3).step_by(4).all(|a| *a == 255));
1305 }
1306
1307 #[test]
1310 fn a_turn_that_is_not_a_quarter_is_left_alone() {
1311 let frame = CameraFrame::new(2, 1, FrameFormat::Rgba8, 45, 0, vec![7; 8])
1312 .expect("a well-formed frame");
1313 let upright = frame.upright_rgba8();
1314 assert_eq!((upright.width, upright.height), (2, 1));
1315 assert_eq!(upright.rgba, frame.to_rgba8());
1316 }
1317
1318 #[test]
1319 fn the_lens_list_is_published_and_observed() {
1320 let _guard = crate::registry::test_service_guard();
1321 clear_platform_camera();
1322 assert_eq!(camera_lenses(), CameraLenses::default());
1323
1324 let seen = Arc::new(Mutex::new(Vec::new()));
1325 let recorder = Arc::clone(&seen);
1326 let observer = observe_camera_lenses(move |lenses| {
1327 recorder
1328 .lock()
1329 .unwrap_or_else(|error| error.into_inner())
1330 .push(lenses)
1331 });
1332
1333 let published = CameraLenses {
1334 lenses: vec![CameraLens {
1335 id: "0".into(),
1336 name: "Back".into(),
1337 facing: LensFacing::Back,
1338 }],
1339 active: Some("0".into()),
1340 };
1341 publish_camera_lenses(published.clone());
1342 publish_camera_lenses(published.clone());
1343 assert_eq!(camera_lenses(), published);
1344 assert_eq!(
1345 *seen.lock().unwrap_or_else(|error| error.into_inner()),
1346 vec![CameraLenses::default(), published.clone()],
1347 "the current list arrives at once, and a repeat is not re-delivered"
1348 );
1349
1350 drop(observer);
1351 publish_camera_lenses(CameraLenses::default());
1352 assert_eq!(
1353 seen.lock().unwrap_or_else(|error| error.into_inner()).len(),
1354 2,
1355 "a dropped observer hears nothing more"
1356 );
1357 clear_platform_camera();
1358 assert_eq!(camera_lenses(), CameraLenses::default());
1359 }
1360}