use libc as c; use v4l2_sys as v4l2; use std::mem::{MaybeUninit, zeroed, replace}; use std::ptr::null_mut; use std::slice::from_raw_parts; use std::os::fd::RawFd; use std::path::Path; use std::io::Error as IoError; use std::fmt::{Display, Debug, Formatter, Error as FmtError}; use std::error::Error as ErrorTrait; use std::{str, iter, array}; use std::time::Duration; use chrono::{DateTime, Local}; macro_rules! define_flagset{ ($tname:ident: $ctype:ty; $($name:ident = $mask:expr),+) => { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct $tname{ $(pub $name: bool),+ } impl $tname{ pub fn zero() -> Self{ Self{ $($name: false),+ } } } impl From<$ctype> for $tname{ fn from(src: $ctype) -> Self{ let mut ret = Self::zero(); $( if src & $mask != 0{ ret.$name = true; } )+ ret } } impl From<$tname> for $ctype{ fn from(src: $tname) -> Self{ let mut ret = 0; $( if src.$name{ ret |= $mask; } )+ ret } } }; } macro_rules! define_cenum{ ($tname:ident: $ctype:ty; $($name:ident = $mask:expr),+) => { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr($ctype)] pub enum $tname{ $($name = $mask),+ } impl From<$ctype> for $tname{ fn from(src: $ctype) -> Self{ $(if src == $mask{ return Self::$name; })+ panic!("{} is invalid value for {}", src, stringify!($tname)); } } impl From<$tname> for $ctype{ fn from(src: $tname) -> Self{ src as Self } } }; } define_flagset!{ BufFlags: u32; mapped = v4l2::V4L2_BUF_FLAG_MAPPED, queued = v4l2::V4L2_BUF_FLAG_QUEUED, done = v4l2::V4L2_BUF_FLAG_DONE, error = v4l2::V4L2_BUF_FLAG_ERROR, keyframe = v4l2::V4L2_BUF_FLAG_KEYFRAME, pframe = v4l2::V4L2_BUF_FLAG_PFRAME, bframe = v4l2::V4L2_BUF_FLAG_BFRAME, timecode = v4l2::V4L2_BUF_FLAG_TIMECODE, prepared = v4l2::V4L2_BUF_FLAG_PREPARED, no_cache_invalidate = v4l2::V4L2_BUF_FLAG_NO_CACHE_INVALIDATE, no_cache_clean = v4l2::V4L2_BUF_FLAG_NO_CACHE_CLEAN, last = v4l2::V4L2_BUF_FLAG_LAST } define_cenum!{ Field: u32; Any = v4l2::v4l2_field_V4L2_FIELD_ANY, None = v4l2::v4l2_field_V4L2_FIELD_NONE, Top = v4l2::v4l2_field_V4L2_FIELD_TOP, Bottom = v4l2::v4l2_field_V4L2_FIELD_BOTTOM, Interlaced = v4l2::v4l2_field_V4L2_FIELD_INTERLACED, SeqTb = v4l2::v4l2_field_V4L2_FIELD_SEQ_TB, SeqBt = v4l2::v4l2_field_V4L2_FIELD_SEQ_BT, Alternate = v4l2::v4l2_field_V4L2_FIELD_ALTERNATE, InterlacedTb = v4l2::v4l2_field_V4L2_FIELD_INTERLACED_TB, InterlacedBt = v4l2::v4l2_field_V4L2_FIELD_INTERLACED_BT } #[derive(Copy, Clone, PartialEq, Eq)] pub struct ImageFormat([u8; 4]); impl ImageFormat{ pub fn slice(&self) -> &[u8]{ let p = self.0.iter().rev() .position(|c| *c != 0x20) .unwrap_or(4); &self.0[..4-p] } pub fn as_str(&self) -> &str{ // XXX: it is invalid when self.be() str::from_utf8(self.slice()).unwrap() } pub fn name(&self) -> String{ let s = self.slice(); let v = iter::once(s[0] & 0x7f).chain(s[1..].iter().copied()).collect(); String::from_utf8(v).unwrap() } pub fn be(&self) -> bool{ self.0[0] & 0x80 != 0 } } impl From<&str> for ImageFormat{ fn from(mut src: &str) -> Self{ let be = if src.ends_with(":be"){ let l = src.len(); src = &src[..l-3]; true }else{ false }; assert!(src.chars().skip(4).next().is_none()); let mut a: [u8; 4] = array::from_fn( |n| src.chars().skip(n).next().unwrap_or(' ') as u8); if be{ a[0] |= 0x80; } Self(a) } } impl From for ImageFormat{ fn from(src: u32) -> Self{ Self(src.to_le_bytes()) } } impl From for u32{ fn from(src: ImageFormat) -> Self{ Self::from_le_bytes(src.0) } } impl Debug for ImageFormat{ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>{ write!(f, "ImageFormat({}{})", self.name(), if self.be(){ ":be" }else{ "" }) } } impl PartialEq for ImageFormat{ fn eq(&self, other: &str) -> bool{ Into::::into(other) == *self } } impl PartialEq for str{ fn eq(&self, other: &ImageFormat) -> bool{ Into::::into(self) == *other } } #[derive(Copy, Clone)] pub struct BufAttrs{ pub flags: BufFlags, pub field: Field, pub timestamp: c::timeval, pub sequence: u32, } impl BufAttrs{ // XXX: It is correct only when V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC pub fn get_datetime(&self) -> Option>{ let now_monotonic = unsafe{ let mut buf = MaybeUninit::uninit(); assert!(c::clock_gettime(c::CLOCK_MONOTONIC, buf.as_mut_ptr()) == 0); buf.assume_init() }; let now_wallclock = Local::now(); let mut timestamp = c::timespec{ tv_sec: self.timestamp.tv_sec as _, tv_nsec: self.timestamp.tv_usec as _, }; timestamp.tv_nsec *= 1000; let (mut so, mut nso, future); if now_monotonic.tv_sec < timestamp.tv_sec || now_monotonic.tv_sec == timestamp.tv_sec && now_monotonic.tv_nsec < timestamp.tv_nsec { future = true; so = timestamp.tv_sec - now_monotonic.tv_sec; nso = timestamp.tv_nsec - now_monotonic.tv_nsec; }else{ future = false; so = now_monotonic.tv_sec - timestamp.tv_sec; nso = now_monotonic.tv_nsec - timestamp.tv_nsec; } if nso < 0{ nso += 1000_000_000; so -= 1; } assert!(0 <= nso && nso < 1000_000_000); let to = Duration::new(so as u64, nso as u32); if future{ Some(now_wallclock + to) }else{ Some(now_wallclock - to) } } } impl Debug for BufAttrs{ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>{ #[derive(Debug)] #[allow(non_camel_case_types,unused)] struct timeval{ tv_sec: c::time_t, tv_usec: c::suseconds_t, } f.debug_struct("BufAttrs") .field("flags", &self.flags) .field("field", &self.field) .field("timestamp", &timeval{ tv_sec: self.timestamp.tv_sec, tv_usec: self.timestamp.tv_usec, }) .field("sequence", &self.sequence) .finish() } } #[derive(Copy, Clone, Debug)] struct MmappedBuffer{ ptr: *mut u8, length: u32, } #[derive(Clone, Debug)] enum IoMethod{ Uninit, Mmap(/* unsafe to construct */ Vec), } #[derive(Debug)] pub struct Device{ fd: RawFd, cap: v4l2::v4l2_capability, io_capture: IoMethod, } // Safe to Send while *mut u8 in IoMethod is never be copied. unsafe impl Send for Device{} macro_rules! /* unsafe */ mk_ioctl_getter{ ($name:ident, $type:ty, $op:expr; $($qn:ident: $qt:ty),*) => { fn $name(&self, $($qn: $qt),*) -> Result<$type, IoError>{ let mut val = MaybeUninit::<$type>::uninit(); $(unsafe{ (&raw mut (*val.as_mut_ptr()).$qn).write($qn) };)* if unsafe{ c::ioctl(self.fd, $op, val.as_mut_ptr()) } < 0{ Err(IoError::last_os_error()) }else{ Ok(unsafe{ val.assume_init() }) } } }; } macro_rules! /* unsafe */ mk_ioctl_setter{ ($name:ident, $type:ty, $op:expr) => { fn $name(&self, mut val: $type) -> Result<$type, IoError>{ if unsafe{ c::ioctl(self.fd, $op, &mut val as *mut $type) } < 0{ Err(IoError::last_os_error()) }else{ Ok(val) } } }; } impl Device{ pub unsafe fn from_rawfd(fd: RawFd) -> Result{ let mut cap = MaybeUninit::::uninit(); if unsafe{c::ioctl(fd, v4l2::VIDIOC_QUERYCAP, cap.as_mut_ptr())} < 0{ return Err(IoError::last_os_error()); } let cap = cap.assume_init(); Ok(Device{ fd, cap, io_capture: IoMethod::Uninit, }) } pub fn open(path: impl AsRef) -> Result{ let pathstr_bytes = path.as_ref().as_os_str().as_encoded_bytes(); let p_bytes: Vec = pathstr_bytes.iter() .copied() .chain(iter::once(0)) .collect(); let fd = unsafe{c::open( p_bytes.as_ptr() as *const c::c_char, c::O_RDWR)}; if fd < 0{ return Err(IoError::last_os_error()); } unsafe{Device::from_rawfd(fd)} } mk_ioctl_getter!(ioctl_get_format, v4l2::v4l2_format, v4l2::VIDIOC_G_FMT; type_: u32); mk_ioctl_setter!(ioctl_set_format, v4l2::v4l2_format, v4l2::VIDIOC_S_FMT); //mk_ioctl_getter!(ioctl_get_streamparm, // v4l2::v4l2_streamparm, v4l2::VIDIOC_G_PARM; // type_: u32); //mk_ioctl_setter!(ioctl_set_streamparm, // v4l2::v4l2_streamparm, v4l2::VIDIOC_S_PARM); mk_ioctl_setter!(ioctl_req_buffers, v4l2::v4l2_requestbuffers, v4l2::VIDIOC_REQBUFS); mk_ioctl_setter!(ioctl_query_buffer, v4l2::v4l2_buffer, v4l2::VIDIOC_QUERYBUF); mk_ioctl_setter!(ioctl_queue_buffer, v4l2::v4l2_buffer, v4l2::VIDIOC_QBUF); mk_ioctl_getter!(ioctl_dequeue_buffer, v4l2::v4l2_buffer, v4l2::VIDIOC_DQBUF; type_: u32); unsafe fn unmap_bufs(bufs: Vec){ for buf in bufs{ if unsafe{ c::munmap( buf.ptr as *mut c::c_void, buf.length as usize) } < 0{ panic!("munmap: {:?}", IoError::last_os_error()); } } } fn uninit_io(&self, io: IoMethod, ty: u32) -> Result<(), IoError>{ match io{ IoMethod::Uninit => (), IoMethod::Mmap(bufs) => { unsafe{ Self::unmap_bufs(bufs) }; if unsafe{ c::ioctl(self.fd, v4l2::VIDIOC_STREAMOFF, &raw const ty as *const c::c_int) } < 0{ return Err(IoError::last_os_error()); } }, }; Ok(()) } fn init_mmap_input(&self, ty: u32) -> Result{ let mut req = unsafe{ zeroed::() }; req.count = 4; req.type_ = ty; req.memory = v4l2::v4l2_memory_V4L2_MEMORY_MMAP; let count = self.ioctl_req_buffers(req)?.count; if count == 0{ return Ok(IoMethod::Uninit); } let mut bufrs = Vec::with_capacity(count as usize); for i in 0..count{ let mut buf = unsafe{ zeroed::() }; buf.type_ = ty; buf.memory = v4l2::v4l2_memory_V4L2_MEMORY_MMAP; buf.index = i; let buf = self.ioctl_query_buffer(buf)?; bufrs.push(buf); self.ioctl_queue_buffer(buf)?; } if unsafe{ c::ioctl(self.fd, v4l2::VIDIOC_STREAMON, &raw const ty as *const c::c_int) } < 0{ return Err(IoError::last_os_error()); } let mut bufs = Vec::with_capacity(count as usize); for bufr in bufrs{ let ptr = unsafe{ c::mmap(null_mut(), bufr.length as c::size_t, c::PROT_READ | c::PROT_WRITE, c::MAP_SHARED, self.fd, bufr.m.offset as c::off_t) }; if ptr == c::MAP_FAILED{ unsafe{ Self::unmap_bufs(bufs) }; panic!("mmap: {:?}", IoError::last_os_error()); } bufs.push(MmappedBuffer{ ptr: ptr as *mut u8, length: bufr.length, }); } Ok(IoMethod::Mmap(bufs)) } fn dequeue(&self, io: &IoMethod, ty: u32, cb: impl FnOnce(&[u8], BufAttrs)->R ) -> Result{ let bufr = self.ioctl_dequeue_buffer(ty)?; let attrs = BufAttrs{ flags: bufr.flags.into(), field: bufr.field.into(), timestamp: c::timeval{ tv_sec: bufr.timestamp.tv_sec, tv_usec: bufr.timestamp.tv_usec, }, sequence: bufr.sequence, }; let ret; match io{ IoMethod::Uninit => panic!(), IoMethod::Mmap(ref mmb) => { let buf = mmb[bufr.index as usize]; assert!(bufr.bytesused <= buf.length); let slice = unsafe{ from_raw_parts( buf.ptr as *const u8, bufr.bytesused as usize) }; ret = cb(slice, attrs); }, } self.ioctl_queue_buffer(bufr)?; Ok(ret) } pub fn captstream_builder(self) -> Result{ CaptStreamBuilder::new(self) } } impl Drop for Device{ fn drop(&mut self){ let io_capture = replace(&mut self.io_capture, IoMethod::Uninit); let _ = self.uninit_io(io_capture, v4l2::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE); // skip close error check unsafe{ c::close(self.fd) }; } } #[derive(Debug)] pub enum CaptStreamBuilderNewError{ NoVideoCaptureCapability, NoStreamingCapability, IoError(IoError), } impl Display for CaptStreamBuilderNewError{ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>{ Debug::fmt(self, f) // TODO } } impl ErrorTrait for CaptStreamBuilderNewError{} impl From for CaptStreamBuilderNewError{ fn from(src: IoError) -> Self{ Self::IoError(src) } } pub struct CaptStreamBuilder{ v4l2: Device, pix_format: v4l2::v4l2_pix_format, dirty: bool, } impl CaptStreamBuilder{ pub fn new(v4l2: Device) -> Result{ if v4l2.cap.capabilities & v4l2::V4L2_CAP_VIDEO_CAPTURE == 0{ return Err(CaptStreamBuilderNewError::NoVideoCaptureCapability); } if v4l2.cap.capabilities & v4l2::V4L2_CAP_STREAMING == 0{ return Err(CaptStreamBuilderNewError::NoStreamingCapability); } let fmt = v4l2.ioctl_get_format( v4l2::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE)?; assert!(fmt.type_ == v4l2::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE); Ok(Self{ v4l2, pix_format: unsafe{ fmt.fmt.pix }, dirty: false, }) } pub fn build(self) -> Result{ let mut v4l2 = self.v4l2; let mut pix_format = self.pix_format; if self.dirty{ let mut fmt: v4l2::v4l2_format = unsafe{ zeroed() }; fmt.type_ = v4l2::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix = pix_format; let fmt = v4l2.ioctl_set_format(fmt)?; assert!(fmt.type_ == v4l2::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE); pix_format = unsafe{ fmt.fmt.pix }; } v4l2.io_capture = v4l2.init_mmap_input( v4l2::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE)?; Ok(CaptStream{ v4l2, pix_format }) } pub fn set_width(mut self, v: usize) -> Self{ self.pix_format.width = v as u32; self.dirty = true; self } pub fn set_height(mut self, v: usize) -> Self{ self.pix_format.height = v as u32; self.dirty = true; self } pub fn set_pixelformat(mut self, v: ImageFormat) -> Self{ self.pix_format.pixelformat = v.into(); self.pix_format.bytesperline = 0; self.dirty = true; self } pub fn set_field(mut self, v: Field) -> Self{ self.pix_format.field = v.into(); self.dirty = true; self } pub fn set_bytesperline(mut self, v: usize) -> Self{ self.pix_format.bytesperline = v as u32; self.dirty = true; self } pub fn set_sizeimage(mut self, v: usize) -> Self{ self.pix_format.sizeimage = v as u32; self.dirty = true; self } } macro_rules! impl_pix_format_reader{ (+++ $n:ident: usize) => { pub fn $n(&self) -> usize{ self.format().$n as usize } }; (+++ $n:ident: $t:ty) => { pub fn $n(&self) -> $t{ self.format().$n.into() } }; ($t:ty) => { impl $t{ pub fn format(&self) -> v4l2::v4l2_pix_format{ self.pix_format } impl_pix_format_reader!(+++ width: usize); impl_pix_format_reader!(+++ height: usize); impl_pix_format_reader!(+++ pixelformat: ImageFormat); impl_pix_format_reader!(+++ field: Field); impl_pix_format_reader!(+++ bytesperline: usize); impl_pix_format_reader!(+++ sizeimage: usize); //impl_pix_format_reader!(+++ colorspace: ColorSpace); } }; } impl_pix_format_reader!(CaptStreamBuilder); pub struct CaptStream{ v4l2: Device, pix_format: v4l2::v4l2_pix_format, } impl CaptStream{ pub fn next(&self, cb: impl FnOnce(&[u8], BufAttrs)->R) -> Result{ self.v4l2.dequeue(&self.v4l2.io_capture, v4l2::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE, cb) } } impl_pix_format_reader!(CaptStream);