blob: 6f463593d274480bb1a7f112586afff5bbb9017c [file] [log] [blame]
use std::{
fs::File,
io,
io::BufReader,
io::Write,
path::Path,
sync::atomic::AtomicBool,
sync::atomic::{AtomicUsize, Ordering},
sync::Arc,
};
use anyhow::ensure;
use v4l2::{
decoder::stateful::Decoder,
device::queue::{
direction::{Capture, Output},
dqbuf::DQBuffer,
},
memory::{DMABufHandle, DMABufferHandles},
Format,
};
use v4l2::{decoder::stateful::GetBufferError, memory::dmabuf_exporter, QueueType};
use v4l2::{
decoder::{format::fwht::FwhtFrameParser, stateful::SetCaptureFormatRet},
device::queue::{qbuf::OutputQueueable, FormatBuilder},
memory::{
pooled_provider::{PooledHandles, PooledHandlesProvider},
MemoryType, UserPtrHandle,
},
};
use clap::{App, Arg};
fn main() {
let matches = App::new("FWHT decoder")
.arg(
Arg::with_name("stream")
.required(true)
.help("Path to the FWHT stream to decode"),
)
.arg(
Arg::with_name("device")
.required(true)
.help("Path to the vicodec device file"),
)
.arg(
Arg::with_name("output_file")
.long("save")
.required(false)
.takes_value(true)
.help("Save the decoded RGB frames to a file"),
)
.get_matches();
let stream_path = matches
.value_of("stream")
.expect("Stream argument not specified");
let device_path = matches
.value_of("device")
.expect("Device argument not specified");
let stream = BufReader::new(File::open(stream_path).expect("Compressed stream not found"));
let mut output_file: Option<File> = if let Some(path) = matches.value_of("output_file") {
Some(File::create(path).expect("Invalid output file specified."))
} else {
None
};
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.");
}
const NUM_OUTPUT_BUFFERS: usize = 4;
let poll_count_reader = Arc::new(AtomicUsize::new(0));
let poll_count_writer = Arc::clone(&poll_count_reader);
let start_time = std::time::Instant::now();
let mut output_buffer_size = 0usize;
let output_ready_cb =
move |mut cap_dqbuf: DQBuffer<Capture, PooledHandles<DMABufferHandles<File>>>| {
let bytes_used = cap_dqbuf.data.get_first_plane().bytesused() as usize;
let elapsed = start_time.elapsed();
let frame_nb = cap_dqbuf.data.sequence() + 1;
let fps = frame_nb as f32 / elapsed.as_millis() as f32 * 1000.0;
let ppf = poll_count_reader.load(Ordering::SeqCst) as f32 / frame_nb as f32;
print!(
"\rDecoded buffer {:#5}, index: {:#2}), bytes used:{:#6} fps: {:#5.2} ppf: {:#4.2}",
cap_dqbuf.data.sequence(),
cap_dqbuf.data.index(),
bytes_used,
fps,
ppf,
);
io::stdout().flush().unwrap();
if let Some(ref mut output) = output_file {
let pooled_handles = cap_dqbuf.take_handles().unwrap();
let handles = pooled_handles.handles();
let mapping = handles[0].map().expect("Failed to map capture buffer");
output
.write_all(&mapping)
.expect("Error while writing output data");
}
};
type PooledDMABufHandlesProvider = PooledHandlesProvider<Vec<DMABufHandle<File>>>;
let device_path_cb = String::from(device_path);
let set_capture_format_cb =
move |f: FormatBuilder,
min_num_buffers: usize|
-> anyhow::Result<SetCaptureFormatRet<PooledDMABufHandlesProvider>> {
let format = f.set_pixelformat(b"RGB3").apply()?;
println!("New CAPTURE format: {:?}", format);
let dmabuf_fds: Vec<Vec<_>> = dmabuf_exporter::export_dmabufs(
&Path::new(&device_path_cb),
QueueType::VideoCaptureMplane,
&format,
min_num_buffers,
)
.unwrap();
Ok(SetCaptureFormatRet {
provider: PooledHandlesProvider::new(dmabuf_fds),
// TODO: can't the provider report the memory type that it is
// actually serving itself?
mem_type: MemoryType::DMABuf,
num_buffers: min_num_buffers,
})
};
let mut decoder = Decoder::open(&Path::new(&device_path))
.expect("Failed to open device")
.set_output_format(|f| {
let format: Format = f.set_pixelformat(b"FWHT").apply()?;
ensure!(
format.pixelformat == b"FWHT".into(),
"FWHT format not supported"
);
println!("Temporary output format: {:?}", format);
output_buffer_size = format.plane_fmt[0].sizeimage as usize;
Ok(())
})
.expect("Failed to set output format")
.allocate_output_buffers::<Vec<UserPtrHandle<Vec<u8>>>>(NUM_OUTPUT_BUFFERS)
.expect("Failed to allocate output buffers")
.set_poll_counter(poll_count_writer)
.start(
|_: DQBuffer<Output, Vec<UserPtrHandle<Vec<u8>>>>| (),
output_ready_cb,
set_capture_format_cb,
)
.expect("Failed to start decoder");
// Remove mutability.
let output_buffer_size = output_buffer_size;
println!("Allocated {} buffers", decoder.num_output_buffers());
println!("Required size for output buffers: {}", output_buffer_size);
let parser = FwhtFrameParser::new(stream)
.unwrap_or_else(|| panic!("No FWHT stream detected in {}", stream_path));
let mut total_size: usize = 0;
'mainloop: for (_cpt, mut frame) in parser.enumerate() {
// Ctrl-c ?
if lets_quit.load(Ordering::SeqCst) {
break;
}
let bytes_used = frame.len();
// Resize to the minimum required OUTPUT buffer size.
if frame.len() < output_buffer_size {
frame.resize(output_buffer_size, 0u8);
}
let v4l2_buffer = match decoder.get_buffer() {
Ok(buffer) => buffer,
// If we got interrupted while waiting for a buffer, just exit normally.
Err(GetBufferError::PollError(e)) if e.kind() == io::ErrorKind::Interrupted => {
break 'mainloop
}
Err(e) => panic!(e),
};
v4l2_buffer
.queue_with_handles(vec![UserPtrHandle::from(frame)], &[bytes_used])
.expect("Failed to queue input frame");
total_size += bytes_used;
}
decoder.stop().unwrap();
println!();
println!("Total size: {}", total_size);
}