xref: /aosp_15_r20/external/crosvm/devices/src/virtio/video/ffmpeg.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::error::Error;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::fmt::Display;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::fmt::Formatter;
8*bb4ee6a4SAndroid Build Coastguard Worker 
9*bb4ee6a4SAndroid Build Coastguard Worker use base::MappedRegion;
10*bb4ee6a4SAndroid Build Coastguard Worker use base::MemoryMappingArena;
11*bb4ee6a4SAndroid Build Coastguard Worker use base::MmapError;
12*bb4ee6a4SAndroid Build Coastguard Worker use ffmpeg::avcodec::AvBuffer;
13*bb4ee6a4SAndroid Build Coastguard Worker use ffmpeg::avcodec::AvBufferSource;
14*bb4ee6a4SAndroid Build Coastguard Worker use ffmpeg::avcodec::AvError;
15*bb4ee6a4SAndroid Build Coastguard Worker use ffmpeg::avcodec::AvFrame;
16*bb4ee6a4SAndroid Build Coastguard Worker use ffmpeg::avcodec::AvFrameError;
17*bb4ee6a4SAndroid Build Coastguard Worker use ffmpeg::avcodec::AvPixelFormat;
18*bb4ee6a4SAndroid Build Coastguard Worker use ffmpeg::avcodec::Dimensions;
19*bb4ee6a4SAndroid Build Coastguard Worker use ffmpeg::avcodec::PlaneDescriptor;
20*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error as ThisError;
21*bb4ee6a4SAndroid Build Coastguard Worker 
22*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::video::format::Format;
23*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::video::resource::BufferHandle;
24*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::video::resource::GuestResource;
25*bb4ee6a4SAndroid Build Coastguard Worker 
26*bb4ee6a4SAndroid Build Coastguard Worker /// A simple wrapper that turns a [`MemoryMappingArena`] into an [`AvBufferSource`].
27*bb4ee6a4SAndroid Build Coastguard Worker ///
28*bb4ee6a4SAndroid Build Coastguard Worker /// **Note:** Only use this if you can reasonably assume the mapped memory won't be written to from
29*bb4ee6a4SAndroid Build Coastguard Worker /// another thread or the VM! The reason [`AvBufferSource`] is not directly implemented on
30*bb4ee6a4SAndroid Build Coastguard Worker /// [`MemoryMappingArena`] is exactly due to this unsafety: If the guest or someone else writes to
31*bb4ee6a4SAndroid Build Coastguard Worker /// the buffer FFmpeg is currently reading from or writing to, undefined behavior might occur. This
32*bb4ee6a4SAndroid Build Coastguard Worker /// struct acts as an opt-in mechanism for this potential unsafety.
33*bb4ee6a4SAndroid Build Coastguard Worker pub struct MemoryMappingAvBufferSource(MemoryMappingArena);
34*bb4ee6a4SAndroid Build Coastguard Worker 
35*bb4ee6a4SAndroid Build Coastguard Worker impl From<MemoryMappingArena> for MemoryMappingAvBufferSource {
from(inner: MemoryMappingArena) -> Self36*bb4ee6a4SAndroid Build Coastguard Worker     fn from(inner: MemoryMappingArena) -> Self {
37*bb4ee6a4SAndroid Build Coastguard Worker         Self(inner)
38*bb4ee6a4SAndroid Build Coastguard Worker     }
39*bb4ee6a4SAndroid Build Coastguard Worker }
40*bb4ee6a4SAndroid Build Coastguard Worker impl AvBufferSource for MemoryMappingAvBufferSource {
as_ptr(&self) -> *const u841*bb4ee6a4SAndroid Build Coastguard Worker     fn as_ptr(&self) -> *const u8 {
42*bb4ee6a4SAndroid Build Coastguard Worker         self.0.as_ptr() as _
43*bb4ee6a4SAndroid Build Coastguard Worker     }
44*bb4ee6a4SAndroid Build Coastguard Worker 
len(&self) -> usize45*bb4ee6a4SAndroid Build Coastguard Worker     fn len(&self) -> usize {
46*bb4ee6a4SAndroid Build Coastguard Worker         self.0.size()
47*bb4ee6a4SAndroid Build Coastguard Worker     }
48*bb4ee6a4SAndroid Build Coastguard Worker 
is_empty(&self) -> bool49*bb4ee6a4SAndroid Build Coastguard Worker     fn is_empty(&self) -> bool {
50*bb4ee6a4SAndroid Build Coastguard Worker         self.len() == 0
51*bb4ee6a4SAndroid Build Coastguard Worker     }
52*bb4ee6a4SAndroid Build Coastguard Worker }
53*bb4ee6a4SAndroid Build Coastguard Worker 
54*bb4ee6a4SAndroid Build Coastguard Worker pub trait TryAsAvFrameExt {
55*bb4ee6a4SAndroid Build Coastguard Worker     type BufferSource;
56*bb4ee6a4SAndroid Build Coastguard Worker     type Error: Error;
57*bb4ee6a4SAndroid Build Coastguard Worker 
58*bb4ee6a4SAndroid Build Coastguard Worker     /// Try to create an [`AvFrame`] from `self`.
59*bb4ee6a4SAndroid Build Coastguard Worker     ///
60*bb4ee6a4SAndroid Build Coastguard Worker     /// `wrapper` is a constructor for `T` that wraps `Self::BufferSource` and implement the
61*bb4ee6a4SAndroid Build Coastguard Worker     /// `AvBufferSource` functionality. This can be used to provide a custom destructor
62*bb4ee6a4SAndroid Build Coastguard Worker     /// implementation: for example, sending a virtio message signaling the buffer source is no
63*bb4ee6a4SAndroid Build Coastguard Worker     /// longer used and it can be recycled by the guest.
64*bb4ee6a4SAndroid Build Coastguard Worker     ///
65*bb4ee6a4SAndroid Build Coastguard Worker     /// Note that `Self::BufferSource` might not always implement `AvBufferSource`. For instance,
66*bb4ee6a4SAndroid Build Coastguard Worker     /// it's not guaranteed that a `MemoryMappingArena` created from `GuestResource` will be always
67*bb4ee6a4SAndroid Build Coastguard Worker     /// immutable, and the backend need to explicit acknowledge the potential unsoundness by
68*bb4ee6a4SAndroid Build Coastguard Worker     /// wrapping it in [`MemoryMappingAvBufferSource`].
try_as_av_frame<T: AvBufferSource + 'static>( &self, wrapper: impl FnOnce(Self::BufferSource) -> T, ) -> Result<AvFrame, Self::Error>69*bb4ee6a4SAndroid Build Coastguard Worker     fn try_as_av_frame<T: AvBufferSource + 'static>(
70*bb4ee6a4SAndroid Build Coastguard Worker         &self,
71*bb4ee6a4SAndroid Build Coastguard Worker         wrapper: impl FnOnce(Self::BufferSource) -> T,
72*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<AvFrame, Self::Error>;
73*bb4ee6a4SAndroid Build Coastguard Worker }
74*bb4ee6a4SAndroid Build Coastguard Worker 
75*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, ThisError)]
76*bb4ee6a4SAndroid Build Coastguard Worker pub enum GuestResourceToAvFrameError {
77*bb4ee6a4SAndroid Build Coastguard Worker     #[error("error while creating the AvFrame: {0}")]
78*bb4ee6a4SAndroid Build Coastguard Worker     AvFrameError(#[from] AvFrameError),
79*bb4ee6a4SAndroid Build Coastguard Worker     #[error("cannot mmap guest resource: {0}")]
80*bb4ee6a4SAndroid Build Coastguard Worker     MappingResource(#[from] MmapError),
81*bb4ee6a4SAndroid Build Coastguard Worker     #[error("virtio resource format is not convertible to AvPixelFormat")]
82*bb4ee6a4SAndroid Build Coastguard Worker     InvalidFormat,
83*bb4ee6a4SAndroid Build Coastguard Worker     #[error("out of memory while allocating AVBuffer")]
84*bb4ee6a4SAndroid Build Coastguard Worker     CannotAllocateAvBuffer,
85*bb4ee6a4SAndroid Build Coastguard Worker }
86*bb4ee6a4SAndroid Build Coastguard Worker 
87*bb4ee6a4SAndroid Build Coastguard Worker impl From<AvError> for GuestResourceToAvFrameError {
from(e: AvError) -> Self88*bb4ee6a4SAndroid Build Coastguard Worker     fn from(e: AvError) -> Self {
89*bb4ee6a4SAndroid Build Coastguard Worker         GuestResourceToAvFrameError::AvFrameError(AvFrameError::AvError(e))
90*bb4ee6a4SAndroid Build Coastguard Worker     }
91*bb4ee6a4SAndroid Build Coastguard Worker }
92*bb4ee6a4SAndroid Build Coastguard Worker 
93*bb4ee6a4SAndroid Build Coastguard Worker impl TryAsAvFrameExt for GuestResource {
94*bb4ee6a4SAndroid Build Coastguard Worker     type BufferSource = MemoryMappingArena;
95*bb4ee6a4SAndroid Build Coastguard Worker     type Error = GuestResourceToAvFrameError;
96*bb4ee6a4SAndroid Build Coastguard Worker 
try_as_av_frame<T: AvBufferSource + 'static>( &self, wrapper: impl FnOnce(MemoryMappingArena) -> T, ) -> Result<AvFrame, Self::Error>97*bb4ee6a4SAndroid Build Coastguard Worker     fn try_as_av_frame<T: AvBufferSource + 'static>(
98*bb4ee6a4SAndroid Build Coastguard Worker         &self,
99*bb4ee6a4SAndroid Build Coastguard Worker         wrapper: impl FnOnce(MemoryMappingArena) -> T,
100*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<AvFrame, Self::Error> {
101*bb4ee6a4SAndroid Build Coastguard Worker         let mut builder = AvFrame::builder()?;
102*bb4ee6a4SAndroid Build Coastguard Worker         builder.set_dimensions(Dimensions {
103*bb4ee6a4SAndroid Build Coastguard Worker             width: self.width,
104*bb4ee6a4SAndroid Build Coastguard Worker             height: self.height,
105*bb4ee6a4SAndroid Build Coastguard Worker         })?;
106*bb4ee6a4SAndroid Build Coastguard Worker         let format = self
107*bb4ee6a4SAndroid Build Coastguard Worker             .format
108*bb4ee6a4SAndroid Build Coastguard Worker             .try_into()
109*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(|_| GuestResourceToAvFrameError::InvalidFormat)?;
110*bb4ee6a4SAndroid Build Coastguard Worker         builder.set_format(format)?;
111*bb4ee6a4SAndroid Build Coastguard Worker         let planes = &self.planes;
112*bb4ee6a4SAndroid Build Coastguard Worker         // We have at least one plane, so we can unwrap below.
113*bb4ee6a4SAndroid Build Coastguard Worker         let len = format.plane_sizes(planes.iter().map(|p| p.stride as _), self.height)?;
114*bb4ee6a4SAndroid Build Coastguard Worker         let start = planes.iter().map(|p| p.offset).min().unwrap();
115*bb4ee6a4SAndroid Build Coastguard Worker         let end = planes
116*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
117*bb4ee6a4SAndroid Build Coastguard Worker             .enumerate()
118*bb4ee6a4SAndroid Build Coastguard Worker             .map(|(i, p)| p.offset + len[i])
119*bb4ee6a4SAndroid Build Coastguard Worker             .max()
120*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
121*bb4ee6a4SAndroid Build Coastguard Worker         let mapping = self.handle.get_mapping(start, end - start)?;
122*bb4ee6a4SAndroid Build Coastguard Worker         Ok(builder.build_owned(
123*bb4ee6a4SAndroid Build Coastguard Worker             [AvBuffer::new(wrapper(mapping))
124*bb4ee6a4SAndroid Build Coastguard Worker                 .ok_or(GuestResourceToAvFrameError::CannotAllocateAvBuffer)?],
125*bb4ee6a4SAndroid Build Coastguard Worker             planes.iter().map(|p| PlaneDescriptor {
126*bb4ee6a4SAndroid Build Coastguard Worker                 buffer_index: 0,
127*bb4ee6a4SAndroid Build Coastguard Worker                 offset: p.offset - start,
128*bb4ee6a4SAndroid Build Coastguard Worker                 stride: p.stride,
129*bb4ee6a4SAndroid Build Coastguard Worker             }),
130*bb4ee6a4SAndroid Build Coastguard Worker         )?)
131*bb4ee6a4SAndroid Build Coastguard Worker     }
132*bb4ee6a4SAndroid Build Coastguard Worker }
133*bb4ee6a4SAndroid Build Coastguard Worker 
134*bb4ee6a4SAndroid Build Coastguard Worker /// The error returned when converting between `AvPixelFormat` and `Format` and there's no
135*bb4ee6a4SAndroid Build Coastguard Worker /// applicable format.
136*bb4ee6a4SAndroid Build Coastguard Worker // The empty field prevents constructing this and allows extending it in the future.
137*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)]
138*bb4ee6a4SAndroid Build Coastguard Worker pub struct TryFromFormatError(());
139*bb4ee6a4SAndroid Build Coastguard Worker 
140*bb4ee6a4SAndroid Build Coastguard Worker impl Display for TryFromFormatError {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result141*bb4ee6a4SAndroid Build Coastguard Worker     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
142*bb4ee6a4SAndroid Build Coastguard Worker         write!(
143*bb4ee6a4SAndroid Build Coastguard Worker             f,
144*bb4ee6a4SAndroid Build Coastguard Worker             "No matching format to convert between AvPixelFormat and Format"
145*bb4ee6a4SAndroid Build Coastguard Worker         )
146*bb4ee6a4SAndroid Build Coastguard Worker     }
147*bb4ee6a4SAndroid Build Coastguard Worker }
148*bb4ee6a4SAndroid Build Coastguard Worker 
149*bb4ee6a4SAndroid Build Coastguard Worker impl Error for TryFromFormatError {}
150*bb4ee6a4SAndroid Build Coastguard Worker 
151*bb4ee6a4SAndroid Build Coastguard Worker impl TryFrom<Format> for AvPixelFormat {
152*bb4ee6a4SAndroid Build Coastguard Worker     type Error = TryFromFormatError;
153*bb4ee6a4SAndroid Build Coastguard Worker 
try_from(value: Format) -> Result<Self, Self::Error>154*bb4ee6a4SAndroid Build Coastguard Worker     fn try_from(value: Format) -> Result<Self, Self::Error> {
155*bb4ee6a4SAndroid Build Coastguard Worker         use ffmpeg::*;
156*bb4ee6a4SAndroid Build Coastguard Worker         AvPixelFormat::try_from(match value {
157*bb4ee6a4SAndroid Build Coastguard Worker             Format::NV12 => AVPixelFormat_AV_PIX_FMT_NV12,
158*bb4ee6a4SAndroid Build Coastguard Worker             Format::YUV420 => AVPixelFormat_AV_PIX_FMT_YUV420P,
159*bb4ee6a4SAndroid Build Coastguard Worker             _ => return Err(TryFromFormatError(())),
160*bb4ee6a4SAndroid Build Coastguard Worker         })
161*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(|_|
162*bb4ee6a4SAndroid Build Coastguard Worker             // The error case should never happen as long as we use valid constant values, but
163*bb4ee6a4SAndroid Build Coastguard Worker             // don't panic in case something goes wrong.
164*bb4ee6a4SAndroid Build Coastguard Worker             TryFromFormatError(()))
165*bb4ee6a4SAndroid Build Coastguard Worker     }
166*bb4ee6a4SAndroid Build Coastguard Worker }
167*bb4ee6a4SAndroid Build Coastguard Worker 
168*bb4ee6a4SAndroid Build Coastguard Worker impl TryFrom<AvPixelFormat> for Format {
169*bb4ee6a4SAndroid Build Coastguard Worker     type Error = TryFromFormatError;
170*bb4ee6a4SAndroid Build Coastguard Worker 
try_from(fmt: AvPixelFormat) -> Result<Self, Self::Error>171*bb4ee6a4SAndroid Build Coastguard Worker     fn try_from(fmt: AvPixelFormat) -> Result<Self, Self::Error> {
172*bb4ee6a4SAndroid Build Coastguard Worker         // https://github.com/rust-lang/rust/issues/39371 Lint wrongly warns the consumer
173*bb4ee6a4SAndroid Build Coastguard Worker         #![allow(non_upper_case_globals)]
174*bb4ee6a4SAndroid Build Coastguard Worker         use ffmpeg::*;
175*bb4ee6a4SAndroid Build Coastguard Worker         Ok(match fmt.pix_fmt() {
176*bb4ee6a4SAndroid Build Coastguard Worker             AVPixelFormat_AV_PIX_FMT_NV12 => Format::NV12,
177*bb4ee6a4SAndroid Build Coastguard Worker             AVPixelFormat_AV_PIX_FMT_YUV420P => Format::YUV420,
178*bb4ee6a4SAndroid Build Coastguard Worker             _ => return Err(TryFromFormatError(())),
179*bb4ee6a4SAndroid Build Coastguard Worker         })
180*bb4ee6a4SAndroid Build Coastguard Worker     }
181*bb4ee6a4SAndroid Build Coastguard Worker }
182