devices/virtio/video/
ffmpeg.rs1use std::error::Error;
6use std::fmt::Display;
7use std::fmt::Formatter;
8
9use base::MappedRegion;
10use base::MemoryMappingArena;
11use base::MmapError;
12use ffmpeg::avcodec::AvBuffer;
13use ffmpeg::avcodec::AvBufferSource;
14use ffmpeg::avcodec::AvError;
15use ffmpeg::avcodec::AvFrame;
16use ffmpeg::avcodec::AvFrameError;
17use ffmpeg::avcodec::AvPixelFormat;
18use ffmpeg::avcodec::Dimensions;
19use ffmpeg::avcodec::PlaneDescriptor;
20use thiserror::Error as ThisError;
21
22use crate::virtio::video::format::Format;
23use crate::virtio::video::resource::BufferHandle;
24use crate::virtio::video::resource::GuestResource;
25
26pub struct MemoryMappingAvBufferSource(MemoryMappingArena);
34
35impl From<MemoryMappingArena> for MemoryMappingAvBufferSource {
36 fn from(inner: MemoryMappingArena) -> Self {
37 Self(inner)
38 }
39}
40impl AvBufferSource for MemoryMappingAvBufferSource {
41 fn as_ptr(&self) -> *const u8 {
42 self.0.as_ptr() as _
43 }
44
45 fn len(&self) -> usize {
46 self.0.size()
47 }
48
49 fn is_empty(&self) -> bool {
50 self.len() == 0
51 }
52}
53
54pub trait TryAsAvFrameExt {
55 type BufferSource;
56 type Error: Error;
57
58 fn try_as_av_frame<T: AvBufferSource + 'static>(
70 &self,
71 wrapper: impl FnOnce(Self::BufferSource) -> T,
72 ) -> Result<AvFrame, Self::Error>;
73}
74
75#[derive(Debug, ThisError)]
76pub enum GuestResourceToAvFrameError {
77 #[error("error while creating the AvFrame: {0}")]
78 AvFrameError(#[from] AvFrameError),
79 #[error("cannot mmap guest resource: {0}")]
80 MappingResource(#[from] MmapError),
81 #[error("virtio resource format is not convertible to AvPixelFormat")]
82 InvalidFormat,
83 #[error("out of memory while allocating AVBuffer")]
84 CannotAllocateAvBuffer,
85}
86
87impl From<AvError> for GuestResourceToAvFrameError {
88 fn from(e: AvError) -> Self {
89 GuestResourceToAvFrameError::AvFrameError(AvFrameError::AvError(e))
90 }
91}
92
93impl TryAsAvFrameExt for GuestResource {
94 type BufferSource = MemoryMappingArena;
95 type Error = GuestResourceToAvFrameError;
96
97 fn try_as_av_frame<T: AvBufferSource + 'static>(
98 &self,
99 wrapper: impl FnOnce(MemoryMappingArena) -> T,
100 ) -> Result<AvFrame, Self::Error> {
101 let mut builder = AvFrame::builder()?;
102 builder.set_dimensions(Dimensions {
103 width: self.width,
104 height: self.height,
105 })?;
106 let format = self
107 .format
108 .try_into()
109 .map_err(|_| GuestResourceToAvFrameError::InvalidFormat)?;
110 builder.set_format(format)?;
111 let planes = &self.planes;
112 let len = format.plane_sizes(planes.iter().map(|p| p.stride as _), self.height)?;
114 let start = planes.iter().map(|p| p.offset).min().unwrap();
115 let end = planes
116 .iter()
117 .enumerate()
118 .map(|(i, p)| p.offset + len[i])
119 .max()
120 .unwrap();
121 let mapping = self.handle.get_mapping(start, end - start)?;
122 Ok(builder.build_owned(
123 [AvBuffer::new(wrapper(mapping))
124 .ok_or(GuestResourceToAvFrameError::CannotAllocateAvBuffer)?],
125 planes.iter().map(|p| PlaneDescriptor {
126 buffer_index: 0,
127 offset: p.offset - start,
128 stride: p.stride,
129 }),
130 )?)
131 }
132}
133
134#[derive(Debug)]
138pub struct TryFromFormatError(());
139
140impl Display for TryFromFormatError {
141 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
142 write!(
143 f,
144 "No matching format to convert between AvPixelFormat and Format"
145 )
146 }
147}
148
149impl Error for TryFromFormatError {}
150
151impl TryFrom<Format> for AvPixelFormat {
152 type Error = TryFromFormatError;
153
154 fn try_from(value: Format) -> Result<Self, Self::Error> {
155 use ffmpeg::*;
156 AvPixelFormat::try_from(match value {
157 Format::NV12 => AVPixelFormat_AV_PIX_FMT_NV12,
158 Format::YUV420 => AVPixelFormat_AV_PIX_FMT_YUV420P,
159 _ => return Err(TryFromFormatError(())),
160 })
161 .map_err(|_|
162 TryFromFormatError(()))
165 }
166}
167
168impl TryFrom<AvPixelFormat> for Format {
169 type Error = TryFromFormatError;
170
171 fn try_from(fmt: AvPixelFormat) -> Result<Self, Self::Error> {
172 #![allow(non_upper_case_globals)]
174 use ffmpeg::*;
175 Ok(match fmt.pix_fmt() {
176 AVPixelFormat_AV_PIX_FMT_NV12 => Format::NV12,
177 AVPixelFormat_AV_PIX_FMT_YUV420P => Format::YUV420,
178 _ => return Err(TryFromFormatError(())),
179 })
180 }
181}