1 // Copyright 2022 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 use std::error::Error; 6 use std::fmt::Display; 7 use std::fmt::Formatter; 8 9 use base::MappedRegion; 10 use base::MemoryMappingArena; 11 use base::MmapError; 12 use ffmpeg::avcodec::AvBuffer; 13 use ffmpeg::avcodec::AvBufferSource; 14 use ffmpeg::avcodec::AvError; 15 use ffmpeg::avcodec::AvFrame; 16 use ffmpeg::avcodec::AvFrameError; 17 use ffmpeg::avcodec::AvPixelFormat; 18 use ffmpeg::avcodec::Dimensions; 19 use ffmpeg::avcodec::PlaneDescriptor; 20 use thiserror::Error as ThisError; 21 22 use crate::virtio::video::format::Format; 23 use crate::virtio::video::resource::BufferHandle; 24 use crate::virtio::video::resource::GuestResource; 25 26 /// A simple wrapper that turns a [`MemoryMappingArena`] into an [`AvBufferSource`]. 27 /// 28 /// **Note:** Only use this if you can reasonably assume the mapped memory won't be written to from 29 /// another thread or the VM! The reason [`AvBufferSource`] is not directly implemented on 30 /// [`MemoryMappingArena`] is exactly due to this unsafety: If the guest or someone else writes to 31 /// the buffer FFmpeg is currently reading from or writing to, undefined behavior might occur. This 32 /// struct acts as an opt-in mechanism for this potential unsafety. 33 pub struct MemoryMappingAvBufferSource(MemoryMappingArena); 34 35 impl From<MemoryMappingArena> for MemoryMappingAvBufferSource { from(inner: MemoryMappingArena) -> Self36 fn from(inner: MemoryMappingArena) -> Self { 37 Self(inner) 38 } 39 } 40 impl AvBufferSource for MemoryMappingAvBufferSource { as_ptr(&self) -> *const u841 fn as_ptr(&self) -> *const u8 { 42 self.0.as_ptr() as _ 43 } 44 len(&self) -> usize45 fn len(&self) -> usize { 46 self.0.size() 47 } 48 is_empty(&self) -> bool49 fn is_empty(&self) -> bool { 50 self.len() == 0 51 } 52 } 53 54 pub trait TryAsAvFrameExt { 55 type BufferSource; 56 type Error: Error; 57 58 /// Try to create an [`AvFrame`] from `self`. 59 /// 60 /// `wrapper` is a constructor for `T` that wraps `Self::BufferSource` and implement the 61 /// `AvBufferSource` functionality. This can be used to provide a custom destructor 62 /// implementation: for example, sending a virtio message signaling the buffer source is no 63 /// longer used and it can be recycled by the guest. 64 /// 65 /// Note that `Self::BufferSource` might not always implement `AvBufferSource`. For instance, 66 /// it's not guaranteed that a `MemoryMappingArena` created from `GuestResource` will be always 67 /// immutable, and the backend need to explicit acknowledge the potential unsoundness by 68 /// wrapping it in [`MemoryMappingAvBufferSource`]. try_as_av_frame<T: AvBufferSource + 'static>( &self, wrapper: impl FnOnce(Self::BufferSource) -> T, ) -> Result<AvFrame, Self::Error>69 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)] 76 pub 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 87 impl From<AvError> for GuestResourceToAvFrameError { from(e: AvError) -> Self88 fn from(e: AvError) -> Self { 89 GuestResourceToAvFrameError::AvFrameError(AvFrameError::AvError(e)) 90 } 91 } 92 93 impl TryAsAvFrameExt for GuestResource { 94 type BufferSource = MemoryMappingArena; 95 type Error = GuestResourceToAvFrameError; 96 try_as_av_frame<T: AvBufferSource + 'static>( &self, wrapper: impl FnOnce(MemoryMappingArena) -> T, ) -> Result<AvFrame, Self::Error>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 // We have at least one plane, so we can unwrap below. 113 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 /// The error returned when converting between `AvPixelFormat` and `Format` and there's no 135 /// applicable format. 136 // The empty field prevents constructing this and allows extending it in the future. 137 #[derive(Debug)] 138 pub struct TryFromFormatError(()); 139 140 impl Display for TryFromFormatError { fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result141 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 149 impl Error for TryFromFormatError {} 150 151 impl TryFrom<Format> for AvPixelFormat { 152 type Error = TryFromFormatError; 153 try_from(value: Format) -> Result<Self, Self::Error>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 // The error case should never happen as long as we use valid constant values, but 163 // don't panic in case something goes wrong. 164 TryFromFormatError(())) 165 } 166 } 167 168 impl TryFrom<AvPixelFormat> for Format { 169 type Error = TryFromFormatError; 170 try_from(fmt: AvPixelFormat) -> Result<Self, Self::Error>171 fn try_from(fmt: AvPixelFormat) -> Result<Self, Self::Error> { 172 // https://github.com/rust-lang/rust/issues/39371 Lint wrongly warns the consumer 173 #![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 } 182