use ffimage::color::Bgra;
use v4l::context::Node;
use v4l::io::traits::CaptureStream;
use v4l::video::Capture;
use v4l::*;
use std::marker::PhantomData;
use std::sync::RwLock;
use crate::InnerCamera;
pub struct Camera {
device: RwLock<v4l::Device>,
device_name: String,
stream: RwLock<Option<v4l::io::mmap::Stream<'static>>>,
}
fn name_or_path(device_node: &v4l::context::Node) -> String {
device_node.name().unwrap_or_else(|| device_node.path().to_string_lossy().to_string())
}
fn get_next_best_format(device: &Device) -> Format {
let _rgb = FourCC::new(b"RGB3");
let mut fmt = device.format().expect("device.format()");
let size = device
.enum_framesizes(fmt.fourcc)
.unwrap()
.into_iter()
.next()
.unwrap()
.size
.to_discrete()
.into_iter()
.last()
.unwrap();
fmt.width = size.width;
fmt.height = size.height;
fmt
}
#[allow(unused)]
fn display_node(node: &Node) {
println!(
"Node {{ index: {}, name: {:?}, path: {:?} }}",
node.index(),
node.name(),
node.path()
);
}
#[allow(unused)]
fn display_device_formats(device: &Device) {
println!("Device formats:");
for fmt in device.enum_formats().unwrap() {
println!(" {:?}", fmt);
for size in device.enum_framesizes(fmt.fourcc).unwrap() {
println!(" {:?}", size);
}
}
}
fn enum_devices() -> Vec<Node> {
v4l::context::enum_devices()
.into_iter()
.filter_map(|node| Device::with_path(node.path()).ok().map(|device| (node, device)))
.filter(|(_, device)| device.format().is_ok())
.map(|(node, _)| node)
.collect()
}
impl Camera {
fn from_node(node: &v4l::context::Node) -> Self {
let device = v4l::Device::with_path(node.path()).unwrap();
device.set_format(&get_next_best_format(&device)).unwrap();
Self {
device: RwLock::new(device),
device_name: name_or_path(node),
stream: RwLock::new(None),
}
}
}
impl InnerCamera for Camera {
type Frame = Frame;
fn new_default_device() -> Self {
let node = enum_devices().into_iter().next().unwrap();
Self::from_node(&node)
}
fn start(&self) {
if self.stream.read().unwrap().is_none() {
let device = self.device.write().unwrap();
let stream =
v4l::io::mmap::Stream::with_buffers(&device, v4l::buffer::Type::VideoCapture, 4)
.expect("Failed to create buffer stream");
let _ = self.stream.write().unwrap().insert(stream);
}
}
fn stop(&self) {
let _ = self.stream.write().unwrap().take();
}
fn wait_for_frame(&self) -> Option<Frame> {
let format = self.device.read().unwrap().format().unwrap();
let size = (format.width, format.height);
if let Ok((buf, _meta)) = self.stream.write().unwrap().as_mut().unwrap().next() {
let data = match &format.fourcc.repr {
b"RGB3" => buf.to_vec(),
b"YUYV" => yuyv_to_rgb32(buf, size.0, size.1),
b"MJPG" => todo!("NJPG not implemented"),
_ => panic!("invalid buffer pixelformat"),
};
Some(Frame { data, size })
} else {
None
}
}
fn change_device(&mut self) {
let devices = enum_devices();
if let Some(pos) = devices.iter().position(|n| name_or_path(n) == self.device_name) {
let new_pos = (pos + 1) % devices.len();
if new_pos != pos {
*self = Self::from_node(&devices[new_pos]);
self.start();
}
} else if !devices.is_empty() {
*self = Self::from_node(&devices[0]);
self.start();
} else {
self.stop();
}
}
}
impl std::fmt::Debug for Camera {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Camera").field("device", &self.device_name).finish()
}
}
pub struct Frame {
data: Vec<u8>,
size: (u32, u32),
}
impl Frame {
pub fn data(&self) -> FrameData {
FrameData { data: self.data.clone(), _phantom: PhantomData }
}
pub fn size_u32(&self) -> (u32, u32) {
self.size
}
}
impl std::fmt::Debug for Frame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Frame").field("data", &self.data.len()).finish()
}
}
#[derive(Debug)]
pub struct FrameData<'a> {
data: Vec<u8>,
_phantom: PhantomData<&'a ()>,
}
impl<'a> FrameData<'a> {
pub fn data_u8(&self) -> &[u8] {
&self.data
}
pub fn data_u32(&self) -> &[u32] {
unsafe { self.data.align_to().1 }
}
}
fn yuyv_to_rgb32(buf: &[u8], w: u32, h: u32) -> Vec<u8> {
use ffimage::color::Rgb;
use ffimage::packed::{ImageBuffer, ImageView};
use ffimage::traits::Convert;
use ffimage_yuv::{yuv::Yuv, yuyv::Yuyv};
let yuv422 = ImageView::<Yuyv<u8>>::from_buf(buf, w, h).unwrap();
let mut yuv444 = ImageBuffer::<Yuv<u8>>::new(w, h, 0u8);
let mut rgb = ImageBuffer::<Rgb<u8>>::new(w, h, 0u8);
let mut rgba = ImageBuffer::<Bgra<u8>>::new(w, h, 0u8);
yuv422.convert(&mut yuv444);
yuv444.convert(&mut rgb);
rgb.convert(&mut rgba);
rgba.into_buf()
}