1use std::os::fd::BorrowedFd;
12use std::sync::Arc;
13
14use base::AsRawDescriptor;
15use base::SharedMemory;
16use base::WaitContext;
17use sync::Mutex;
18use virtio_media::devices::video_decoder::StreamParams;
19use virtio_media::devices::video_decoder::VideoDecoderBackend;
20use virtio_media::devices::video_decoder::VideoDecoderBackendEvent;
21use virtio_media::devices::video_decoder::VideoDecoderBackendSession;
22use virtio_media::devices::video_decoder::VideoDecoderBufferBacking;
23use virtio_media::devices::video_decoder::VideoDecoderSession;
24use virtio_media::ioctl::IoctlResult;
25use virtio_media::v4l2r;
26use virtio_media::v4l2r::bindings;
27use virtio_media::v4l2r::ioctl::V4l2MplaneFormat;
28use virtio_media::v4l2r::PixelFormat;
29use virtio_media::v4l2r::QueueClass;
30use virtio_media::v4l2r::QueueDirection;
31use virtio_media::v4l2r::QueueType;
32
33use crate::virtio::video::decoder::backend::DecoderEvent;
34use crate::virtio::video::decoder::backend::DecoderSession;
35use crate::virtio::video::decoder::capability::Capability;
36use crate::virtio::video::decoder::DecoderBackend;
37use crate::virtio::video::format::Format;
38use crate::virtio::video::format::FramePlane;
39use crate::virtio::video::format::PlaneFormat;
40use crate::virtio::video::resource::GuestMemArea;
41use crate::virtio::video::resource::GuestMemHandle;
42use crate::virtio::video::resource::GuestResource;
43use crate::virtio::video::resource::GuestResourceHandle;
44
45const INPUT_BUFFER_SIZE: u32 = 1024 * 1024;
47
48fn buffer_sizes_for_format(format: Format, coded_size: (u32, u32)) -> (u32, u32) {
50 match PlaneFormat::get_plane_layout(format, coded_size.0, coded_size.1) {
51 None => (0, INPUT_BUFFER_SIZE),
52 Some(layout) => (
53 layout.first().map(|p| p.stride).unwrap_or(0),
54 layout.iter().map(|p| p.plane_size).sum(),
55 ),
56 }
57}
58
59impl From<Format> for PixelFormat {
60 fn from(format: Format) -> Self {
61 PixelFormat::from_fourcc(match format {
62 Format::NV12 => b"NV12",
63 Format::YUV420 => b"YV12",
64 Format::H264 => b"H264",
65 Format::Hevc => b"HEVC",
66 Format::VP8 => b"VP80",
67 Format::VP9 => b"VP90",
68 })
69 }
70}
71
72impl TryFrom<PixelFormat> for Format {
73 type Error = ();
74
75 fn try_from(pixelformat: PixelFormat) -> Result<Self, Self::Error> {
76 match &pixelformat.to_fourcc() {
77 b"NV12" => Ok(Format::NV12),
78 b"YV12" => Ok(Format::YUV420),
79 b"H264" => Ok(Format::H264),
80 b"HEVC" => Ok(Format::Hevc),
81 b"VP80" => Ok(Format::VP8),
82 b"VP90" => Ok(Format::VP9),
83 _ => Err(()),
84 }
85 }
86}
87
88pub struct VirtioVideoAdapterBuffer {
89 shm: Vec<SharedMemory>,
91 registered: bool,
94}
95
96impl VideoDecoderBufferBacking for VirtioVideoAdapterBuffer {
97 fn new(queue: QueueType, index: u32, sizes: &[usize]) -> IoctlResult<Self> {
98 Ok(Self {
99 shm: sizes
100 .iter()
101 .enumerate()
102 .map(|(plane, size)| {
103 SharedMemory::new(
104 format!("virtio_media {:?} {}-{}", queue.direction(), index, plane),
105 *size as u64,
106 )
107 })
108 .collect::<Result<_, base::Error>>()
109 .map_err(|_| libc::ENOMEM)?,
110 registered: false,
111 })
112 }
113
114 fn fd_for_plane(&self, plane_idx: usize) -> Option<BorrowedFd> {
115 self.shm.get(plane_idx).map(|shm|
116 unsafe { BorrowedFd::borrow_raw(shm.descriptor.as_raw_descriptor())})
118 }
119}
120
121enum EosBuffer {
122 None,
124 Available(u32),
126 Awaiting,
128}
129
130pub struct VirtioVideoAdapterSession<D: DecoderBackend> {
131 id: u32,
133
134 backend_session: Option<D::Session>,
136
137 poller: WaitContext<u32>,
140
141 backend: Arc<Mutex<D>>,
143
144 input_format: Format,
145 output_format: Format,
146 coded_size: (u32, u32),
148 stream_params: StreamParams,
150
151 initial_drc_received: bool,
154 need_set_output_params: bool,
157
158 pending_output_buffers: Vec<(u32, Option<GuestResource>)>,
160
161 eos_capture_buffer: EosBuffer,
163
164 num_output_buffers: usize,
168}
169
170impl<D: DecoderBackend> VirtioVideoAdapterSession<D> {
171 fn get_or_create_session(&mut self) -> IoctlResult<&mut D::Session> {
174 let backend_session = match &mut self.backend_session {
175 Some(session) => session,
176 b @ None => {
177 let backend_session =
178 self.backend
179 .lock()
180 .new_session(self.input_format)
181 .map_err(|e| {
182 base::error!(
183 "{:#}",
184 anyhow::anyhow!("while creating backend session: {:#}", e)
185 );
186 libc::EIO
187 })?;
188 self.poller
189 .add(backend_session.event_pipe(), self.id)
190 .map_err(|e| {
191 base::error!("failed to listen to events of session {}: {:#}", self.id, e);
192 libc::EIO
193 })?;
194 b.get_or_insert(backend_session)
195 }
196 };
197
198 Ok(backend_session)
199 }
200
201 fn try_send_pending_output_buffers(&mut self) -> IoctlResult<()> {
202 if !self.initial_drc_received {
204 return Ok(());
205 }
206
207 let _ = self.get_or_create_session();
208 let Some(backend_session) = &mut self.backend_session else {
212 unreachable!()
213 };
214
215 if self.pending_output_buffers.is_empty() {
218 return Ok(());
219 }
220
221 if self.need_set_output_params {
224 let output_format = self.output_format;
225
226 backend_session
227 .set_output_parameters(self.num_output_buffers, output_format)
228 .map_err(|_| libc::EIO)?;
229 self.need_set_output_params = false;
230 }
231
232 for (index, resource) in self.pending_output_buffers.drain(..) {
233 match resource {
234 Some(resource) => backend_session
235 .use_output_buffer(index as i32, resource)
236 .map_err(|_| libc::EIO)?,
237 None => backend_session
238 .reuse_output_buffer(index as i32)
239 .map_err(|_| libc::EIO)?,
240 }
241 }
242
243 Ok(())
244 }
245}
246
247impl<D: DecoderBackend> VideoDecoderBackendSession for VirtioVideoAdapterSession<D> {
248 type BufferStorage = VirtioVideoAdapterBuffer;
249
250 fn current_format(&self, direction: QueueDirection) -> V4l2MplaneFormat {
251 let format = match direction {
252 QueueDirection::Output => self.input_format,
253 QueueDirection::Capture => self.output_format,
254 };
255 let (bytesperline, sizeimage) = buffer_sizes_for_format(format, self.coded_size);
256
257 let mut plane_fmt: [bindings::v4l2_plane_pix_format; bindings::VIDEO_MAX_PLANES as usize] =
258 Default::default();
259 plane_fmt[0] = bindings::v4l2_plane_pix_format {
260 bytesperline,
261 sizeimage,
262 reserved: Default::default(),
263 };
264
265 let pix_mp = bindings::v4l2_pix_format_mplane {
266 width: self.coded_size.0,
267 height: self.coded_size.1,
268 pixelformat: PixelFormat::from(format).to_u32(),
269 field: bindings::v4l2_field_V4L2_FIELD_NONE,
270 plane_fmt,
271 num_planes: 1,
272 flags: 0,
273 ..Default::default()
274 };
275
276 V4l2MplaneFormat::from((direction, pix_mp))
277 }
278
279 fn stream_params(&self) -> StreamParams {
280 self.stream_params.clone()
281 }
282
283 fn drain(&mut self) -> IoctlResult<()> {
284 if let Some(backend_session) = &mut self.backend_session {
285 backend_session.flush().map_err(|_| libc::EIO)
286 } else {
287 Ok(())
288 }
289 }
290
291 fn clear_output_buffers(&mut self) -> IoctlResult<()> {
292 self.pending_output_buffers.clear();
293 self.eos_capture_buffer = EosBuffer::None;
294 if let Some(session) = &mut self.backend_session {
295 session.clear_output_buffers().map_err(|_| libc::EIO)
296 } else {
297 Ok(())
298 }
299 }
300
301 fn next_event(&mut self) -> Option<VideoDecoderBackendEvent> {
302 if let Some(backend_session) = &mut self.backend_session {
303 let event = backend_session.read_event().unwrap();
304
305 match event {
306 DecoderEvent::NotifyEndOfBitstreamBuffer(id) => {
307 Some(VideoDecoderBackendEvent::InputBufferDone {
308 buffer_id: id,
309 error: 0,
310 })
311 }
312 DecoderEvent::ProvidePictureBuffers {
313 min_num_buffers,
314 width,
315 height,
316 visible_rect,
317 } => {
318 self.stream_params = StreamParams {
319 min_output_buffers: min_num_buffers + 1,
321 coded_size: (width as u32, height as u32),
322 visible_rect: v4l2r::Rect {
323 left: visible_rect.left,
324 top: visible_rect.top,
325 width: visible_rect.right.saturating_sub(visible_rect.left) as u32,
326 height: visible_rect.bottom.saturating_sub(visible_rect.top) as u32,
327 },
328 };
329 self.coded_size = self.stream_params.coded_size;
330 self.need_set_output_params = true;
331 self.initial_drc_received = true;
332
333 if self.try_send_pending_output_buffers().is_err() {
335 base::error!(
336 "failed to send pending output buffers after resolution change event"
337 );
338 }
339
340 Some(VideoDecoderBackendEvent::StreamFormatChanged)
341 }
342 DecoderEvent::PictureReady {
343 picture_buffer_id,
344 timestamp,
345 ..
346 } => {
347 let timestamp = bindings::timeval {
348 tv_sec: (timestamp / 1_000_000) as i64,
349 tv_usec: (timestamp % 1_000_000) as i64,
350 };
351 let bytes_used = buffer_sizes_for_format(self.output_format, self.coded_size).1;
352
353 Some(VideoDecoderBackendEvent::FrameCompleted {
354 buffer_id: picture_buffer_id as u32,
355 timestamp,
356 bytes_used: vec![bytes_used],
357 is_last: false,
358 })
359 }
360 DecoderEvent::FlushCompleted(res) => {
361 if let Err(err) = res {
362 base::error!("flush command failed: {:#}", err);
363 }
364
365 match self.eos_capture_buffer {
369 EosBuffer::Available(buffer_id) => {
370 Some(VideoDecoderBackendEvent::FrameCompleted {
371 buffer_id,
372 timestamp: Default::default(),
373 bytes_used: vec![0],
374 is_last: true,
375 })
376 }
377 _ => {
378 self.eos_capture_buffer = EosBuffer::Awaiting;
379 None
380 }
381 }
382 }
383 DecoderEvent::ResetCompleted(_) => todo!(),
384 DecoderEvent::NotifyError(_) => todo!(),
385 }
386 } else {
387 None
388 }
389 }
390
391 fn buffers_allocated(&mut self, direction: QueueDirection, num_buffers: u32) {
392 if matches!(direction, QueueDirection::Capture) {
393 self.num_output_buffers = num_buffers as usize;
394 }
395 }
396
397 fn poll_fd(&self) -> Option<BorrowedFd> {
398 Some(unsafe { BorrowedFd::borrow_raw(self.poller.as_raw_descriptor()) })
401 }
402
403 fn decode(
404 &mut self,
405 input: &Self::BufferStorage,
406 index: u32,
407 timestamp: bindings::timeval,
408 bytes_used: u32,
409 ) -> IoctlResult<()> {
410 let backend_session = self.get_or_create_session()?;
411 let timestamp = timestamp.tv_sec as u64 * 1_000_000 + timestamp.tv_usec as u64;
412 let resource = GuestResourceHandle::GuestPages(GuestMemHandle {
413 desc: input.shm[0]
414 .descriptor
415 .try_clone()
416 .map_err(|_| libc::ENOMEM)?,
417 mem_areas: vec![GuestMemArea {
418 offset: 0,
419 length: input.shm[0].size as usize,
420 }],
421 });
422
423 backend_session
424 .decode(index, timestamp, resource, 0, bytes_used)
425 .map_err(|_| libc::EIO)
426 }
427
428 fn use_as_output(&mut self, index: u32, backing: &mut Self::BufferStorage) -> IoctlResult<()> {
429 match self.eos_capture_buffer {
430 EosBuffer::None => {
431 self.eos_capture_buffer = EosBuffer::Available(index);
432 Ok(())
433 }
434 EosBuffer::Awaiting => {
435 Ok(())
443 }
444 EosBuffer::Available(_) => {
445 let resource = if !backing.registered {
446 let resource = GuestResourceHandle::GuestPages(GuestMemHandle {
447 desc: backing.shm[0]
448 .descriptor
449 .try_clone()
450 .map_err(|_| libc::ENOMEM)?,
451 mem_areas: vec![GuestMemArea {
452 offset: 0,
453 length: backing.shm[0].size as usize,
454 }],
455 });
456
457 let plane_formats = PlaneFormat::get_plane_layout(
458 self.output_format,
459 self.coded_size.0,
460 self.coded_size.1,
461 )
462 .ok_or_else(|| {
463 base::error!("could not obtain plane layout for output buffer");
464 libc::EINVAL
465 })?;
466
467 let mut buffer_offset = 0;
468 let resource_handle = GuestResource {
469 handle: resource,
470 planes: plane_formats
471 .into_iter()
472 .map(|p| {
473 let plane_offset = buffer_offset;
474 buffer_offset += p.plane_size;
475
476 FramePlane {
477 offset: plane_offset as usize,
478 stride: p.stride as usize,
479 size: p.plane_size as usize,
480 }
481 })
482 .collect(),
483 width: self.coded_size.0,
484 height: self.coded_size.1,
485 format: self.output_format,
486 guest_cpu_mappable: false,
487 };
488
489 backing.registered = true;
490
491 Some(resource_handle)
492 } else {
493 None
494 };
495
496 self.pending_output_buffers.push((index, resource));
497
498 self.try_send_pending_output_buffers()
499 }
500 }
501 }
502}
503
504pub struct VirtioVideoAdapter<D: DecoderBackend> {
505 backend: Arc<Mutex<D>>,
506 capability: Capability,
507}
508
509impl<D: DecoderBackend> VirtioVideoAdapter<D> {
510 pub fn new(backend: D) -> Self {
511 let capability = backend.get_capabilities();
512 Self {
513 backend: Arc::new(Mutex::new(backend)),
514 capability,
515 }
516 }
517}
518
519impl<D: DecoderBackend> VideoDecoderBackend for VirtioVideoAdapter<D> {
520 type Session = VirtioVideoAdapterSession<D>;
521
522 fn new_session(&mut self, id: u32) -> IoctlResult<Self::Session> {
523 let first_input_format = self
524 .capability
525 .input_formats()
526 .first()
527 .ok_or(libc::ENODEV)?;
528 let first_output_format = self
529 .capability
530 .output_formats()
531 .first()
532 .ok_or(libc::ENODEV)?;
533
534 let coded_size = first_input_format
535 .frame_formats
536 .first()
537 .map(|f| (f.width.min, f.height.min))
538 .unwrap_or((0, 0));
539
540 Ok(VirtioVideoAdapterSession {
541 id,
542 backend_session: None,
543 poller: WaitContext::new().map_err(|_| libc::EIO)?,
544 backend: Arc::clone(&self.backend),
545 input_format: first_input_format.format,
546 coded_size,
547 output_format: first_output_format.format,
548 stream_params: StreamParams {
549 min_output_buffers: 1,
550 coded_size,
551 visible_rect: v4l2r::Rect {
552 left: 0,
553 top: 0,
554 width: coded_size.0,
555 height: coded_size.1,
556 },
557 },
558 initial_drc_received: false,
559 need_set_output_params: false,
560 pending_output_buffers: Default::default(),
561 num_output_buffers: 0,
562 eos_capture_buffer: EosBuffer::None,
563 })
564 }
565
566 fn close_session(&mut self, _session: Self::Session) {}
567
568 fn enum_formats(
569 &self,
570 _session: &VideoDecoderSession<Self::Session>,
571 direction: QueueDirection,
572 index: u32,
573 ) -> Option<bindings::v4l2_fmtdesc> {
574 let formats = match direction {
575 QueueDirection::Output => self.capability.input_formats(),
576 QueueDirection::Capture => self.capability.output_formats(),
577 };
578 let fmt = formats.get(index as usize)?;
579
580 Some(bindings::v4l2_fmtdesc {
581 index,
582 type_: QueueType::from_dir_and_class(direction, QueueClass::VideoMplane) as u32,
583 pixelformat: PixelFormat::from(fmt.format).to_u32(),
584 ..Default::default()
585 })
586 }
587
588 fn frame_sizes(&self, pixel_format: u32) -> Option<bindings::v4l2_frmsize_stepwise> {
589 let format = Format::try_from(PixelFormat::from_u32(pixel_format)).ok()?;
590 let frame_sizes = self
591 .capability
592 .input_formats()
593 .iter()
594 .chain(self.capability.output_formats().iter())
595 .find(|f| f.format == format)
596 .and_then(|f| f.frame_formats.first())?;
597
598 Some(bindings::v4l2_frmsize_stepwise {
599 min_width: frame_sizes.width.min,
600 max_width: frame_sizes.width.max,
601 step_width: frame_sizes.width.step,
602 min_height: frame_sizes.height.min,
603 max_height: frame_sizes.height.max,
604 step_height: frame_sizes.height.step,
605 })
606 }
607
608 fn adjust_format(
609 &self,
610 session: &Self::Session,
611 direction: QueueDirection,
612 format: V4l2MplaneFormat,
613 ) -> V4l2MplaneFormat {
614 let pix_mp: &bindings::v4l2_pix_format_mplane = format.as_ref();
615
616 let available_formats = match direction {
617 QueueDirection::Output => self.capability.input_formats(),
618 QueueDirection::Capture => self.capability.output_formats(),
619 };
620
621 let pixel_format = Format::try_from(PixelFormat::from_u32(pix_mp.pixelformat)).ok();
622
623 let Some(matching_format) = pixel_format
626 .and_then(|format| available_formats.iter().find(|f| f.format == format))
627 .or_else(|| available_formats.first())
628 else {
629 return V4l2MplaneFormat::from((direction, Default::default()));
632 };
633
634 let (mut width, mut height) = matching_format
637 .frame_formats
638 .iter()
639 .find(|format| {
640 let width = pix_mp.width;
641 let height = pix_mp.height;
642 (format.width.min..format.width.max).contains(&width)
643 && (format.height.min..format.height.max).contains(&height)
644 })
645 .map(|_| (pix_mp.width, pix_mp.height))
646 .unwrap_or_else(|| {
647 matching_format
648 .frame_formats
649 .first()
650 .map(|format| {
651 (
652 std::cmp::min(format.width.max, pix_mp.width),
653 (std::cmp::min(format.height.max, pix_mp.height)),
654 )
655 })
656 .unwrap_or((0, 0))
657 });
658
659 if direction == QueueDirection::Capture {
661 width = std::cmp::max(width, session.coded_size.0);
662 height = std::cmp::max(height, session.coded_size.1);
663 }
664
665 let num_planes = 1;
667 let (bytesperline, sizeimage) =
668 buffer_sizes_for_format(matching_format.format, (width, height));
669 let mut plane_fmt: [bindings::v4l2_plane_pix_format; 8] = Default::default();
670 plane_fmt[0] = bindings::v4l2_plane_pix_format {
671 bytesperline,
672 sizeimage,
673 reserved: Default::default(),
674 };
675
676 V4l2MplaneFormat::from((
677 direction,
678 bindings::v4l2_pix_format_mplane {
679 width,
680 height,
681 pixelformat: PixelFormat::from(matching_format.format).to_u32(),
682 field: bindings::v4l2_field_V4L2_FIELD_NONE,
683 plane_fmt,
684 num_planes,
685 flags: 0,
686 ..Default::default()
687 },
688 ))
689 }
690
691 fn apply_format(
692 &self,
693 session: &mut Self::Session,
694 direction: QueueDirection,
695 format: &V4l2MplaneFormat,
696 ) {
697 match direction {
698 QueueDirection::Output => {
699 session.input_format =
700 Format::try_from(format.pixelformat()).unwrap_or_else(|_| {
701 self.capability
702 .input_formats()
703 .first()
704 .map(|f| f.format)
705 .unwrap_or(Format::H264)
706 });
707 let coded_size = format.size();
708 if coded_size != (0, 0) {
710 let coded_size = format.size();
711 session.coded_size = coded_size;
712 }
713 }
714 QueueDirection::Capture => {
715 session.output_format =
716 Format::try_from(format.pixelformat()).unwrap_or_else(|_| {
717 self.capability
718 .output_formats()
719 .first()
720 .map(|f| f.format)
721 .unwrap_or(Format::NV12)
722 });
723 }
724 }
725 }
726}