1use std::ops::Deref;
6use std::os::fd::BorrowedFd;
7
8use v4l2r::bindings;
9use v4l2r::ioctl::BufferCapabilities;
10use v4l2r::ioctl::BufferField;
11use v4l2r::ioctl::BufferFlags;
12use v4l2r::ioctl::DecoderCmd;
13use v4l2r::ioctl::EventType;
14use v4l2r::ioctl::MemoryConsistency;
15use v4l2r::ioctl::SelectionTarget;
16use v4l2r::ioctl::SelectionType;
17use v4l2r::ioctl::SrcChanges;
18use v4l2r::ioctl::V4l2Buffer;
19use v4l2r::ioctl::V4l2MplaneFormat;
20use v4l2r::ioctl::V4l2PlanesWithBacking;
21use v4l2r::ioctl::V4l2PlanesWithBackingMut;
22use v4l2r::memory::MemoryType;
23use v4l2r::Colorspace;
24use v4l2r::Quantization;
25use v4l2r::QueueClass;
26use v4l2r::QueueDirection;
27use v4l2r::QueueType;
28use v4l2r::XferFunc;
29use v4l2r::YCbCrEncoding;
30
31use crate::io::ReadFromDescriptorChain;
32use crate::io::WriteToDescriptorChain;
33use crate::ioctl::virtio_media_dispatch_ioctl;
34use crate::ioctl::IoctlResult;
35use crate::ioctl::VirtioMediaIoctlHandler;
36use crate::mmap::MmapMappingManager;
37use crate::DequeueBufferEvent;
38use crate::SessionEvent;
39use crate::SgEntry;
40use crate::V4l2Event;
41use crate::V4l2Ioctl;
42use crate::VirtioMediaDevice;
43use crate::VirtioMediaDeviceSession;
44use crate::VirtioMediaEventQueue;
45use crate::VirtioMediaHostMemoryMapper;
46use crate::VIRTIO_MEDIA_MMAP_FLAG_RW;
47
48pub trait VideoDecoderBufferBacking {
50 fn new(queue: QueueType, index: u32, sizes: &[usize]) -> IoctlResult<Self>
51 where
52 Self: Sized;
53
54 fn fd_for_plane(&self, plane_idx: usize) -> Option<BorrowedFd<'_>>;
55}
56
57pub struct VideoDecoderBuffer<S: VideoDecoderBufferBacking> {
58 v4l2_buffer: V4l2Buffer,
59
60 pub backing: S,
62}
63
64impl<S: VideoDecoderBufferBacking> VideoDecoderBuffer<S> {
65 fn new(
66 queue: QueueType,
67 index: u32,
68 sizes: &[usize],
69 mmap_offset: u32,
71 ) -> IoctlResult<Self> {
72 let backing = S::new(queue, index, sizes)?;
73
74 let mut v4l2_buffer = V4l2Buffer::new(queue, index, MemoryType::Mmap);
75 if let V4l2PlanesWithBackingMut::Mmap(mut planes) =
76 v4l2_buffer.planes_with_backing_iter_mut()
77 {
78 let mut plane = planes.next().unwrap();
80 plane.set_mem_offset(mmap_offset);
81 *plane.length = sizes[0] as u32;
82 } else {
83 panic!()
86 }
87
88 v4l2_buffer.set_flags(BufferFlags::TIMESTAMP_MONOTONIC);
89 v4l2_buffer.set_field(BufferField::None);
90
91 Ok(Self {
92 v4l2_buffer,
93 backing,
94 })
95 }
96
97 pub fn index(&self) -> u32 {
98 self.v4l2_buffer.index()
99 }
100
101 pub fn timestamp(&self) -> bindings::timeval {
102 self.v4l2_buffer.timestamp()
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub enum VideoDecoderBackendEvent {
109 StreamFormatChanged,
112 InputBufferDone {
114 buffer_id: u32,
115 error: i32,
117 },
118 FrameCompleted {
120 buffer_id: u32,
121 timestamp: bindings::timeval,
122 bytes_used: Vec<u32>,
123 is_last: bool,
124 },
125}
126
127#[derive(Clone)]
129pub struct StreamParams {
130 pub min_output_buffers: u32,
132 pub coded_size: (u32, u32),
134 pub visible_rect: v4l2r::Rect,
136}
137
138pub trait VideoDecoderBackendSession {
140 type BufferStorage: VideoDecoderBufferBacking;
141
142 fn decode(
148 &mut self,
149 input: &Self::BufferStorage,
150 index: u32,
151 timestamp: bindings::timeval,
152 bytes_used: u32,
153 ) -> IoctlResult<()>;
154
155 fn use_as_output(&mut self, index: u32, backing: &mut Self::BufferStorage) -> IoctlResult<()>;
157
158 fn drain(&mut self) -> IoctlResult<()>;
163
164 fn clear_output_buffers(&mut self) -> IoctlResult<()>;
166
167 fn next_event(&mut self) -> Option<VideoDecoderBackendEvent>;
169
170 fn current_format(&self, direction: QueueDirection) -> V4l2MplaneFormat;
173
174 fn stream_params(&self) -> StreamParams;
176
177 fn buffers_allocated(&mut self, _direction: QueueDirection, _num_buffers: u32) {}
183
184 fn poll_fd(&self) -> Option<BorrowedFd<'_>> {
187 None
188 }
189
190 fn streaming_state(&mut self, _direction: QueueDirection, _streaming: bool) {}
193
194 fn resume(&mut self) {}
202}
203
204#[derive(Debug)]
206enum VideoDecoderStreamingState {
207 Stopped {
210 input_streaming: bool,
211 output_streaming: bool,
212 },
213 Running,
215 Paused,
217}
218
219impl Default for VideoDecoderStreamingState {
220 fn default() -> Self {
221 Self::Stopped {
222 input_streaming: false,
223 output_streaming: false,
224 }
225 }
226}
227
228impl VideoDecoderStreamingState {
229 fn input_streamon(&mut self) {
230 match self {
231 Self::Stopped {
232 ref mut input_streaming,
233 output_streaming,
234 } if !(*input_streaming) => {
235 *input_streaming = true;
236 if *output_streaming {
239 *self = Self::Running;
240 }
241 }
242 Self::Stopped { .. } | Self::Running | Self::Paused => (),
243 }
244 }
245
246 fn input_streamoff(&mut self) {
247 match self {
248 Self::Stopped {
249 ref mut input_streaming,
250 ..
251 } => *input_streaming = false,
252 Self::Running | Self::Paused => {
253 *self = Self::Stopped {
254 input_streaming: false,
255 output_streaming: true,
256 }
257 }
258 }
259 }
260
261 fn output_streamon(&mut self) {
262 match self {
263 Self::Stopped {
264 input_streaming,
265 ref mut output_streaming,
266 } if !(*output_streaming) => {
267 *output_streaming = true;
268 if *input_streaming {
271 *self = Self::Running;
272 }
273 }
274 Self::Stopped { .. } | Self::Running | Self::Paused => (),
275 }
276 }
277
278 fn output_streamoff(&mut self) {
279 match self {
280 Self::Stopped {
281 ref mut output_streaming,
282 ..
283 } => *output_streaming = false,
284 Self::Running | Self::Paused => {
285 *self = Self::Stopped {
286 input_streaming: true,
287 output_streaming: false,
288 }
289 }
290 }
291 }
292
293 fn is_output_streaming(&mut self) -> bool {
294 matches!(
295 self,
296 Self::Running
297 | Self::Stopped {
298 output_streaming: true,
299 ..
300 }
301 )
302 }
303}
304
305enum CropRectangle {
317 Settable(v4l2r::Rect),
319 FromStream(v4l2r::Rect),
321}
322
323impl Deref for CropRectangle {
324 type Target = v4l2r::Rect;
325
326 fn deref(&self) -> &Self::Target {
327 match self {
328 CropRectangle::Settable(r) => r,
329 CropRectangle::FromStream(r) => r,
330 }
331 }
332}
333
334#[derive(Debug, Clone, Copy)]
336struct V4l2FormatColorspace {
337 colorspace: Colorspace,
338 xfer_func: XferFunc,
339 ycbcr_enc: YCbCrEncoding,
340 quantization: Quantization,
341}
342
343impl Default for V4l2FormatColorspace {
344 fn default() -> Self {
345 Self {
346 colorspace: Colorspace::Rec709,
347 xfer_func: XferFunc::None,
348 ycbcr_enc: YCbCrEncoding::E709,
349 quantization: Quantization::LimRange,
350 }
351 }
352}
353
354impl V4l2FormatColorspace {
355 fn apply(self, pix_mp: &mut bindings::v4l2_pix_format_mplane) {
357 pix_mp.colorspace = self.colorspace as u32;
358 pix_mp.__bindgen_anon_1 = bindings::v4l2_pix_format_mplane__bindgen_ty_1 {
359 ycbcr_enc: self.ycbcr_enc as u8,
360 };
361 pix_mp.quantization = self.quantization as u8;
362 pix_mp.xfer_func = self.xfer_func as u8;
363 }
364}
365
366pub struct VideoDecoderSession<S: VideoDecoderBackendSession> {
367 id: u32,
368
369 state: VideoDecoderStreamingState,
370
371 input_buffers: Vec<VideoDecoderBuffer<S::BufferStorage>>,
372 output_buffers: Vec<VideoDecoderBuffer<S::BufferStorage>>,
373 pending_output_buffers: Vec<u32>,
376
377 sequence_cpt: u32,
378
379 src_change_subscribed: bool,
382 eos_subscribed: bool,
385
386 crop_rectangle: CropRectangle,
387
388 colorspace: V4l2FormatColorspace,
390
391 backend_session: S,
393
394 format_change_pending: bool,
400}
401
402impl<S: VideoDecoderBackendSession> VirtioMediaDeviceSession for VideoDecoderSession<S> {
403 fn poll_fd(&self) -> Option<BorrowedFd<'_>> {
404 self.backend_session.poll_fd()
405 }
406}
407
408impl<S: VideoDecoderBackendSession> VideoDecoderSession<S> {
409 fn current_format(&self, direction: QueueDirection) -> V4l2MplaneFormat {
415 let format = self.backend_session.current_format(direction);
416
417 let mut pix_mp =
418 *<V4l2MplaneFormat as AsRef<bindings::v4l2_pix_format_mplane>>::as_ref(&format);
419
420 self.colorspace.apply(&mut pix_mp);
421
422 V4l2MplaneFormat::from((direction, pix_mp))
423 }
424
425 fn try_decoder_cmd(&self, cmd: DecoderCmd) -> IoctlResult<DecoderCmd> {
426 match cmd {
427 DecoderCmd::Stop { .. } => Ok(DecoderCmd::stop()),
428 DecoderCmd::Start { .. } => Ok(DecoderCmd::start()),
429 DecoderCmd::Pause { .. } => {
430 match &self.state {
431 VideoDecoderStreamingState::Stopped { .. } => Err(libc::EINVAL),
434 VideoDecoderStreamingState::Running | VideoDecoderStreamingState::Paused => {
435 Ok(DecoderCmd::pause())
436 }
437 }
438 }
439 DecoderCmd::Resume => {
440 match &self.state {
441 VideoDecoderStreamingState::Stopped { .. } => Err(libc::EINVAL),
444 VideoDecoderStreamingState::Paused | VideoDecoderStreamingState::Running => {
445 Ok(DecoderCmd::resume())
446 }
447 }
448 }
449 }
450 }
451
452 fn try_send_pending_output_buffers(&mut self) -> IoctlResult<()> {
460 if !self.state.is_output_streaming() {
461 return Ok(());
462 }
463
464 for i in self.pending_output_buffers.drain(..) {
465 let buffer = self.output_buffers.get_mut(i as usize).unwrap();
466 self.backend_session
467 .use_as_output(buffer.index(), &mut buffer.backing)?;
468 }
469
470 Ok(())
471 }
472}
473
474pub trait VideoDecoderBackend: Sized {
479 type Session: VideoDecoderBackendSession;
480
481 fn new_session(&mut self, id: u32) -> IoctlResult<Self::Session>;
483 fn close_session(&mut self, session: Self::Session);
485
486 fn enum_formats(
489 &self,
490 session: &VideoDecoderSession<Self::Session>,
491 direction: QueueDirection,
492 index: u32,
493 ) -> Option<bindings::v4l2_fmtdesc>;
494 fn frame_sizes(&self, pixel_format: u32) -> Option<bindings::v4l2_frmsize_stepwise>;
497
498 fn adjust_format(
503 &self,
504 session: &Self::Session,
505 direction: QueueDirection,
506 format: V4l2MplaneFormat,
507 ) -> V4l2MplaneFormat;
508
509 fn apply_format(
511 &self,
512 session: &mut Self::Session,
513 direction: QueueDirection,
514 format: &V4l2MplaneFormat,
515 );
516}
517
518pub struct VideoDecoder<
519 D: VideoDecoderBackend,
520 Q: VirtioMediaEventQueue,
521 HM: VirtioMediaHostMemoryMapper,
522> {
523 backend: D,
524 event_queue: Q,
525 host_mapper: MmapMappingManager<HM>,
526}
527
528impl<B, Q, HM> VideoDecoder<B, Q, HM>
529where
530 B: VideoDecoderBackend,
531 Q: VirtioMediaEventQueue,
532 HM: VirtioMediaHostMemoryMapper,
533{
534 pub fn new(backend: B, event_queue: Q, host_mapper: HM) -> Self {
535 Self {
536 backend,
537 event_queue,
538 host_mapper: MmapMappingManager::from(host_mapper),
539 }
540 }
541
542 fn try_format(
544 &self,
545 session: &VideoDecoderSession<B::Session>,
546 queue: QueueType,
547 format: bindings::v4l2_format,
548 ) -> IoctlResult<V4l2MplaneFormat> {
549 if queue.class() != QueueClass::VideoMplane {
550 return Err(libc::EINVAL);
551 }
552
553 let pix_mp = unsafe { format.fmt.pix_mp };
555
556 let colorspace = if queue.direction() == QueueDirection::Output {
558 V4l2FormatColorspace {
559 colorspace: Colorspace::n(pix_mp.colorspace)
560 .unwrap_or(session.colorspace.colorspace),
561 xfer_func: XferFunc::n(pix_mp.xfer_func as u32)
562 .unwrap_or(session.colorspace.xfer_func),
563 ycbcr_enc: YCbCrEncoding::n(unsafe { pix_mp.__bindgen_anon_1.ycbcr_enc as u32 })
565 .unwrap_or(session.colorspace.ycbcr_enc),
566 quantization: Quantization::n(pix_mp.quantization as u32)
567 .unwrap_or(session.colorspace.quantization),
568 }
569 } else {
570 session.colorspace
571 };
572
573 let format = V4l2MplaneFormat::from((queue.direction(), pix_mp));
574
575 let format =
576 self.backend
577 .adjust_format(&session.backend_session, queue.direction(), format);
578
579 let mut pix_mp =
580 *<V4l2MplaneFormat as AsRef<bindings::v4l2_pix_format_mplane>>::as_ref(&format);
581
582 colorspace.apply(&mut pix_mp);
583
584 Ok(V4l2MplaneFormat::from((queue.direction(), pix_mp)))
585 }
586}
587
588impl<B, Q, HM, Reader, Writer> VirtioMediaDevice<Reader, Writer> for VideoDecoder<B, Q, HM>
589where
590 B: VideoDecoderBackend,
591 Q: VirtioMediaEventQueue,
592 HM: VirtioMediaHostMemoryMapper,
593 Reader: ReadFromDescriptorChain,
594 Writer: WriteToDescriptorChain,
595{
596 type Session = <Self as VirtioMediaIoctlHandler>::Session;
597
598 fn new_session(&mut self, session_id: u32) -> Result<Self::Session, i32> {
599 let backend_session = self.backend.new_session(session_id)?;
600
601 Ok(VideoDecoderSession {
602 id: session_id,
603 backend_session,
604 state: Default::default(),
605 input_buffers: Default::default(),
606 output_buffers: Default::default(),
607 pending_output_buffers: Default::default(),
608 sequence_cpt: 0,
609 src_change_subscribed: false,
610 eos_subscribed: false,
611 crop_rectangle: CropRectangle::Settable(v4l2r::Rect::new(0, 0, 0, 0)),
612 colorspace: Default::default(),
613 format_change_pending: false,
614 })
615 }
616
617 fn close_session(&mut self, session: Self::Session) {
618 for buffer in session
620 .input_buffers
621 .iter()
622 .chain(session.output_buffers.iter())
623 {
624 if let V4l2PlanesWithBacking::Mmap(planes) =
625 buffer.v4l2_buffer.planes_with_backing_iter()
626 {
627 for plane in planes {
628 self.host_mapper.unregister_buffer(plane.mem_offset());
629 }
630 }
631 }
632
633 self.backend.close_session(session.backend_session);
634 }
635
636 fn do_ioctl(
637 &mut self,
638 session: &mut Self::Session,
639 ioctl: V4l2Ioctl,
640 reader: &mut Reader,
641 writer: &mut Writer,
642 ) -> std::io::Result<()> {
643 virtio_media_dispatch_ioctl(self, session, ioctl, reader, writer)
644 }
645
646 fn do_mmap(
647 &mut self,
648 session: &mut Self::Session,
649 flags: u32,
650 offset: u32,
651 ) -> Result<(u64, u64), i32> {
652 let (buffer, plane_idx) = session
655 .input_buffers
656 .iter()
657 .chain(session.output_buffers.iter())
658 .filter_map(|b| {
659 if let V4l2PlanesWithBacking::Mmap(planes) =
660 b.v4l2_buffer.planes_with_backing_iter()
661 {
662 Some(std::iter::repeat(b).zip(planes.enumerate()))
663 } else {
664 None
665 }
666 })
667 .flatten()
668 .find(|(_, (_, p))| p.mem_offset() == offset)
669 .map(|(b, (i, _))| (b, i))
670 .ok_or(libc::EINVAL)?;
671 let rw = (flags & VIRTIO_MEDIA_MMAP_FLAG_RW) != 0;
672
673 let fd = buffer.backing.fd_for_plane(plane_idx).unwrap();
674
675 self.host_mapper
676 .create_mapping(offset, fd, rw)
677 .map_err(|e| {
678 log::error!(
679 "failed to map MMAP buffer at offset 0x{:x}: {:#}",
680 offset,
681 e
682 );
683 libc::EINVAL
684 })
685 }
686
687 fn do_munmap(&mut self, guest_addr: u64) -> Result<(), i32> {
688 self.host_mapper
689 .remove_mapping(guest_addr)
690 .map(|_| ())
691 .map_err(|_| libc::EINVAL)
692 }
693
694 fn process_events(&mut self, session: &mut Self::Session) -> Result<(), i32> {
695 let has_event = if let Some(event) = session.backend_session.next_event() {
696 match event {
697 VideoDecoderBackendEvent::InputBufferDone {
698 buffer_id: id,
699 error,
700 } => {
701 let Some(buffer) = session.input_buffers.get_mut(id as usize) else {
702 log::error!("no matching OUTPUT buffer with id {} to process event", id);
703 return Ok(());
704 };
705
706 buffer.v4l2_buffer.clear_flags(BufferFlags::QUEUED);
707
708 if error != 0 {
709 buffer.v4l2_buffer.set_flags(BufferFlags::ERROR);
710 }
711
712 self.event_queue
713 .send_event(V4l2Event::DequeueBuffer(DequeueBufferEvent::new(
714 session.id,
715 buffer.v4l2_buffer.clone(),
716 )));
717 }
718 VideoDecoderBackendEvent::StreamFormatChanged => {
719 let stream_params = session.backend_session.stream_params();
720
721 session.crop_rectangle = CropRectangle::FromStream(stream_params.visible_rect);
723
724 if session.src_change_subscribed {
725 self.event_queue
726 .send_event(V4l2Event::Event(SessionEvent::new(
727 session.id,
728 bindings::v4l2_event {
729 type_: bindings::V4L2_EVENT_SOURCE_CHANGE,
730 u: bindings::v4l2_event__bindgen_ty_1 {
731 src_change: bindings::v4l2_event_src_change {
732 changes: SrcChanges::RESOLUTION.bits(),
733 },
734 },
735 ..Default::default()
737 },
738 )))
739 }
740
741 session.format_change_pending = true;
742 }
743 VideoDecoderBackendEvent::FrameCompleted {
744 buffer_id,
745 timestamp,
746 bytes_used,
747 is_last,
748 } => {
749 let Some(buffer) = session.output_buffers.get_mut(buffer_id as usize) else {
750 log::error!(
751 "no matching CAPTURE buffer with id {} to process event",
752 buffer_id
753 );
754 return Ok(());
755 };
756
757 buffer.v4l2_buffer.clear_flags(BufferFlags::QUEUED);
758 buffer.v4l2_buffer.set_flags(BufferFlags::TIMESTAMP_COPY);
759 if is_last {
760 buffer.v4l2_buffer.set_flags(BufferFlags::LAST);
761 }
762 buffer.v4l2_buffer.set_sequence(session.sequence_cpt);
763 session.sequence_cpt += 1;
764 buffer.v4l2_buffer.set_timestamp(timestamp);
765 let first_plane = buffer.v4l2_buffer.get_first_plane_mut();
766 *first_plane.bytesused = bytes_used.first().copied().unwrap_or(0);
767 self.event_queue
768 .send_event(V4l2Event::DequeueBuffer(DequeueBufferEvent::new(
769 session.id,
770 buffer.v4l2_buffer.clone(),
771 )));
772
773 if is_last {
774 if session.format_change_pending {
775 session.format_change_pending = false;
779
780 } else if session.eos_subscribed {
788 self.event_queue
789 .send_event(V4l2Event::Event(SessionEvent::new(
790 session.id,
791 bindings::v4l2_event {
792 type_: bindings::V4L2_EVENT_EOS,
793 ..Default::default()
794 },
795 )))
796 }
797 }
798 }
799 }
800 true
801 } else {
802 false
803 };
804
805 if !has_event {
806 log::warn!("process_events called but no event was pending");
807 }
808
809 Ok(())
810 }
811}
812
813impl<B, Q, HM> VirtioMediaIoctlHandler for VideoDecoder<B, Q, HM>
814where
815 B: VideoDecoderBackend,
816 Q: VirtioMediaEventQueue,
817 HM: VirtioMediaHostMemoryMapper,
818{
819 type Session = VideoDecoderSession<B::Session>;
820
821 fn enum_fmt(
822 &mut self,
823 session: &Self::Session,
824 queue: QueueType,
825 index: u32,
826 ) -> IoctlResult<bindings::v4l2_fmtdesc> {
827 match queue {
828 QueueType::VideoOutputMplane | QueueType::VideoCaptureMplane => {
829 self.backend.enum_formats(session, queue.direction(), index)
830 }
831 _ => None,
832 }
833 .ok_or(libc::EINVAL)
834 }
835
836 fn enum_framesizes(
837 &mut self,
838 _session: &Self::Session,
839 index: u32,
840 pixel_format: u32,
841 ) -> IoctlResult<bindings::v4l2_frmsizeenum> {
842 if index != 0 {
844 return Err(libc::EINVAL);
845 }
846
847 Ok(bindings::v4l2_frmsizeenum {
848 index: 0,
849 pixel_format,
850 type_: bindings::v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_STEPWISE,
851 __bindgen_anon_1: bindings::v4l2_frmsizeenum__bindgen_ty_1 {
852 stepwise: self.backend.frame_sizes(pixel_format).ok_or(libc::EINVAL)?,
853 },
854 ..Default::default()
855 })
856 }
857
858 fn g_fmt(
859 &mut self,
860 session: &Self::Session,
861 queue: QueueType,
862 ) -> IoctlResult<bindings::v4l2_format> {
863 if !matches!(
864 queue,
865 QueueType::VideoOutputMplane | QueueType::VideoCaptureMplane,
866 ) {
867 return Err(libc::EINVAL);
868 }
869
870 let format = session.current_format(queue.direction());
871 let v4l2_format: &bindings::v4l2_format = format.as_ref();
872 Ok(*v4l2_format)
873 }
874
875 fn try_fmt(
876 &mut self,
877 session: &Self::Session,
878 queue: QueueType,
879 format: bindings::v4l2_format,
880 ) -> IoctlResult<bindings::v4l2_format> {
881 let format = self.try_format(session, queue, format)?;
882
883 let v4l2_format: &bindings::v4l2_format = format.as_ref();
884 Ok(*v4l2_format)
885 }
886
887 fn s_fmt(
888 &mut self,
889 session: &mut Self::Session,
890 queue: QueueType,
891 format: bindings::v4l2_format,
892 ) -> IoctlResult<bindings::v4l2_format> {
893 let format = self.try_format(session, queue, format)?;
894
895 self.backend
896 .apply_format(&mut session.backend_session, queue.direction(), &format);
897
898 if queue.direction() == QueueDirection::Output {
900 session.colorspace.colorspace = format.colorspace();
901 session.colorspace.xfer_func = format.xfer_func();
902 session.colorspace.ycbcr_enc = format.ycbcr_enc();
903 session.colorspace.quantization = format.quantization();
904 }
905
906 if let CropRectangle::Settable(rect) = &mut session.crop_rectangle {
908 let (width, height) = format.size();
909 *rect = v4l2r::Rect::new(0, 0, width, height);
910 }
911
912 let v4l2_format: &bindings::v4l2_format = format.as_ref();
913 Ok(*v4l2_format)
914 }
915
916 fn reqbufs(
917 &mut self,
918 session: &mut Self::Session,
919 queue: QueueType,
920 memory: MemoryType,
921 count: u32,
922 flags: MemoryConsistency,
923 ) -> IoctlResult<bindings::v4l2_requestbuffers> {
924 if memory != MemoryType::Mmap {
925 return Err(libc::EINVAL);
926 }
927 let (buffers, count) = match queue {
930 QueueType::VideoOutputMplane => (&mut session.input_buffers, count),
931 QueueType::VideoCaptureMplane => (
932 &mut session.output_buffers,
933 count,
937 ),
938 _ => return Err(libc::EINVAL),
939 };
940
941 if (count as usize) < buffers.len() {
942 for buffer in &buffers[count as usize..] {
943 if let V4l2PlanesWithBacking::Mmap(planes) =
944 buffer.v4l2_buffer.planes_with_backing_iter()
945 {
946 for plane in planes {
947 self.host_mapper.unregister_buffer(plane.mem_offset());
948 }
949 }
950 }
951 buffers.truncate(count as usize);
952 } else {
953 let sizeimage = session
954 .backend_session
955 .current_format(queue.direction())
956 .planes()
957 .first()
958 .ok_or(libc::EINVAL)?
959 .sizeimage;
960 let new_buffers = (buffers.len()..count as usize)
961 .map(|i| {
962 let mmap_offset = self
963 .host_mapper
964 .register_buffer(None, sizeimage)
965 .map_err(|_| libc::EINVAL)?;
966
967 VideoDecoderBuffer::new(
968 queue,
969 i as u32,
970 &[sizeimage as usize],
972 mmap_offset,
973 )
974 .inspect_err(|_| {
975 self.host_mapper.unregister_buffer(mmap_offset);
978 })
979 })
980 .collect::<IoctlResult<Vec<_>>>()?;
981 buffers.extend(new_buffers);
982 }
983
984 session
985 .backend_session
986 .buffers_allocated(queue.direction(), count);
987
988 Ok(bindings::v4l2_requestbuffers {
989 count,
990 type_: queue as u32,
991 memory: memory as u32,
992 capabilities: (BufferCapabilities::SUPPORTS_MMAP
993 | BufferCapabilities::SUPPORTS_ORPHANED_BUFS)
994 .bits(),
995 flags: flags.bits(),
996 reserved: Default::default(),
997 })
998 }
999
1000 fn querybuf(
1001 &mut self,
1002 session: &Self::Session,
1003 queue: QueueType,
1004 index: u32,
1005 ) -> IoctlResult<V4l2Buffer> {
1006 let buffers = match queue {
1007 QueueType::VideoOutputMplane => &session.input_buffers,
1008 QueueType::VideoCaptureMplane => &session.output_buffers,
1009 _ => return Err(libc::EINVAL),
1010 };
1011 let buffer = buffers.get(index as usize).ok_or(libc::EINVAL)?;
1012
1013 Ok(buffer.v4l2_buffer.clone())
1014 }
1015
1016 fn subscribe_event(
1017 &mut self,
1018 session: &mut Self::Session,
1019 event: v4l2r::ioctl::EventType,
1020 _flags: v4l2r::ioctl::SubscribeEventFlags,
1021 ) -> IoctlResult<()> {
1022 match event {
1023 EventType::SourceChange(0) => {
1024 session.src_change_subscribed = true;
1025 Ok(())
1026 }
1027 EventType::Eos => {
1028 session.eos_subscribed = true;
1029 Ok(())
1030 }
1031 _ => Err(libc::EINVAL),
1032 }
1033 }
1034
1035 fn unsubscribe_event(
1037 &mut self,
1038 session: &mut Self::Session,
1039 event: bindings::v4l2_event_subscription,
1040 ) -> IoctlResult<()> {
1041 let mut valid = false;
1042
1043 if event.type_ == 0 || matches!(EventType::try_from(&event), Ok(EventType::SourceChange(0)))
1044 {
1045 session.src_change_subscribed = false;
1046 valid = true;
1047 }
1048 if event.type_ == 0 || matches!(EventType::try_from(&event), Ok(EventType::Eos)) {
1049 session.eos_subscribed = false;
1050 valid = true;
1051 }
1052
1053 if valid {
1054 Ok(())
1055 } else {
1056 Err(libc::EINVAL)
1057 }
1058 }
1059
1060 fn streamon(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
1061 let buffers = match queue {
1062 QueueType::VideoOutputMplane => &session.input_buffers,
1063 QueueType::VideoCaptureMplane => &session.output_buffers,
1064 _ => return Err(libc::EINVAL),
1065 };
1066
1067 let already_running = matches!(session.state, VideoDecoderStreamingState::Running);
1068
1069 if buffers.is_empty() {
1071 return Err(libc::EINVAL);
1072 }
1073
1074 match queue.direction() {
1075 QueueDirection::Output => session.state.input_streamon(),
1076 QueueDirection::Capture => session.state.output_streamon(),
1077 }
1078
1079 session
1080 .backend_session
1081 .streaming_state(queue.direction(), true);
1082
1083 if !already_running && matches!(session.state, VideoDecoderStreamingState::Running) {
1084 }
1086
1087 session.try_send_pending_output_buffers()
1088 }
1089
1090 fn streamoff(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
1091 let buffers = match queue.direction() {
1092 QueueDirection::Output => {
1093 session.state.input_streamoff();
1095
1096 &mut session.input_buffers
1097 }
1098 QueueDirection::Capture => {
1099 session.backend_session.clear_output_buffers()?;
1100 session.state.output_streamoff();
1101 session.pending_output_buffers.clear();
1102
1103 &mut session.output_buffers
1104 }
1105 };
1106
1107 for buffer in buffers {
1108 buffer.v4l2_buffer.clear_flags(BufferFlags::QUEUED);
1109 }
1110
1111 session
1112 .backend_session
1113 .streaming_state(queue.direction(), false);
1114
1115 Ok(())
1116 }
1117
1118 fn g_selection(
1119 &mut self,
1120 session: &Self::Session,
1121 sel_type: SelectionType,
1122 sel_target: SelectionTarget,
1123 ) -> IoctlResult<bindings::v4l2_rect> {
1124 match (sel_type, sel_target) {
1125 (SelectionType::Capture, SelectionTarget::CropBounds) => {
1127 let coded_size = session.backend_session.stream_params().coded_size;
1128 Ok(v4l2r::Rect::new(0, 0, coded_size.0, coded_size.1).into())
1129 }
1130 (
1132 SelectionType::Capture,
1133 SelectionTarget::Crop
1134 | SelectionTarget::CropDefault
1135 | SelectionTarget::ComposeDefault
1136 | SelectionTarget::ComposeBounds
1137 | SelectionTarget::Compose,
1138 ) => {
1139 Ok((*session.crop_rectangle).into())
1141 }
1142 _ => Err(libc::EINVAL),
1143 }
1144 }
1145
1146 fn s_selection(
1147 &mut self,
1148 session: &mut Self::Session,
1149 sel_type: SelectionType,
1150 sel_target: SelectionTarget,
1151 mut sel_rect: bindings::v4l2_rect,
1152 _sel_flags: v4l2r::ioctl::SelectionFlags,
1153 ) -> IoctlResult<bindings::v4l2_rect> {
1154 if !matches!(
1155 (sel_type, sel_target),
1156 (SelectionType::Capture, SelectionTarget::Compose)
1157 ) {
1158 return Err(libc::EINVAL);
1159 }
1160
1161 if let CropRectangle::Settable(rect) = &mut session.crop_rectangle {
1164 let coded_size = session
1165 .backend_session
1166 .current_format(QueueDirection::Capture)
1167 .size();
1168 sel_rect.left = std::cmp::max(0, sel_rect.left);
1169 sel_rect.top = std::cmp::max(0, sel_rect.top);
1170 sel_rect.width = std::cmp::min(coded_size.0, sel_rect.width - sel_rect.left as u32);
1171 sel_rect.height = std::cmp::min(coded_size.0, sel_rect.height - sel_rect.top as u32);
1172
1173 *rect = sel_rect.into();
1174 }
1175
1176 self.g_selection(session, sel_type, sel_target)
1177 }
1178
1179 fn qbuf(
1180 &mut self,
1181 session: &mut Self::Session,
1182 buffer: V4l2Buffer,
1183 _guest_regions: Vec<Vec<SgEntry>>,
1184 ) -> IoctlResult<V4l2Buffer> {
1185 let buffers = match buffer.queue() {
1186 QueueType::VideoOutputMplane => &mut session.input_buffers,
1187 QueueType::VideoCaptureMplane => &mut session.output_buffers,
1188 _ => return Err(libc::EINVAL),
1189 };
1190 let host_buffer = buffers
1191 .get_mut(buffer.index() as usize)
1192 .ok_or(libc::EINVAL)?;
1193
1194 if buffer.memory() != host_buffer.v4l2_buffer.memory() {
1196 return Err(libc::EINVAL);
1197 }
1198
1199 match buffer.queue().direction() {
1200 QueueDirection::Output => {
1201 let v4l2_buffer = &mut host_buffer.v4l2_buffer;
1203 v4l2_buffer.set_field(BufferField::None);
1204 v4l2_buffer.set_timestamp(buffer.timestamp());
1205 let first_plane = buffer.get_first_plane();
1206 *v4l2_buffer.get_first_plane_mut().bytesused = *first_plane.bytesused;
1207 let host_first_plane = v4l2_buffer.get_first_plane_mut();
1208 *host_first_plane.length = *first_plane.length;
1209 *host_first_plane.bytesused = *first_plane.bytesused;
1210 if let Some(data_offset) = host_first_plane.data_offset {
1211 *data_offset = first_plane.data_offset.copied().unwrap_or(0);
1212 }
1213
1214 let bytes_used = {
1215 let first_plane = host_buffer.v4l2_buffer.get_first_plane();
1216 if *first_plane.bytesused == 0 {
1219 *first_plane.length
1220 } else {
1221 *first_plane.bytesused
1222 }
1223 };
1224
1225 session.backend_session.decode(
1226 &host_buffer.backing,
1227 host_buffer.index(),
1228 host_buffer.timestamp(),
1229 bytes_used,
1230 )?;
1231
1232 host_buffer.v4l2_buffer.add_flags(BufferFlags::QUEUED);
1233
1234 Ok(host_buffer.v4l2_buffer.clone())
1235 }
1236 QueueDirection::Capture => {
1237 let v4l2_buffer = &mut host_buffer.v4l2_buffer;
1239 v4l2_buffer.add_flags(BufferFlags::QUEUED);
1240 v4l2_buffer.clear_flags(BufferFlags::LAST);
1241 let host_first_plane = v4l2_buffer.get_first_plane_mut();
1242 let first_plane = buffer.get_first_plane();
1243 *host_first_plane.length = *first_plane.length;
1244 *host_first_plane.bytesused = *first_plane.bytesused;
1245 if let Some(data_offset) = host_first_plane.data_offset {
1246 *data_offset = first_plane.data_offset.copied().unwrap_or(0);
1247 }
1248
1249 let res = v4l2_buffer.clone();
1250
1251 session.pending_output_buffers.push(buffer.index());
1252 session.try_send_pending_output_buffers()?;
1253
1254 Ok(res)
1255 }
1256 }
1257 }
1258
1259 fn try_decoder_cmd(
1260 &mut self,
1261 session: &Self::Session,
1262 cmd: bindings::v4l2_decoder_cmd,
1263 ) -> IoctlResult<bindings::v4l2_decoder_cmd> {
1264 let cmd = DecoderCmd::try_from(cmd).map_err(|_| libc::EINVAL)?;
1265 session.try_decoder_cmd(cmd).map(Into::into)
1266 }
1267
1268 fn decoder_cmd(
1269 &mut self,
1270 session: &mut Self::Session,
1271 cmd: bindings::v4l2_decoder_cmd,
1272 ) -> IoctlResult<bindings::v4l2_decoder_cmd> {
1273 let cmd = DecoderCmd::try_from(cmd).map_err(|_| libc::EINVAL)?;
1274 let cmd = session.try_decoder_cmd(cmd)?;
1275
1276 match cmd {
1278 DecoderCmd::Stop { .. } => {
1279 if !matches!(session.state, VideoDecoderStreamingState::Stopped { .. }) {
1281 session.state = VideoDecoderStreamingState::Stopped {
1282 input_streaming: true,
1283 output_streaming: true,
1284 };
1285
1286 session.backend_session.drain()?;
1288 }
1289 }
1290 DecoderCmd::Start { .. } => {
1291 if let VideoDecoderStreamingState::Stopped {
1293 input_streaming,
1294 output_streaming,
1295 } = &session.state
1296 {
1297 if *input_streaming && *output_streaming {
1298 session.state = VideoDecoderStreamingState::Running;
1299 session
1300 .backend_session
1301 .streaming_state(QueueDirection::Capture, true);
1302 }
1303 session.try_send_pending_output_buffers()?;
1304 }
1305 }
1306 DecoderCmd::Pause { .. } => {
1307 if matches!(session.state, VideoDecoderStreamingState::Running) {
1308 session.state = VideoDecoderStreamingState::Paused;
1309 }
1310 }
1311 DecoderCmd::Resume => {
1312 if matches!(session.state, VideoDecoderStreamingState::Paused) {
1313 session.state = VideoDecoderStreamingState::Running;
1314 }
1315 }
1316 }
1317
1318 Ok(cmd.into())
1319 }
1320}