use std::fs::File; use std::io::{self, Write}; use std::path::Path; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; use std::{cell::RefCell, collections::VecDeque, time::Instant}; use v4l2r::{ device::{ poller::PollError, queue::{ direction::Capture, dqbuf::DqBuffer, generic::{GenericBufferHandles, GenericQBuffer, GenericSupportedMemoryType}, handles_provider::MmapProvider, OutputQueueable, }, }, encoder::*, memory::{MmapHandle, UserPtrHandle}, Format, }; use v4l2r_utils::framegen::FrameGenerator; use anyhow::ensure; use clap::{App, Arg}; fn main() { env_logger::init(); let matches = App::new("V4L2 stateful encoder") .arg( Arg::with_name("num_frames") .long("stop_after") .takes_value(true) .help("Stop after encoding a given number of buffers"), ) .arg( Arg::with_name("device") .required(true) .help("Path to the vicodec device file"), ) .arg( Arg::with_name("frame_size") .long("frame_size") .required(false) .takes_value(true) .default_value("640x480") .help("Size of the frames to encode (e.g. \"640x480\")"), ) .arg( Arg::with_name("output_file") .long("save") .required(false) .takes_value(true) .help("Save the encoded stream to a file"), ) .arg( Arg::with_name("output_mem") .long("output_mem") .required(false) .takes_value(true) .default_value("mmap") .help("Type of memory to use for the OUTPUT queue (mmap, user or dmabuf)"), ) .get_matches(); let device_path = matches.value_of("device").unwrap_or("/dev/video0"); let mut stop_after = match clap::value_t!(matches.value_of("num_frames"), usize) { Ok(v) => Some(v), Err(e) if e.kind == clap::ErrorKind::ArgumentNotFound => None, Err(e) => panic!("Invalid value for stop_after: {}", e), }; let frame_size = matches .value_of("frame_size") .map(|s| { const ERROR_MSG: &str = "Invalid parameter for frame_size"; let split: Vec<&str> = s.split('x').collect(); if split.len() != 2 { panic!("{}", ERROR_MSG); } let width: usize = split[0].parse().expect(ERROR_MSG); let height: usize = split[1].parse().expect(ERROR_MSG); (width, height) }) .unwrap(); let mut output_file = matches .value_of("output_file") .map(|s| File::create(s).expect("Invalid output file specified.")); let output_mem = match matches.value_of("output_mem") { Some("mmap") => GenericSupportedMemoryType::Mmap, Some("user") => GenericSupportedMemoryType::UserPtr, Some("dmabuf") => GenericSupportedMemoryType::DmaBuf, _ => panic!("Invalid value for output_mem"), }; let lets_quit = Arc::new(AtomicBool::new(false)); // Setup the Ctrl+c handler. { let lets_quit_handler = lets_quit.clone(); ctrlc::set_handler(move || { lets_quit_handler.store(true, Ordering::SeqCst); }) .expect("Failed to set Ctrl-C handler."); } let encoder = Encoder::open(Path::new(&device_path)) .expect("Failed to open device") .set_capture_format(|f| { let format: Format = f.set_pixelformat(b"FWHT").apply()?; ensure!( format.pixelformat == b"FWHT".into(), "FWHT format not supported" ); Ok(()) }) .expect("Failed to set capture format") .set_output_format(|f| { let format: Format = f .set_pixelformat(b"RGB3") .set_size(frame_size.0, frame_size.1) .apply()?; ensure!( format.pixelformat == b"RGB3".into(), "RGB3 format not supported" ); ensure!( format.width as usize == frame_size.0 && format.height as usize == frame_size.1, "Output frame resolution not supported" ); Ok(()) }) .expect("Failed to set output format"); let output_format = encoder .get_output_format() .expect("Failed to get output format"); println!("Adjusted output format: {:?}", output_format); let capture_format = encoder .get_capture_format() .expect("Failed to get capture format"); println!("Adjusted capture format: {:?}", capture_format); println!( "Configured encoder for {}x{} ({} bytes per line)", output_format.width, output_format.height, output_format.plane_fmt[0].bytesperline ); let mut frame_gen = FrameGenerator::new( output_format.width as usize, output_format.height as usize, output_format.plane_fmt[0].bytesperline as usize, ) .expect("Failed to create frame generator"); const NUM_BUFFERS: usize = 2; let free_buffers: Option> = match output_mem { GenericSupportedMemoryType::Mmap | GenericSupportedMemoryType::DmaBuf => None, GenericSupportedMemoryType::UserPtr => Some( std::iter::repeat(vec![0u8; output_format.plane_fmt[0].sizeimage as usize]) .take(NUM_BUFFERS) .collect(), ), }; let free_buffers = RefCell::new(free_buffers); let dmabufs: Option> = match output_mem { GenericSupportedMemoryType::Mmap | GenericSupportedMemoryType::UserPtr => None, GenericSupportedMemoryType::DmaBuf => Some( v4l2r_utils::dmabuf_exporter::export_dmabufs(&output_format, NUM_BUFFERS) .unwrap() .into_iter() .collect(), ), }; let dmabufs = RefCell::new(dmabufs); let input_done_cb = |buffer: CompletedOutputBuffer| { let handles = match buffer { CompletedOutputBuffer::Dequeued(mut buf) => buf.take_handles().unwrap(), CompletedOutputBuffer::Canceled(buf) => buf.plane_handles, }; match handles { // We have nothing to do for MMAP buffers. GenericBufferHandles::Mmap(_) => {} // For user-allocated memory, return the buffer to the free list. GenericBufferHandles::User(mut u) => { free_buffers .borrow_mut() .as_mut() .unwrap() .push_back(u.remove(0).0); } GenericBufferHandles::DmaBuf(d) => { dmabufs.borrow_mut().as_mut().unwrap().push_back(d); } }; }; let mut total_size = 0usize; let start_time = Instant::now(); let poll_count_reader = Arc::new(AtomicUsize::new(0)); let poll_count_writer = Arc::clone(&poll_count_reader); let mut frame_counter = 0usize; let output_ready_cb = move |cap_dqbuf: DqBuffer>| { let bytes_used = *cap_dqbuf.data.get_first_plane().bytesused as usize; // Ignore zero-sized buffers. if bytes_used == 0 { return; } total_size = total_size.wrapping_add(bytes_used); let elapsed = start_time.elapsed(); frame_counter += 1; let fps = frame_counter as f32 / elapsed.as_millis() as f32 * 1000.0; let ppf = poll_count_reader.load(Ordering::SeqCst) as f32 / frame_counter as f32; print!( "\rEncoded buffer {:#5}, index: {:#2}), bytes used:{:#6} total encoded size:{:#8} fps: {:#5.2} ppf: {:#4.2}" , cap_dqbuf.data.sequence(), cap_dqbuf.data.index(), bytes_used, total_size, fps, ppf, ); io::stdout().flush().unwrap(); if let Some(ref mut output) = output_file { let mapping = cap_dqbuf .get_plane_mapping(0) .expect("Failed to map capture buffer"); output .write_all(mapping.as_ref()) .expect("Error while writing output data"); } }; let mut encoder = encoder .allocate_output_buffers_generic::(output_mem, NUM_BUFFERS) .expect("Failed to allocate OUTPUT buffers") .allocate_capture_buffers(NUM_BUFFERS, MmapProvider::new(&capture_format)) .expect("Failed to allocate CAPTURE buffers") .set_poll_counter(poll_count_writer) .start(input_done_cb, output_ready_cb) .expect("Failed to start encoder"); while !lets_quit.load(Ordering::SeqCst) { if let Some(max_cpt) = &mut stop_after { if *max_cpt == 0 { break; } *max_cpt -= 1; } let v4l2_buffer = match encoder.get_buffer() { Ok(buffer) => buffer, // If we got interrupted while waiting for a buffer, just exit normally. Err(GetBufferError::PollError(PollError::EPollWait(nix::errno::Errno::EINTR))) => break, Err(e) => panic!("{}", e), }; let bytes_used = frame_gen.frame_size(); match v4l2_buffer { GenericQBuffer::Mmap(buf) => { let mut mapping = buf .get_plane_mapping(0) .expect("Failed to get MMAP mapping"); frame_gen .next_frame(&mut mapping) .expect("Failed to generate frame"); buf.queue(&[bytes_used]) .expect("Failed to queue input frame"); } GenericQBuffer::User(buf) => { let mut buffer = free_buffers .borrow_mut() .as_mut() .unwrap() .pop_front() .expect("No backing buffer to bind"); frame_gen .next_frame(&mut buffer) .expect("Failed to generate frame"); buf.queue_with_handles( GenericBufferHandles::from(vec![UserPtrHandle::from(buffer)]), &[bytes_used], ) .expect("Failed to queue input frame"); } GenericQBuffer::DmaBuf(buf) => { let buffer = dmabufs .borrow_mut() .as_mut() .unwrap() .pop_front() .expect("No backing dmabuf to bind"); let mut mapping = buffer[0].map().unwrap(); frame_gen .next_frame(&mut mapping) .expect("Failed to generate frame"); buf.queue_with_handles(GenericBufferHandles::from(buffer), &[bytes_used]) .expect("Failed to queue input frame"); } } } encoder.stop().unwrap(); // Insert new line since we were overwriting the same one println!(); if output_mem == GenericSupportedMemoryType::UserPtr { // All the OUTPUT buffers should have been returned assert_eq!(free_buffers.borrow().as_ref().unwrap().len(), NUM_BUFFERS); } }