1 use std::{
2 fs::File,
3 io,
4 io::BufReader,
5 io::Write,
6 path::Path,
7 sync::atomic::AtomicBool,
8 sync::atomic::{AtomicUsize, Ordering},
9 sync::Arc,
10 };
11
12 use anyhow::ensure;
13 use nix::sys::time::{TimeVal, TimeValLike};
14 use v4l2r::{
15 decoder::{format::fwht::FwhtFrameParser, FormatChangedReply},
16 device::queue::{handles_provider::MmapProvider, FormatBuilder},
17 memory::{MemoryType, MmapHandle},
18 PlaneLayout,
19 };
20 use v4l2r::{
21 decoder::{
22 format::{h264::H264FrameSplitter, StreamSplitter},
23 stateful::GetBufferError,
24 },
25 PixelFormat,
26 };
27 use v4l2r::{
28 decoder::{stateful::Decoder, DecoderEvent},
29 device::{
30 poller::PollError,
31 queue::{direction::Capture, dqbuf::DqBuffer},
32 },
33 Format, Rect,
34 };
35
36 use clap::{App, Arg};
37
38 enum Codec {
39 Fwht,
40 H264,
41 }
42
main()43 fn main() {
44 env_logger::init();
45
46 let matches = App::new("V4L2 stateful decoder")
47 .arg(
48 Arg::with_name("stream")
49 .required(true)
50 .help("Path to the encoded stream to decode"),
51 )
52 .arg(
53 Arg::with_name("device")
54 .required(true)
55 .help("Path to the vicodec device file"),
56 )
57 .arg(
58 Arg::with_name("input_format")
59 .long("input_format")
60 .required(false)
61 .takes_value(true)
62 .default_value("fwht")
63 .help("Format of the encoded stream (fwht or h264)"),
64 )
65 .arg(
66 Arg::with_name("output_file")
67 .long("save")
68 .required(false)
69 .takes_value(true)
70 .help("Save the decoded RGB frames to a file"),
71 )
72 .get_matches();
73
74 let stream_path = matches
75 .value_of("stream")
76 .expect("Stream argument not specified");
77 let device_path = matches
78 .value_of("device")
79 .expect("Device argument not specified");
80 let codec = match matches
81 .value_of("input_format")
82 .expect("Input format not specified")
83 {
84 "fwht" => Codec::Fwht,
85 "h264" => Codec::H264,
86 _ => panic!("Invalid input format specified"),
87 };
88
89 let stream = BufReader::new(File::open(stream_path).expect("Compressed stream not found"));
90
91 let mut output_file: Option<File> = matches
92 .value_of("output_file")
93 .map(|path| File::create(path).expect("Invalid output file specified."));
94
95 let lets_quit = Arc::new(AtomicBool::new(false));
96 // Setup the Ctrl+c handler.
97 {
98 let lets_quit_handler = lets_quit.clone();
99 ctrlc::set_handler(move || {
100 lets_quit_handler.store(true, Ordering::SeqCst);
101 })
102 .expect("Failed to set Ctrl-C handler.");
103 }
104
105 const NUM_OUTPUT_BUFFERS: usize = 4;
106
107 let poll_count_reader = Arc::new(AtomicUsize::new(0));
108 let poll_count_writer = Arc::clone(&poll_count_reader);
109 let start_time = std::time::Instant::now();
110 let mut frame_counter = 0usize;
111 let mut output_ready_cb = move |cap_dqbuf: DqBuffer<Capture, Vec<MmapHandle>>| {
112 let bytes_used = *cap_dqbuf.data.get_first_plane().bytesused as usize;
113 // Ignore zero-sized buffers.
114 if bytes_used == 0 {
115 return;
116 }
117
118 let elapsed = start_time.elapsed();
119 frame_counter += 1;
120 let fps = frame_counter as f32 / elapsed.as_millis() as f32 * 1000.0;
121 let ppf = poll_count_reader.load(Ordering::SeqCst) as f32 / frame_counter as f32;
122 print!(
123 "\rDecoded buffer {:#5}, index: {:#2}), bytes used:{:#6} fps: {:#5.2} ppf: {:#4.2}",
124 cap_dqbuf.data.sequence(),
125 cap_dqbuf.data.index(),
126 bytes_used,
127 fps,
128 ppf,
129 );
130 io::stdout().flush().unwrap();
131
132 if let Some(ref mut output) = output_file {
133 for i in 0..cap_dqbuf.data.num_planes() {
134 let mapping = cap_dqbuf
135 .get_plane_mapping(i)
136 .expect("Failed to map capture buffer plane");
137 output
138 .write_all(&mapping)
139 .expect("Error while writing output data");
140 }
141 }
142 };
143 let decoder_event_cb = move |event: DecoderEvent<MmapProvider>| match event {
144 DecoderEvent::FrameDecoded(dqbuf) => output_ready_cb(dqbuf),
145 DecoderEvent::EndOfStream => (),
146 };
147 let set_capture_format_cb = move |f: FormatBuilder,
148 visible_rect: Rect,
149 min_num_buffers: usize|
150 -> anyhow::Result<FormatChangedReply<MmapProvider>> {
151 // Let's keep the pixel format that the decoder found convenient.
152 let format = f.format();
153
154 println!(
155 "New CAPTURE format: {:?} (visible rect: {})",
156 format, visible_rect
157 );
158
159 Ok(FormatChangedReply {
160 provider: MmapProvider::new(format),
161 // TODO: can't the provider report the memory type that it is
162 // actually serving itself?
163 mem_type: MemoryType::Mmap,
164 num_buffers: min_num_buffers,
165 })
166 };
167
168 let mut decoder = Decoder::open(Path::new(device_path))
169 .expect("Failed to open device")
170 .set_output_format(|f| {
171 let pixel_format: PixelFormat = match codec {
172 Codec::Fwht => b"FWHT".into(),
173 Codec::H264 => b"H264".into(),
174 };
175 let format: Format = f
176 .set_pixelformat(pixel_format)
177 // 1 MB per decoding unit should be enough for most streams.
178 .set_planes_layout(vec![PlaneLayout {
179 sizeimage: 1024 * 1024,
180 ..Default::default()
181 }])
182 .apply()?;
183
184 ensure!(
185 format.pixelformat == pixel_format,
186 format!("{} format not supported by device", pixel_format)
187 );
188
189 println!("Tentative OUTPUT format: {:?}", format);
190
191 Ok(())
192 })
193 .expect("Failed to set output format")
194 .allocate_output_buffers::<Vec<MmapHandle>>(NUM_OUTPUT_BUFFERS)
195 .expect("Failed to allocate output buffers")
196 .set_poll_counter(poll_count_writer)
197 .start(|_| (), decoder_event_cb, set_capture_format_cb)
198 .expect("Failed to start decoder");
199
200 println!("Allocated {} buffers", decoder.num_output_buffers());
201
202 let parser = match codec {
203 Codec::Fwht => Box::new(
204 FwhtFrameParser::new(stream)
205 .unwrap_or_else(|| panic!("No FWHT stream detected in {}", stream_path)),
206 ) as Box<dyn StreamSplitter>,
207 Codec::H264 => Box::new(
208 H264FrameSplitter::new(stream)
209 .unwrap_or_else(|| panic!("No H.264 stream detected in {}", stream_path)),
210 ) as Box<dyn StreamSplitter>,
211 };
212
213 'mainloop: for (bitstream_id, frame) in parser.enumerate() {
214 // Ctrl-c ?
215 if lets_quit.load(Ordering::SeqCst) {
216 break;
217 }
218
219 let v4l2_buffer = match decoder.get_buffer() {
220 Ok(buffer) => buffer,
221 // If we got interrupted while waiting for a buffer, just exit normally.
222 Err(GetBufferError::PollError(PollError::EPollWait(nix::errno::Errno::EINTR))) => {
223 break 'mainloop
224 }
225 Err(e) => panic!("{}", e),
226 };
227
228 let mut mapping = v4l2_buffer
229 .get_plane_mapping(0)
230 .expect("Failed to get OUTPUT buffer mapping");
231 mapping.as_mut()[0..frame.len()].copy_from_slice(&frame);
232 drop(mapping);
233
234 // TODO setting the timestamp should not be necessary. This is a requirement of the crosvm
235 // video device.
236 v4l2_buffer
237 .set_timestamp(TimeVal::seconds(bitstream_id as i64))
238 .queue(&[frame.len()])
239 .expect("Failed to queue input frame");
240 }
241
242 decoder.drain(true).unwrap();
243 decoder.stop().unwrap();
244 println!();
245 }
246