camera/
hosted.rs

1// SPDX-FileCopyrightText: 2026 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::{collections::BTreeMap, sync::Mutex};
5
6use xous::MemoryRange;
7
8use crate::{CAMERA_BYTES_PER_PX, CAMERA_FB_SIZE_BYTES, CAMERA_HEIGHT, CAMERA_MARGIN, CAMERA_WIDTH};
9
10pub struct Frame {
11    id: u32,
12}
13
14static SHMEM_CACHE: Mutex<BTreeMap<u32, MemoryRange>> = Mutex::new(BTreeMap::new());
15
16impl Frame {
17    pub fn allocate() -> (Self, &'static mut [u8]) {
18        for _ in 0..10 {
19            let id = rand::random::<u32>();
20            if let Ok(shmem) =
21                shared_memory::ShmemConf::new().size(CAMERA_FB_SIZE_BYTES).os_id(Self::os_id(id)).create()
22            {
23                let shmem = Box::leak(Box::new(shmem));
24                let slice = unsafe { shmem.as_slice_mut() };
25                for pixel in slice.chunks_mut(4) {
26                    // Set alpha to opaque
27                    pixel[3] = 255;
28                }
29                return (Self { id }, slice);
30            }
31        }
32        panic!("Could not create shmem for camera");
33    }
34
35    fn os_id(id: u32) -> String { format!("/xous_cam_buf_{id}") }
36
37    pub fn padded_range(&self) -> xous::MemoryRange {
38        if let Some(range) = SHMEM_CACHE.lock().unwrap().get(&self.id) {
39            return *range;
40        }
41        let shmem = shared_memory::ShmemConf::new()
42            .size(CAMERA_FB_SIZE_BYTES)
43            .os_id(Self::os_id(self.id))
44            .open()
45            .unwrap();
46        let shmem = Box::leak(Box::new(shmem));
47        let slice = unsafe { shmem.as_slice_mut() };
48        let range = unsafe { MemoryRange::new(slice.as_ptr() as usize, slice.len()).unwrap() };
49        SHMEM_CACHE.lock().unwrap().insert(self.id, range);
50        range
51    }
52
53    pub fn content(&self) -> xous::MemoryRange {
54        self.padded_range()
55            .subrange(
56                CAMERA_WIDTH * CAMERA_MARGIN * CAMERA_BYTES_PER_PX,
57                CAMERA_WIDTH * CAMERA_HEIGHT * CAMERA_BYTES_PER_PX,
58            )
59            .unwrap()
60    }
61}
62
63impl server::AsScalar<1> for Frame {
64    fn as_scalar(&self) -> [u32; 1] { [self.id] }
65}
66
67impl server::FromScalar<1> for Frame {
68    fn from_scalar([id]: [u32; 1]) -> Self { Self { id } }
69}