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