devices/virtio/media/
decoder_adapter.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
5//! An adapter from the virtio-media protocol to the video devices originally used with
6//! virtio-video.
7//!
8//! This allows to reuse already-existing crosvm virtio-video devices with
9//! virtio-media.
10
11use 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
45/// Use 1 MB input buffers by default. This should probably be resolution-dependent.
46const INPUT_BUFFER_SIZE: u32 = 1024 * 1024;
47
48/// Returns the V4L2 `(bytesperline, sizeimage)` for the given `format` and `coded_size`.
49fn 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    // Plane backing memory, for MMAP buffers only.
90    shm: Vec<SharedMemory>,
91    // Switched to `true` once the buffer's backing memory has been registered with
92    // `use_output_buffer`.
93    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            // SAFETY: the shm'd fd is valid and `BorrowedFd` ensures its use won't outlive us.
117            unsafe { BorrowedFd::borrow_raw(shm.descriptor.as_raw_descriptor())})
118    }
119}
120
121enum EosBuffer {
122    /// No EOS buffer queued yet and no EOS pending.
123    None,
124    /// EOS buffer is available, and no EOS pending.
125    Available(u32),
126    /// EOS is pending but we have no buffer queued yet.
127    Awaiting,
128}
129
130pub struct VirtioVideoAdapterSession<D: DecoderBackend> {
131    /// Session ID.
132    id: u32,
133
134    /// The backend session can only be created once we know the input format.
135    backend_session: Option<D::Session>,
136
137    /// Proxy poller. We need this because this backend creates the actual session with a delay,
138    /// but the device needs the session FD as soon as it is created.
139    poller: WaitContext<u32>,
140
141    /// Shared reference to the device backend. Required so we can create the session lazily.
142    backend: Arc<Mutex<D>>,
143
144    input_format: Format,
145    output_format: Format,
146    /// Coded size currently set for the CAPTURE buffers.
147    coded_size: (u32, u32),
148    /// Stream parameters, as obtained from the stream itself.
149    stream_params: StreamParams,
150
151    /// Whether the initial DRC event has been received. The backend will ignore CAPTURE buffers
152    /// queued before that.
153    initial_drc_received: bool,
154    /// Whether the `set_output_parameters` of the backend needs to be called before a CAPTURE
155    /// buffer is sent.
156    need_set_output_params: bool,
157
158    /// Indices of CAPTURE buffers that are queued but not send to the backend.
159    pending_output_buffers: Vec<(u32, Option<GuestResource>)>,
160
161    /// Index of the capture buffer we kept in order to signal EOS.
162    eos_capture_buffer: EosBuffer,
163
164    /// Number of output buffers that have been allocated, as reported by the decoder device.
165    ///
166    /// This is required to call `set_output_parameters` on the virtio-video session.
167    num_output_buffers: usize,
168}
169
170impl<D: DecoderBackend> VirtioVideoAdapterSession<D> {
171    /// Get the currently existing backend session if it exists, or create it using the current
172    /// input format if it doesn't.
173    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        // We cannot send output buffers until the initial DRC event is received.
203        if !self.initial_drc_received {
204            return Ok(());
205        }
206
207        let _ = self.get_or_create_session();
208        // Will always succeed before `get_or_create_session` has been called. Ideally we would use
209        // its result directly, but this borrows a mutable reference to `self` which would prevent
210        // other borrows later in this method.
211        let Some(backend_session) = &mut self.backend_session else {
212            unreachable!()
213        };
214
215        // Leave early if we have no pending output buffers. This ensures we only set the
216        // parameters right before the first buffer is queued.
217        if self.pending_output_buffers.is_empty() {
218            return Ok(());
219        }
220
221        // Set the output parameters if this is the first CAPTURE buffer we queue after a
222        // resolution change event.
223        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                        // Add one extra buffer to keep one on the side in order to signal EOS.
320                        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                    // We can queue pending picture buffers now.
334                    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                    // Regardless of the result, the flush has completed.
366                    // Use the buffer we kept aside for signaling EOS, or wait until the next
367                    // buffer is queued so we can use it for that purpose.
368                    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        // SAFETY: safe because WaitContext has a valid FD and BorrowedFd ensures its use doesn't
399        // outlive us.
400        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                // TODO: mmm how do we do that?
436                // Answer: we don't! We just return a buffer event with the LAST flag set, and the
437                // higher-level event handler takes care of sending eos!
438                //
439                // ... and how do we trigger the event for that buffer?
440                //
441                // self.send_eos(self, v4l2_buffer.index());
442                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        // If the received pixel format is valid, find the format in our capabilities that matches,
624        // otherwise fall back to the first format.
625        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            // There are no formats defined at all for the device, so return a bogus one like a
630            // V4L2 device would do.
631            return V4l2MplaneFormat::from((direction, Default::default()));
632        };
633
634        // Now check that the requested resolution is within the supported range, or fallback to
635        // an arbitrary one.
636        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        // CAPTURE resolution cannot be lower than OUTPUT one.
660        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        // We only support one plane per buffer for now.
666        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                // Setting the resolution manually affects the stream parameters.
709                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}