use std::sync::Arc;
use v4l::capability::Flags;
use v4l::video::Capture;
use rustcv_core::builder::CameraConfig;
use rustcv_core::error::{CameraError, Result};
use rustcv_core::pixel_format::PixelFormat;
use rustcv_core::traits::{DeviceControls, DeviceInfo, Stream};
use crate::controls::create_controls;
use crate::pixel_map;
use crate::stream::V4l2Stream;
pub fn list_devices() -> Result<Vec<DeviceInfo>> {
let mut devices = Vec::new();
let node_iter = v4l::context::enum_devices();
for node in node_iter {
let path = node.path().to_string_lossy().to_string();
if let Ok(dev) = v4l::Device::with_path(&path) {
if let Ok(caps) = dev.query_caps() {
if caps.capabilities.contains(Flags::VIDEO_CAPTURE) {
devices.push(DeviceInfo {
name: node.name().unwrap_or_else(|| "Unknown Camera".into()),
id: path, backend: "V4L2".to_string(),
bus_info: Some(caps.bus),
});
}
}
}
}
Ok(devices)
}
pub fn open(id: &str, config: CameraConfig) -> Result<(Box<dyn Stream>, DeviceControls)> {
let dev = v4l::Device::with_path(id).map_err(CameraError::Io)?;
let negotiated_fmt = negotiate_format(&dev, &config)?;
let mut fmt = dev.format().map_err(CameraError::Io)?;
fmt.width = negotiated_fmt.width;
fmt.height = negotiated_fmt.height;
fmt.fourcc =
pixel_map::to_v4l_fourcc(negotiated_fmt.format).ok_or(CameraError::FormatNotSupported)?;
let applied_fmt = dev.set_format(&fmt).map_err(CameraError::Io)?;
tracing::info!(
"Camera opened: {}x{} @ {}",
applied_fmt.width,
applied_fmt.height,
applied_fmt.fourcc
);
let dev_arc = Arc::new(dev);
let stream = V4l2Stream::new(dev_arc.clone(), &applied_fmt, config.buffer_count)?;
let controls = create_controls(dev_arc);
Ok((Box::new(stream), controls))
}
struct NegotiatedFormat {
width: u32,
height: u32,
format: PixelFormat,
#[allow(dead_code)]
fps: u32, }
fn negotiate_format(dev: &v4l::Device, config: &CameraConfig) -> Result<NegotiatedFormat> {
let mut best_score = -1;
let mut best_fmt = None;
let supported_formats = dev.enum_formats().map_err(CameraError::Io)?;
for v4l_fmt in supported_formats {
let core_fmt = pixel_map::from_v4l_fourcc(v4l_fmt.fourcc);
let resolutions = dev.enum_framesizes(v4l_fmt.fourcc).unwrap_or_default();
for res in resolutions {
for size in res.size.to_discrete() {
let current_score = calculate_score(config, size.width, size.height, core_fmt);
if current_score > best_score {
best_score = current_score;
best_fmt = Some(NegotiatedFormat {
width: size.width,
height: size.height,
format: core_fmt,
fps: 30, });
}
}
}
}
best_fmt.ok_or(CameraError::FormatNotSupported)
}
fn calculate_score(config: &CameraConfig, w: u32, h: u32, fmt: PixelFormat) -> i32 {
let mut score = 0;
for (req_w, req_h, prio) in &config.resolution_req {
if w == *req_w && h == *req_h {
score += *prio as i32 * 10;
}
}
for (req_fmt, prio) in &config.format_req {
if fmt == *req_fmt {
score += *prio as i32 * 10;
}
}
score += (w / 100) as i32;
score
}