use crate::errors::CameraError;
use crate::types::{CameraDeviceInfo, CameraFormat, CameraFrame, CameraInitParams};
use nokhwa::{
pixel_format::RgbFormat,
query,
utils::{RequestedFormat, RequestedFormatType},
Camera,
};
use std::sync::{Arc, Mutex};
pub fn list_cameras() -> Result<Vec<CameraDeviceInfo>, CameraError> {
let cameras = query(nokhwa::utils::ApiBackend::AVFoundation)
.map_err(|e| CameraError::InitializationError(format!("Failed to query cameras: {}", e)))?;
let mut device_list = Vec::new();
for camera_info in cameras {
let mut device =
CameraDeviceInfo::new(camera_info.index().to_string(), camera_info.human_name());
device = device.with_description(camera_info.description().to_string());
let formats = vec![
CameraFormat::new(1920, 1080, 30.0),
CameraFormat::new(1280, 720, 30.0),
CameraFormat::new(640, 480, 30.0),
];
device = device.with_formats(formats);
device_list.push(device);
}
Ok(device_list)
}
pub fn initialize_camera(params: CameraInitParams) -> Result<MacOSCamera, CameraError> {
let device_index = params
.device_id
.parse::<u32>()
.map_err(|_| CameraError::InitializationError("Invalid device ID".to_string()))?;
let requested_format = RequestedFormat::new::<RgbFormat>(RequestedFormatType::Exact(
nokhwa::utils::CameraFormat::new(
nokhwa::utils::Resolution::new(params.format.width, params.format.height),
nokhwa::utils::FrameFormat::MJPEG,
params.format.fps as u32,
),
));
let camera = Camera::new(
nokhwa::utils::CameraIndex::Index(device_index),
requested_format,
)
.map_err(|e| CameraError::InitializationError(format!("Failed to initialize camera: {}", e)))?;
Ok(MacOSCamera {
camera: Arc::new(Mutex::new(camera)),
device_id: params.device_id,
format: params.format,
callback: Arc::new(Mutex::new(None)),
})
}
pub struct MacOSCamera {
camera: Arc<Mutex<Camera>>,
device_id: String,
format: CameraFormat,
callback: Arc<Mutex<Option<Box<dyn Fn(CameraFrame) + Send + 'static>>>>,
}
impl MacOSCamera {
pub fn capture_frame(&self) -> Result<CameraFrame, CameraError> {
let mut camera = self
.camera
.lock()
.map_err(|_| CameraError::CaptureError("Failed to lock camera".to_string()))?;
let frame = camera
.frame()
.map_err(|e| CameraError::CaptureError(format!("Failed to capture frame: {}", e)))?;
let camera_frame = CameraFrame::new(
frame.buffer_bytes().to_vec(),
frame.resolution().width_x,
frame.resolution().height_y,
self.device_id.clone(),
);
let camera_frame = camera_frame.with_format(format!("{:?}", self.format));
if let Some(ref cb) = *self.callback.lock().unwrap() {
cb(camera_frame.clone());
}
Ok(camera_frame)
}
pub fn get_format(&self) -> &CameraFormat {
&self.format
}
pub fn get_device_id(&self) -> &str {
&self.device_id
}
pub fn is_available(&self) -> bool {
self.camera
.lock()
.map(|c| c.is_stream_open())
.unwrap_or(false)
}
pub fn start_stream(&self) -> Result<(), CameraError> {
let mut camera = self
.camera
.lock()
.map_err(|_| CameraError::InitializationError("Failed to lock camera".to_string()))?;
camera.open_stream().map_err(|e| {
CameraError::InitializationError(format!("Failed to start stream: {}", e))
})?;
Ok(())
}
pub fn stop_stream(&self) -> Result<(), CameraError> {
let mut camera = self
.camera
.lock()
.map_err(|_| CameraError::InitializationError("Failed to lock camera".to_string()))?;
camera.stop_stream().map_err(|e| {
CameraError::InitializationError(format!("Failed to stop stream: {}", e))
})?;
Ok(())
}
pub fn get_controls(&self) -> Result<crate::types::CameraControls, CameraError> {
Ok(crate::types::CameraControls::default())
}
pub fn apply_controls(
&mut self,
_controls: &crate::types::CameraControls,
) -> Result<(), CameraError> {
Ok(())
}
pub fn test_capabilities(&self) -> Result<crate::types::CameraCapabilities, CameraError> {
Ok(crate::types::CameraCapabilities::default())
}
pub fn get_performance_metrics(
&self,
) -> Result<crate::types::CameraPerformanceMetrics, CameraError> {
Ok(crate::types::CameraPerformanceMetrics::default())
}
pub fn set_callback<F>(&self, callback: F) -> Result<(), CameraError>
where
F: Fn(CameraFrame) + Send + 'static,
{
*self.callback.lock().unwrap() = Some(Box::new(callback));
Ok(())
}
}
impl Drop for MacOSCamera {
fn drop(&mut self) {
if let Ok(mut camera) = self.camera.lock() {
let _ = camera.stop_stream();
}
}
}
unsafe impl Send for MacOSCamera {}
unsafe impl Sync for MacOSCamera {}