Skip to main content

virtio_media/devices/
video_decoder.rs

1// Copyright 2024 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
48/// Backing MMAP memory for `VirtioVideoMediaDecoderBuffer`.
49pub 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    /// Backend-specific storage.
61    pub backing: S,
62}
63
64impl<S: VideoDecoderBufferBacking> VideoDecoderBuffer<S> {
65    fn new(
66        queue: QueueType,
67        index: u32,
68        sizes: &[usize],
69        // TODO: need as many offsets as there are planes.
70        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            // SAFETY: every buffer has at least one plane.
79            let mut plane = planes.next().unwrap();
80            plane.set_mem_offset(mmap_offset);
81            *plane.length = sizes[0] as u32;
82        } else {
83            // SAFETY: we have just set the buffer type to MMAP. Reaching this point means a bug in
84            // the code.
85            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/// Events reported by the [`VideoDecoderBackendSession::next_event`] method.
107#[derive(Debug, Clone, PartialEq, Eq)]
108pub enum VideoDecoderBackendEvent {
109    /// Sent whenever the format of the stream has changed. The new format can be read using
110    /// [`VideoDecoderBackendSession::current_format`].
111    StreamFormatChanged,
112    /// Sent whenever an `OUTPUT` buffer is done processing and can be reused.
113    InputBufferDone {
114        buffer_id: u32,
115        /// If error != 0, indicate the error flag on the corresponding v4l2_buffer
116        error: i32,
117    },
118    /// Sent whenever a decoded frame is ready on the `CAPTURE` queue.
119    FrameCompleted {
120        buffer_id: u32,
121        timestamp: bindings::timeval,
122        bytes_used: Vec<u32>,
123        is_last: bool,
124    },
125}
126
127/// Description of the current stream parameters, as parsed from the input.
128#[derive(Clone)]
129pub struct StreamParams {
130    /// Minimum number of output buffers necessary to decode the stream.
131    pub min_output_buffers: u32,
132    /// Coded size of the stream.
133    pub coded_size: (u32, u32),
134    /// Visible rectangle containing the part of the frame to display.
135    pub visible_rect: v4l2r::Rect,
136}
137
138/// Trait for a video decoding session.
139pub trait VideoDecoderBackendSession {
140    type BufferStorage: VideoDecoderBufferBacking;
141
142    /// Decode the encoded stream in `input`, of length `bytes_used`, which corresponds to
143    /// OUTPUT buffer `index`.
144    ///
145    /// `timestamp` is the timestamp of the frame, to be reported in any frame produced from this
146    /// call.
147    fn decode(
148        &mut self,
149        input: &Self::BufferStorage,
150        index: u32,
151        timestamp: bindings::timeval,
152        bytes_used: u32,
153    ) -> IoctlResult<()>;
154
155    /// Use `backing` as the backing storage for output buffer `index`.
156    fn use_as_output(&mut self, index: u32, backing: &mut Self::BufferStorage) -> IoctlResult<()>;
157
158    /// Start draining the decoder pipeline for all buffers still in it.
159    ///
160    /// The backend will report a frame with the `V4L2_BUF_FLAG_LAST` once the drain
161    /// process is completed.
162    fn drain(&mut self) -> IoctlResult<()>;
163
164    /// Remove any output buffer that has been previously added using [`use_as_output`].
165    fn clear_output_buffers(&mut self) -> IoctlResult<()>;
166
167    /// Returns the next pending event if there is one, or `None` if there aren't any.
168    fn next_event(&mut self) -> Option<VideoDecoderBackendEvent>;
169
170    /// Returns the current format set for the given `direction`, in a form suitable as a reply to
171    /// `VIDIOC_G_FMT`.
172    fn current_format(&self, direction: QueueDirection) -> V4l2MplaneFormat;
173
174    /// Returns the stream parameters as read from the input.
175    fn stream_params(&self) -> StreamParams;
176
177    /// Called whenever the decoder device has allocated buffers for a given queue.
178    ///
179    /// This can be useful for some backends that need to know how many buffers they will work
180    /// with. The default implementation does nothing, which should be suitable for backends that
181    /// don't care.
182    fn buffers_allocated(&mut self, _direction: QueueDirection, _num_buffers: u32) {}
183
184    /// Returns a file descriptor that signals `POLLIN` whenever an event is pending and can be
185    /// read using [`next_event`], or `None` if the backend does not support this.
186    fn poll_fd(&self) -> Option<BorrowedFd<'_>> {
187        None
188    }
189
190    /// Optional hook called whenever the streaming state of a queue changes. Some backends may
191    /// need this information to operate properly.
192    fn streaming_state(&mut self, _direction: QueueDirection, _streaming: bool) {}
193
194    /// Optional hook called by the decoder to signal it has processed a pausing event
195    /// sent by the backend.
196    ///
197    /// Pausing event are currently limited to [`VideoDecoderBackendEvent::StreamFormatChanged`].
198    /// Whenever the resolution changes, the backend must stop processing until the decoder has
199    /// adapted its conditions for decoding to resume (e.g. CAPTURE buffers of the proper size and
200    /// format have been allocated).
201    fn resume(&mut self) {}
202}
203
204/// State of a session.
205#[derive(Debug)]
206enum VideoDecoderStreamingState {
207    /// Initial state, and state after a `STOP` command or a successful drain. Contains the
208    /// state of both streaming queues.
209    Stopped {
210        input_streaming: bool,
211        output_streaming: bool,
212    },
213    /// State when both queues are streaming.
214    Running,
215    /// State when a `PAUSE` command has been received. Both queues are streaming in this state.
216    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 we switch to a state where both queues are streaming, then the device is
237                // running.
238                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 we switch to a state where both queues are streaming, then the device is
269                // running.
270                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
305/// Management of the crop rectangle.
306///
307/// There are two ways this parameter can be set:
308///
309/// * Manually by the client, by calling `VIDIOC_S_SELECTION` with `V4L2_SEL_TGT_COMPOSE`. This has
310///   an effect only before the first resolution change event is emitted, and is the only way to
311///   properly set the crop rectangle for codecs/hardware that don't support DRC detection.
312///
313/// * From the information contained in the stream, signaled via a
314///   [`VideoDecoderBackendEvent::StreamFormatChanged`] event. Once this event has been emitted, the
315///   crop rectangle is fixed and determined by the stream.
316enum CropRectangle {
317    /// Crop rectangle has not been determined from the stream yet and can be set by the client.
318    Settable(v4l2r::Rect),
319    /// Crop rectangle has been determined from the stream and cannot be modified.
320    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/// Struct containing validated colorspace information for a format.
335#[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    /// Apply the colorspace information of this object to `pix_mp`.
356    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    /// Indices of CAPTURE buffers that are queued but not send to the backend yet because the
374    /// decoder is not running.
375    pending_output_buffers: Vec<u32>,
376
377    sequence_cpt: u32,
378
379    /// Whether the input source change event has been subscribed to by the driver. If `true` then
380    /// the device will emit resolution change events.
381    src_change_subscribed: bool,
382    /// Whether the EOS event has been subscribed to by the driver. If `true` then the device will
383    /// emit EOS events.
384    eos_subscribed: bool,
385
386    crop_rectangle: CropRectangle,
387
388    /// Current colorspace information of the format.
389    colorspace: V4l2FormatColorspace,
390
391    /// Adapter-specific data.
392    backend_session: S,
393
394    /// The buffer-flag LAST has at least two different interpretations: Generally
395    /// it signals the end of the stream, however, during a format change sequence it
396    /// marks the transition point from the previous to the new sequence. We keep
397    /// track of pending format changes here to avoid signalling EOS if the incoming
398    /// buffer's LAST flag is just marking the completion of the format change.
399    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    /// Returns the current format for `direction`.
410    ///
411    /// This is essentially like calling the backend's corresponding
412    /// [`VideoDecoderBackendSession::current_format`] method, but also applies the colorspace
413    /// information potentially set by the user.
414    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                    // The V4L2 documentation says this should return `EPERM`, but v4l2-compliance
432                    // requires `EINVAL`...
433                    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                    // The V4L2 documentation says this should return `EPERM`, but v4l2-compliance
442                    // requires `EINVAL`...
443                    VideoDecoderStreamingState::Stopped { .. } => Err(libc::EINVAL),
444                    VideoDecoderStreamingState::Paused | VideoDecoderStreamingState::Running => {
445                        Ok(DecoderCmd::resume())
446                    }
447                }
448            }
449        }
450    }
451
452    /// Send all the output buffers that are pending to the backend, if the decoder is running.
453    ///
454    /// In the adapter backend, if we receive buffers this means both queues are streaming - IOW we
455    /// can queue them as soon as the condition is good.
456    ///
457    /// In the decoder device, we need to keep them until both queues are streaming. Same applies
458    /// to input buffers BTW.
459    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
474/// Trait for actual implementations of video decoding, to be used with [`VideoDecoder`].
475///
476/// [`VideoDecoder`] takes care of (mostly) abstracting V4L2 away ; implementors of this trait are
477/// the ones that provide the actual video decoding service.
478pub trait VideoDecoderBackend: Sized {
479    type Session: VideoDecoderBackendSession;
480
481    /// Create a new session with the provided `id`.
482    fn new_session(&mut self, id: u32) -> IoctlResult<Self::Session>;
483    /// Close and destroy `session`.
484    fn close_session(&mut self, session: Self::Session);
485
486    /// Returns the format at `index` for the given queue `direction`, or None if `index` is out of
487    /// bounds.
488    fn enum_formats(
489        &self,
490        session: &VideoDecoderSession<Self::Session>,
491        direction: QueueDirection,
492        index: u32,
493    ) -> Option<bindings::v4l2_fmtdesc>;
494    /// Returns the supported frame sizes for `pixel_format`, or None if the format is not
495    /// supported.
496    fn frame_sizes(&self, pixel_format: u32) -> Option<bindings::v4l2_frmsize_stepwise>;
497
498    /// Adjust `format` to make it applicable to the queue with the given `direction` for the current `session`.
499    ///
500    /// This method doesn't fail, implementations must return the closest acceptable format that
501    /// can be applied unchanged with [`Self::apply_format`].
502    fn adjust_format(
503        &self,
504        session: &Self::Session,
505        direction: QueueDirection,
506        format: V4l2MplaneFormat,
507    ) -> V4l2MplaneFormat;
508
509    /// Applies `format` to the queue of the given `direction`. The format is adjusted if needed.
510    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    /// Validate `format` for `queue` and return the adjusted format.
543    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        // SAFETY: safe because we have just confirmed the queue type is mplane.
554        let pix_mp = unsafe { format.fmt.pix_mp };
555
556        // Process the colorspace now so we can restore it after applying the backend adjustment.
557        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                // TODO: safe because...
564                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        // Unregister all MMAP buffers.
619        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        // Search for a MMAP plane with the right offset.
653        // TODO: O(n), not critical but not great either.
654        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                    // The crop rectangle is now determined by the stream and cannot be changed.
722                    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                                    // TODO: fill pending, sequence, and timestamp.
736                                    ..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                            // The end of the format-change sequence is also
776                            // signaled via a buffer marked "LAST", but this is
777                            // not to be interpreted as the end of the stream.
778                            session.format_change_pending = false;
779
780                            // TODO: If a client (probably unaware of or unable
781                            // to deal with dynamic format changes) attempts to
782                            // DQBUF buffers after the LAST marker (without going
783                            // through the dynamic resolution change sequence),
784                            // we are supposed to return -EPIPE according to
785                            // https://docs.kernel.org/userspace-api/media/v4l/dev-decoder.html
786                            // Section 4.5.1.9 Dynamic Resolution Change
787                        } 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        // We only support step-wise frame sizes.
843        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        //  Setting the colorspace information on the `OUTPUT` queue sets it for both queues.
899        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 the crop rectangle is still settable, adjust it to the size of the new format.
907        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        // TODO: fail if streaming?
928
929        let (buffers, count) = match queue {
930            QueueType::VideoOutputMplane => (&mut session.input_buffers, count),
931            QueueType::VideoCaptureMplane => (
932                &mut session.output_buffers,
933                // TODO: no no, we need to reallocate all the buffers if the queue parameters have
934                // changed... especially if the new format won't fit into the old buffers!
935                // count.max(session.backend_session.stream_params().min_output_buffers),
936                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                        // TODO: only single-planar formats supported.
971                        &[sizeimage as usize],
972                        mmap_offset,
973                    )
974                    .inspect_err(|_| {
975                        // TODO: no, we need to unregister all the buffers and restore the
976                        // previous state?
977                        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    // TODO: parse the event and use an enum value to signal ALL or single event?
1036    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        // Cannot stream if no buffers allocated.
1070        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            // TODO: start queueing pending buffers?
1085        }
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                // TODO: something to do on the backend?
1094                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            // Coded resolution of the stream.
1126            (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            // Visible area of CAPTURE buffers.
1131            (
1132                SelectionType::Capture,
1133                SelectionTarget::Crop
1134                | SelectionTarget::CropDefault
1135                | SelectionTarget::ComposeDefault
1136                | SelectionTarget::ComposeBounds
1137                | SelectionTarget::Compose,
1138            ) => {
1139                //Ok(session.backend_session.stream_params().visible_rect.into())
1140                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 the crop rectangle is still settable, allow its modification within the bounds of the
1162        // coded resolution.
1163        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        // Check that the buffer's memory type corresponds to the one requested during allocation.
1195        if buffer.memory() != host_buffer.v4l2_buffer.memory() {
1196            return Err(libc::EINVAL);
1197        }
1198
1199        match buffer.queue().direction() {
1200            QueueDirection::Output => {
1201                // Update buffer state
1202                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                    // V4L2's spec mentions that if `bytes_used == 0` then the whole buffer is considered to be
1217                    // used.
1218                    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                // Update buffer state
1238                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        // The command is valid, apply it.
1277        match cmd {
1278            DecoderCmd::Stop { .. } => {
1279                // Switch to stopped state if we aren't already there.
1280                if !matches!(session.state, VideoDecoderStreamingState::Stopped { .. }) {
1281                    session.state = VideoDecoderStreamingState::Stopped {
1282                        input_streaming: true,
1283                        output_streaming: true,
1284                    };
1285
1286                    // Start the `DRAIN` sequence.
1287                    session.backend_session.drain()?;
1288                }
1289            }
1290            DecoderCmd::Start { .. } => {
1291                // Restart the decoder if we were in the stopped state with both queues streaming.
1292                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}