devices/virtio/video/decoder/backend/
mod.rs1use base::AsRawDescriptor;
9
10use crate::virtio::video::decoder::Capability;
11use crate::virtio::video::error::VideoError;
12use crate::virtio::video::error::VideoResult;
13use crate::virtio::video::format::Format;
14use crate::virtio::video::format::Rect;
15use crate::virtio::video::resource::GuestResource;
16use crate::virtio::video::resource::GuestResourceHandle;
17
18#[cfg(feature = "ffmpeg")]
19pub mod ffmpeg;
20
21#[cfg(feature = "vaapi")]
22pub mod vaapi;
23#[cfg(feature = "libvda")]
24pub mod vda;
25
26pub trait DecoderSession {
28 fn set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()>;
32
33 fn decode(
51 &mut self,
52 resource_id: u32,
53 timestamp: u64,
54 resource: GuestResourceHandle,
55 offset: u32,
56 bytes_used: u32,
57 ) -> VideoResult<()>;
58
59 fn flush(&mut self) -> VideoResult<()>;
64
65 fn reset(&mut self) -> VideoResult<()>;
69
70 fn clear_output_buffers(&mut self) -> VideoResult<()>;
73
74 fn event_pipe(&self) -> &dyn AsRawDescriptor;
77
78 fn use_output_buffer(
89 &mut self,
90 picture_buffer_id: i32,
91 resource: GuestResource,
92 ) -> VideoResult<()>;
93
94 fn reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()>;
102
103 fn read_event(&mut self) -> VideoResult<DecoderEvent>;
105}
106
107impl<S: AsMut<dyn DecoderSession> + AsRef<dyn DecoderSession> + ?Sized> DecoderSession for S {
108 fn set_output_parameters(&mut self, buffer_count: usize, format: Format) -> VideoResult<()> {
109 self.as_mut().set_output_parameters(buffer_count, format)
110 }
111
112 fn decode(
113 &mut self,
114 resource_id: u32,
115 timestamp: u64,
116 resource: GuestResourceHandle,
117 offset: u32,
118 bytes_used: u32,
119 ) -> VideoResult<()> {
120 self.as_mut()
121 .decode(resource_id, timestamp, resource, offset, bytes_used)
122 }
123
124 fn flush(&mut self) -> VideoResult<()> {
125 self.as_mut().flush()
126 }
127
128 fn reset(&mut self) -> VideoResult<()> {
129 self.as_mut().reset()
130 }
131
132 fn clear_output_buffers(&mut self) -> VideoResult<()> {
133 self.as_mut().clear_output_buffers()
134 }
135
136 fn event_pipe(&self) -> &dyn AsRawDescriptor {
137 self.as_ref().event_pipe()
138 }
139
140 fn use_output_buffer(
141 &mut self,
142 picture_buffer_id: i32,
143 resource: GuestResource,
144 ) -> VideoResult<()> {
145 self.as_mut().use_output_buffer(picture_buffer_id, resource)
146 }
147
148 fn reuse_output_buffer(&mut self, picture_buffer_id: i32) -> VideoResult<()> {
149 self.as_mut().reuse_output_buffer(picture_buffer_id)
150 }
151
152 fn read_event(&mut self) -> VideoResult<DecoderEvent> {
153 self.as_mut().read_event()
154 }
155}
156
157pub trait DecoderBackend: Send {
158 type Session: DecoderSession;
159
160 fn get_capabilities(&self) -> Capability;
162
163 fn new_session(&mut self, format: Format) -> VideoResult<Self::Session>;
165
166 fn into_trait_object(self) -> Box<dyn DecoderBackend<Session = Box<dyn DecoderSession>>>
169 where
170 Self: Sized + 'static,
171 {
172 Box::new(GenericDecoderBackend(self)) as Box<dyn DecoderBackend<Session = _>>
173 }
174}
175
176struct GenericDecoderBackend<S: DecoderBackend>(pub S);
179
180impl<S> DecoderBackend for GenericDecoderBackend<S>
181where
182 S: DecoderBackend,
183 <S as DecoderBackend>::Session: 'static,
184{
185 type Session = Box<dyn DecoderSession>;
186
187 fn get_capabilities(&self) -> Capability {
188 self.0.get_capabilities()
189 }
190
191 fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> {
192 self.0
193 .new_session(format)
194 .map(|s| Box::new(s) as Box<dyn DecoderSession>)
195 }
196}
197
198impl<S> DecoderBackend for Box<S>
199where
200 S: ?Sized,
201 S: DecoderBackend,
202{
203 type Session = S::Session;
204
205 fn get_capabilities(&self) -> Capability {
206 self.as_ref().get_capabilities()
207 }
208
209 fn new_session(&mut self, format: Format) -> VideoResult<Self::Session> {
210 self.as_mut().new_session(format)
211 }
212}
213
214#[derive(Debug)]
215pub enum DecoderEvent {
216 ProvidePictureBuffers {
221 min_num_buffers: u32,
222 width: i32,
223 height: i32,
224 visible_rect: Rect,
225 },
226 PictureReady {
232 picture_buffer_id: i32,
233 timestamp: u64,
234 },
235 NotifyEndOfBitstreamBuffer(u32),
239 NotifyError(VideoError),
241 FlushCompleted(VideoResult<()>),
243 ResetCompleted(VideoResult<()>),
245}
246
247#[cfg(test)]
248mod tests {
250 use std::time::Duration;
251
252 use base::MappedRegion;
253 use base::MemoryMappingBuilder;
254 use base::SharedMemory;
255 use base::WaitContext;
256
257 use super::*;
258 use crate::virtio::video::format::FramePlane;
259 use crate::virtio::video::resource::GuestMemArea;
260 use crate::virtio::video::resource::GuestMemHandle;
261 use crate::virtio::video::resource::VirtioObjectHandle;
262
263 const H264_STREAM: &[u8] = include_bytes!("test-25fps.h264");
265 const H264_STREAM_WIDTH: i32 = 320;
266 const H264_STREAM_HEIGHT: i32 = 240;
267 const H264_STREAM_NUM_FRAMES: usize = 250;
268 const H264_STREAM_CRCS: &str = include_str!("test-25fps.crc");
269
270 struct H264NalIterator<'a> {
278 stream: &'a [u8],
279 pos: usize,
280 }
281
282 impl<'a> H264NalIterator<'a> {
283 fn new(stream: &'a [u8]) -> Self {
284 Self { stream, pos: 0 }
285 }
286
287 fn next_frame_pos(&self) -> Option<usize> {
289 const H264_START_CODE: [u8; 4] = [0x0, 0x0, 0x0, 0x1];
290 self.stream[self.pos + 1..]
291 .windows(H264_START_CODE.len())
292 .position(|window| window == H264_START_CODE)
293 .map(|pos| self.pos + pos + 1)
294 }
295
296 fn contains_frame(slice: &[u8]) -> bool {
299 slice[4..].windows(4).any(|window| {
300 window[0..3] == [0x0, 0x0, 0x1]
301 && (window[3] & 0x1f == 0x5 || window[3] & 0x1f == 0x1)
302 })
303 }
304 }
305
306 impl<'a> Iterator for H264NalIterator<'a> {
307 type Item = &'a [u8];
308
309 fn next(&mut self) -> Option<Self::Item> {
310 match self.pos {
311 cur_pos if cur_pos == self.stream.len() => None,
312 cur_pos => loop {
313 self.pos = self.next_frame_pos().unwrap_or(self.stream.len());
314 let slice = &self.stream[cur_pos..self.pos];
315
316 if Self::contains_frame(slice) || self.pos == self.stream.len() {
318 return Some(slice);
319 }
320 },
321 }
322 }
323 }
324
325 #[allow(dead_code)]
328 pub fn build_object_handle(mem: &SharedMemory) -> GuestResourceHandle {
329 GuestResourceHandle::VirtioObject(VirtioObjectHandle {
330 desc: base::clone_descriptor(mem).unwrap(),
331 modifier: 0,
332 })
333 }
334
335 #[allow(dead_code)]
338 pub fn build_guest_mem_handle(mem: &SharedMemory) -> GuestResourceHandle {
339 GuestResourceHandle::GuestPages(GuestMemHandle {
340 desc: base::clone_descriptor(mem).unwrap(),
341 mem_areas: vec![GuestMemArea {
342 offset: 0,
343 length: mem.size() as usize,
344 }],
345 })
346 }
347
348 pub fn decode_h264_generic<D, I, O>(
351 decoder: &mut D,
352 input_resource_builder: I,
353 output_resource_builder: O,
354 ) where
355 D: DecoderBackend,
356 I: Fn(&SharedMemory) -> GuestResourceHandle,
357 O: Fn(&SharedMemory) -> GuestResourceHandle,
358 {
359 const NUM_OUTPUT_BUFFERS: usize = 4;
360 const INPUT_BUF_SIZE: usize = 0x4000;
361 const OUTPUT_BUFFER_SIZE: usize =
362 (H264_STREAM_WIDTH * (H264_STREAM_HEIGHT + H264_STREAM_HEIGHT / 2)) as usize;
363 let mut session = decoder
364 .new_session(Format::H264)
365 .expect("failed to create H264 decoding session.");
366 let wait_ctx = WaitContext::new().expect("Failed to create wait context");
367 wait_ctx
368 .add(session.event_pipe(), 0u8)
369 .expect("Failed to add event pipe to wait context");
370 let output_buffers = (0..NUM_OUTPUT_BUFFERS)
372 .map(|i| {
373 SharedMemory::new(
374 format!("video-output-buffer-{i}"),
375 OUTPUT_BUFFER_SIZE as u64,
376 )
377 .unwrap()
378 })
379 .collect::<Vec<_>>();
380 let input_shm = SharedMemory::new("video-input-buffer", INPUT_BUF_SIZE as u64).unwrap();
381 let input_mapping = MemoryMappingBuilder::new(input_shm.size() as usize)
382 .from_shared_memory(&input_shm)
383 .build()
384 .unwrap();
385
386 let mut decoded_frames_count = 0usize;
387 let mut expected_frames_crcs = H264_STREAM_CRCS.lines();
388
389 let mut on_frame_decoded = |session: &mut D::Session, picture_buffer_id: i32| {
390 let mapping = MemoryMappingBuilder::new(OUTPUT_BUFFER_SIZE)
392 .from_shared_memory(&output_buffers[picture_buffer_id as usize])
393 .build()
394 .unwrap();
395 let mut frame_data = vec![0u8; mapping.size()];
396 assert_eq!(
397 mapping.read_slice(&mut frame_data, 0).unwrap(),
398 mapping.size()
399 );
400
401 let mut hasher = crc32fast::Hasher::new();
402 hasher.update(&frame_data);
403 let frame_crc = hasher.finalize();
404 assert_eq!(
405 format!("{frame_crc:08x}"),
406 expected_frames_crcs
407 .next()
408 .expect("No CRC for decoded frame")
409 );
410
411 session.reuse_output_buffer(picture_buffer_id).unwrap();
413 decoded_frames_count += 1;
414 };
415
416 const TIMESTAMP_FOR_INPUT_ID_FACTOR: u64 = 1_000_000;
418 for (input_id, slice) in H264NalIterator::new(H264_STREAM).enumerate() {
419 let buffer_handle = input_resource_builder(&input_shm);
420 input_mapping
421 .write_slice(slice, 0)
422 .expect("Failed to write stream data into input buffer.");
423 session
424 .decode(
425 input_id as u32,
426 input_id as u64 * TIMESTAMP_FOR_INPUT_ID_FACTOR,
427 buffer_handle,
428 0,
429 slice.len() as u32,
430 )
431 .expect("Call to decode() failed.");
432
433 let mut events = Vec::new();
435 while !wait_ctx.wait_timeout(Duration::ZERO).unwrap().is_empty() {
436 events.push(session.read_event().unwrap());
437 }
438
439 let event_idx = events
441 .iter()
442 .position(|event| {
443 let input_id = input_id as u32;
444 matches!(event, DecoderEvent::NotifyEndOfBitstreamBuffer(index) if *index == input_id)
445 })
446 .unwrap();
447 events.remove(event_idx);
448
449 if input_id == 0 {
452 let event_idx = events
453 .iter()
454 .position(|event| {
455 matches!(
456 event,
457 DecoderEvent::ProvidePictureBuffers {
458 width: H264_STREAM_WIDTH,
459 height: H264_STREAM_HEIGHT,
460 visible_rect: Rect {
461 left: 0,
462 top: 0,
463 right: H264_STREAM_WIDTH,
464 bottom: H264_STREAM_HEIGHT,
465 },
466 ..
467 }
468 )
469 })
470 .unwrap();
471 events.remove(event_idx);
472
473 let out_format = Format::NV12;
474
475 session
476 .set_output_parameters(NUM_OUTPUT_BUFFERS, out_format)
477 .unwrap();
478
479 for (picture_buffer_id, buffer) in output_buffers.iter().enumerate() {
481 session
482 .use_output_buffer(
483 picture_buffer_id as i32,
484 GuestResource {
485 handle: output_resource_builder(buffer),
486 planes: vec![
487 FramePlane {
488 offset: 0,
489 stride: H264_STREAM_WIDTH as usize,
490 size: (H264_STREAM_WIDTH * H264_STREAM_HEIGHT) as usize,
491 },
492 FramePlane {
493 offset: (H264_STREAM_WIDTH * H264_STREAM_HEIGHT) as usize,
494 stride: H264_STREAM_WIDTH as usize,
495 size: (H264_STREAM_WIDTH * H264_STREAM_HEIGHT) as usize,
496 },
497 ],
498 width: H264_STREAM_WIDTH as _,
499 height: H264_STREAM_HEIGHT as _,
500 format: out_format,
501 guest_cpu_mappable: false,
502 },
503 )
504 .unwrap();
505 }
506 }
507
508 for event in events {
510 match event {
511 DecoderEvent::PictureReady {
512 picture_buffer_id, ..
513 } => on_frame_decoded(&mut session, picture_buffer_id),
514 e => panic!("Unexpected event: {e:?}"),
515 }
516 }
517 }
518
519 session.flush().unwrap();
520
521 let mut received_flush_completed = false;
523 while !wait_ctx.wait_timeout(Duration::ZERO).unwrap().is_empty() {
524 match session.read_event().unwrap() {
525 DecoderEvent::PictureReady {
526 picture_buffer_id, ..
527 } => on_frame_decoded(&mut session, picture_buffer_id),
528 DecoderEvent::FlushCompleted(Ok(())) => {
529 received_flush_completed = true;
530 break;
531 }
532 e => panic!("Unexpected event: {e:?}"),
533 }
534 }
535
536 assert!(received_flush_completed);
538
539 assert_eq!(wait_ctx.wait_timeout(Duration::ZERO).unwrap().len(), 0);
541
542 assert_eq!(expected_frames_crcs.next(), None);
544
545 assert_eq!(decoded_frames_count, H264_STREAM_NUM_FRAMES);
547 }
548}