use std::ffi::{CStr, CString}; use std::fmt; use std::fs; use std::io::Write; use std::mem; use std::ptr; use std::slice; use log::*; use libv4l_sys as v4l; macro_rules! errno { () => { unsafe { *libc::__errno_location() } }; } #[derive(Debug, Clone)] struct Framesize { pub width: u32, pub height: u32, } impl fmt::Display for Framesize { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}x{}", self.width, self.height) } } #[derive(Debug)] struct Buffer { start: *mut libc::c_void, length: libc::size_t, } fn strerror() -> String { let errno = errno!(); unsafe { CStr::from_ptr(libc::strerror(errno)) } .to_string_lossy() .into() } fn rioctl(fd: libc::c_int, request: libc::c_ulong, arg: *mut libc::c_void) -> libc::c_int { let mut r; loop { r = unsafe { v4l::v4l2_ioctl(fd, request, arg) }; if r == -1 && ((errno!() == libc::EINTR) || (errno!() == libc::EAGAIN)) { continue; } else { break; } } r } fn xioctl(fd: libc::c_int, request: libc::c_ulong, arg: *mut libc::c_void) { if rioctl(fd, request, arg) == -1 { error!("error {}, {}", errno!(), strerror()); panic!() } } fn main() { println!("v4l2grab"); env_logger::init(); let dev_name = CString::new("/dev/video0").unwrap(); let fd = unsafe { let fd = v4l::v4l2_open(dev_name.as_ptr(), libc::O_RDWR | libc::O_NONBLOCK, 0); if fd == -1 { error!("open device error: {}: {}", fd, strerror()); panic!() } fd }; let fmt = unsafe { let mut fmt: v4l::v4l2_format = mem::zeroed(); fmt.type_ = v4l::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 320; fmt.fmt.pix.height = 240; fmt.fmt.pix.pixelformat = v4l::pixel_format::V4L2_PIX_FMT_RGB24; fmt.fmt.pix.field = v4l::v4l2_field_V4L2_FIELD_INTERLACED; xioctl( fd, v4l::codes::VIDIOC_S_FMT, &mut fmt as *mut _ as *mut libc::c_void, ); if fmt.fmt.pix.pixelformat != v4l::pixel_format::V4L2_PIX_FMT_RGB24 { println!("Libv4l didn't accept RGB24 format. Can't proceed."); panic!() } if (fmt.fmt.pix.width != 640) || (fmt.fmt.pix.height != 480) { println!( "Warning: driver is sending image at {}x{}", fmt.fmt.pix.width, fmt.fmt.pix.height ); } fmt }; let mut req = unsafe { let mut req: v4l::v4l2_requestbuffers = mem::zeroed(); req.count = 2; req.type_ = v4l::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = v4l::v4l2_memory_V4L2_MEMORY_MMAP; req }; xioctl( fd, v4l::codes::VIDIOC_REQBUFS, &mut req as *mut _ as *mut libc::c_void, ); let mut buffers = Vec::new(); for n_buffers in 0..req.count { let mut buf = unsafe { let mut buf: v4l::v4l2_buffer = mem::zeroed(); buf.type_ = v4l::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = v4l::v4l2_memory_V4L2_MEMORY_MMAP; buf.index = n_buffers; buf }; xioctl( fd, v4l::codes::VIDIOC_QUERYBUF, &mut buf as *mut _ as *mut libc::c_void, ); let buffer = unsafe { Buffer { start: v4l::v4l2_mmap( ptr::null_mut(), buf.length.try_into().unwrap(), libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED, fd, buf.m.offset as i64, ), length: buf.length as libc::size_t, } }; if libc::MAP_FAILED == buffer.start { error!("mmap"); panic!() } buffers.push(buffer); } for i in 0..buffers.len() { let mut buf = unsafe { let mut buf: v4l::v4l2_buffer = mem::zeroed(); buf.type_ = v4l::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = v4l::v4l2_memory_V4L2_MEMORY_MMAP; buf.index = i as u32; buf }; xioctl( fd, v4l::codes::VIDIOC_QBUF, &mut buf as *mut _ as *mut libc::c_void, ); } let mut type_ = v4l::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; xioctl( fd, v4l::codes::VIDIOC_STREAMON, &mut type_ as *mut _ as *mut libc::c_void, ); for i in 0..20 { debug!("0..20: {}", i); unsafe { let mut fds: libc::fd_set = mem::zeroed(); loop { libc::FD_ZERO(&mut fds); libc::FD_SET(fd, &mut fds); let mut tv = libc::timeval { tv_sec: 2, tv_usec: 0, }; let r = libc::select(fd + 1, &mut fds, ptr::null_mut(), ptr::null_mut(), &mut tv); if !(r == -1 && (errno!() == libc::EINTR)) { if cfg!(target_os = "linux") { debug!("time left: {}.{:06}", tv.tv_sec, tv.tv_usec); } break; } if r == -1 { error!("select error {}, {}", errno!(), strerror()); panic!() } } } let mut buf = unsafe { let mut buf: v4l::v4l2_buffer = mem::zeroed(); buf.type_ = v4l::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = v4l::v4l2_memory_V4L2_MEMORY_MMAP; buf }; debug!("VIDIOC_DQBUF"); xioctl( fd, v4l::codes::VIDIOC_DQBUF, &mut buf as *mut _ as *mut libc::c_void, ); { let mut fout = fs::File::create(&format!("out{:03}.ppm", i)).unwrap(); write!( fout, "P6\n{} {} 255\n", unsafe { fmt.fmt.pix.width }, unsafe { fmt.fmt.pix.height } ) .unwrap(); unsafe { fout.write_all(slice::from_raw_parts( buffers[buf.index as usize].start as *const u8, buf.bytesused as usize, )) .unwrap(); } } debug!("VIDIOC_QBUF"); xioctl( fd, v4l::codes::VIDIOC_QBUF, &mut buf as *mut _ as *mut libc::c_void, ); } let mut type_ = v4l::v4l2_buf_type_V4L2_BUF_TYPE_VIDEO_CAPTURE; debug!("VIDIOC_STREAMOFF"); xioctl( fd, v4l::codes::VIDIOC_STREAMOFF, &mut type_ as *mut _ as *mut libc::c_void, ); unsafe { for buf in buffers { v4l::v4l2_munmap(buf.start, buf.length); } v4l::v4l2_close(fd); } }