1*1b4853f5SAndroid Build Coastguard Worker // Copyright 2022-2024 The ChromiumOS Authors 2*1b4853f5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*1b4853f5SAndroid Build Coastguard Worker // found in the LICENSE file. 4*1b4853f5SAndroid Build Coastguard Worker 5*1b4853f5SAndroid Build Coastguard Worker //! This module implements a lightweight and safe interface over the conversion functions of 6*1b4853f5SAndroid Build Coastguard Worker //! `libswscale`. It is designed to concentrate all calls to unsafe methods in one place, while 7*1b4853f5SAndroid Build Coastguard Worker //! providing a higher-level interface for converting decoded frames from one format to another. 8*1b4853f5SAndroid Build Coastguard Worker 9*1b4853f5SAndroid Build Coastguard Worker use thiserror::Error as ThisError; 10*1b4853f5SAndroid Build Coastguard Worker 11*1b4853f5SAndroid Build Coastguard Worker use crate::ffmpeg::avcodec::AvError; 12*1b4853f5SAndroid Build Coastguard Worker use crate::ffmpeg::avcodec::AvFrame; 13*1b4853f5SAndroid Build Coastguard Worker use crate::ffmpeg::avcodec::Dimensions; 14*1b4853f5SAndroid Build Coastguard Worker use crate::ffmpeg::ffi; 15*1b4853f5SAndroid Build Coastguard Worker 16*1b4853f5SAndroid Build Coastguard Worker /// A struct able to copy a decoded `AvFrame` into an `OutputBuffer`'s memory, converting the pixel 17*1b4853f5SAndroid Build Coastguard Worker /// format if needed. 18*1b4853f5SAndroid Build Coastguard Worker pub struct SwConverter { 19*1b4853f5SAndroid Build Coastguard Worker sws_context: *mut ffi::SwsContext, 20*1b4853f5SAndroid Build Coastguard Worker src_pix_format: ffi::AVPixelFormat, 21*1b4853f5SAndroid Build Coastguard Worker dst_pix_format: ffi::AVPixelFormat, 22*1b4853f5SAndroid Build Coastguard Worker } 23*1b4853f5SAndroid Build Coastguard Worker 24*1b4853f5SAndroid Build Coastguard Worker #[derive(Debug, ThisError)] 25*1b4853f5SAndroid Build Coastguard Worker pub enum ConversionError { 26*1b4853f5SAndroid Build Coastguard Worker #[error("AvFrame's format {frame} does not match converter {converter} configuration")] 27*1b4853f5SAndroid Build Coastguard Worker FormatMismatch { 28*1b4853f5SAndroid Build Coastguard Worker frame: ffi::AVPixelFormat, 29*1b4853f5SAndroid Build Coastguard Worker converter: ffi::AVPixelFormat, 30*1b4853f5SAndroid Build Coastguard Worker }, 31*1b4853f5SAndroid Build Coastguard Worker #[error("source AvFrame's dimension {0:?} does not match destination's {1:?}")] 32*1b4853f5SAndroid Build Coastguard Worker DimensionMismatch(Dimensions, Dimensions), 33*1b4853f5SAndroid Build Coastguard Worker #[error("destination AvFrame needs to be refcounted with refcount=1")] 34*1b4853f5SAndroid Build Coastguard Worker NotWritable, 35*1b4853f5SAndroid Build Coastguard Worker #[error("error during conversion with libswscale: {0}")] 36*1b4853f5SAndroid Build Coastguard Worker AvError(#[from] AvError), 37*1b4853f5SAndroid Build Coastguard Worker } 38*1b4853f5SAndroid Build Coastguard Worker 39*1b4853f5SAndroid Build Coastguard Worker impl Drop for SwConverter { drop(&mut self)40*1b4853f5SAndroid Build Coastguard Worker fn drop(&mut self) { 41*1b4853f5SAndroid Build Coastguard Worker // SAFETY: 42*1b4853f5SAndroid Build Coastguard Worker // Safe because `sws_context` is valid through the life of this object. 43*1b4853f5SAndroid Build Coastguard Worker unsafe { ffi::sws_freeContext(self.sws_context) }; 44*1b4853f5SAndroid Build Coastguard Worker } 45*1b4853f5SAndroid Build Coastguard Worker } 46*1b4853f5SAndroid Build Coastguard Worker 47*1b4853f5SAndroid Build Coastguard Worker #[derive(Debug, ThisError)] 48*1b4853f5SAndroid Build Coastguard Worker pub enum SwConverterCreationError { 49*1b4853f5SAndroid Build Coastguard Worker #[error("sws_getContext returned NULL")] 50*1b4853f5SAndroid Build Coastguard Worker CreationFailure, 51*1b4853f5SAndroid Build Coastguard Worker } 52*1b4853f5SAndroid Build Coastguard Worker 53*1b4853f5SAndroid Build Coastguard Worker impl SwConverter { 54*1b4853f5SAndroid Build Coastguard Worker /// Create a new format converter that will convert frames from `src_format` to `dst_format`. 55*1b4853f5SAndroid Build Coastguard Worker /// 56*1b4853f5SAndroid Build Coastguard Worker /// `width` and `height` are the coded size of the frames to be converted. The source and target 57*1b4853f5SAndroid Build Coastguard Worker /// must have the same size in pixels. new( width: usize, height: usize, src_pix_format: ffi::AVPixelFormat, dst_pix_format: ffi::AVPixelFormat, ) -> Result<Self, SwConverterCreationError>58*1b4853f5SAndroid Build Coastguard Worker pub fn new( 59*1b4853f5SAndroid Build Coastguard Worker width: usize, 60*1b4853f5SAndroid Build Coastguard Worker height: usize, 61*1b4853f5SAndroid Build Coastguard Worker src_pix_format: ffi::AVPixelFormat, 62*1b4853f5SAndroid Build Coastguard Worker dst_pix_format: ffi::AVPixelFormat, 63*1b4853f5SAndroid Build Coastguard Worker ) -> Result<Self, SwConverterCreationError> { 64*1b4853f5SAndroid Build Coastguard Worker // SAFETY: 65*1b4853f5SAndroid Build Coastguard Worker // Safe because we don't pass any non-null pointer to this function. 66*1b4853f5SAndroid Build Coastguard Worker let sws_context = unsafe { 67*1b4853f5SAndroid Build Coastguard Worker ffi::sws_getContext( 68*1b4853f5SAndroid Build Coastguard Worker width as i32, 69*1b4853f5SAndroid Build Coastguard Worker height as i32, 70*1b4853f5SAndroid Build Coastguard Worker src_pix_format, 71*1b4853f5SAndroid Build Coastguard Worker width as i32, 72*1b4853f5SAndroid Build Coastguard Worker height as i32, 73*1b4853f5SAndroid Build Coastguard Worker dst_pix_format, 74*1b4853f5SAndroid Build Coastguard Worker 0, 75*1b4853f5SAndroid Build Coastguard Worker std::ptr::null_mut(), 76*1b4853f5SAndroid Build Coastguard Worker std::ptr::null_mut(), 77*1b4853f5SAndroid Build Coastguard Worker std::ptr::null_mut(), 78*1b4853f5SAndroid Build Coastguard Worker ) 79*1b4853f5SAndroid Build Coastguard Worker }; 80*1b4853f5SAndroid Build Coastguard Worker 81*1b4853f5SAndroid Build Coastguard Worker if sws_context.is_null() { 82*1b4853f5SAndroid Build Coastguard Worker Err(SwConverterCreationError::CreationFailure) 83*1b4853f5SAndroid Build Coastguard Worker } else { 84*1b4853f5SAndroid Build Coastguard Worker Ok(Self { 85*1b4853f5SAndroid Build Coastguard Worker sws_context, 86*1b4853f5SAndroid Build Coastguard Worker src_pix_format, 87*1b4853f5SAndroid Build Coastguard Worker dst_pix_format, 88*1b4853f5SAndroid Build Coastguard Worker }) 89*1b4853f5SAndroid Build Coastguard Worker } 90*1b4853f5SAndroid Build Coastguard Worker } 91*1b4853f5SAndroid Build Coastguard Worker 92*1b4853f5SAndroid Build Coastguard Worker /// Copy `src` into `dst` while converting its pixel format according to the parameters the 93*1b4853f5SAndroid Build Coastguard Worker /// frame converter was created with. 94*1b4853f5SAndroid Build Coastguard Worker /// 95*1b4853f5SAndroid Build Coastguard Worker /// `dst` must be a [writable] frame with the same dimensions as `src` and the same format as 96*1b4853f5SAndroid Build Coastguard Worker /// `dst_pix_format` passed to the constructor. 97*1b4853f5SAndroid Build Coastguard Worker /// 98*1b4853f5SAndroid Build Coastguard Worker /// Note that empty `dst` is not currently allowed as this function does not handle allocation. 99*1b4853f5SAndroid Build Coastguard Worker /// 100*1b4853f5SAndroid Build Coastguard Worker /// [writable]: AvFrame::is_writable convert(&mut self, src: &AvFrame, dst: &mut AvFrame) -> Result<(), ConversionError>101*1b4853f5SAndroid Build Coastguard Worker pub fn convert(&mut self, src: &AvFrame, dst: &mut AvFrame) -> Result<(), ConversionError> { 102*1b4853f5SAndroid Build Coastguard Worker if src.format != self.src_pix_format { 103*1b4853f5SAndroid Build Coastguard Worker return Err(ConversionError::FormatMismatch { 104*1b4853f5SAndroid Build Coastguard Worker frame: src.format, 105*1b4853f5SAndroid Build Coastguard Worker converter: self.src_pix_format, 106*1b4853f5SAndroid Build Coastguard Worker }); 107*1b4853f5SAndroid Build Coastguard Worker } 108*1b4853f5SAndroid Build Coastguard Worker 109*1b4853f5SAndroid Build Coastguard Worker if dst.format != self.dst_pix_format { 110*1b4853f5SAndroid Build Coastguard Worker return Err(ConversionError::FormatMismatch { 111*1b4853f5SAndroid Build Coastguard Worker frame: dst.format, 112*1b4853f5SAndroid Build Coastguard Worker converter: self.dst_pix_format, 113*1b4853f5SAndroid Build Coastguard Worker }); 114*1b4853f5SAndroid Build Coastguard Worker } 115*1b4853f5SAndroid Build Coastguard Worker 116*1b4853f5SAndroid Build Coastguard Worker if src.dimensions() != dst.dimensions() { 117*1b4853f5SAndroid Build Coastguard Worker return Err(ConversionError::DimensionMismatch( 118*1b4853f5SAndroid Build Coastguard Worker src.dimensions(), 119*1b4853f5SAndroid Build Coastguard Worker dst.dimensions(), 120*1b4853f5SAndroid Build Coastguard Worker )); 121*1b4853f5SAndroid Build Coastguard Worker } 122*1b4853f5SAndroid Build Coastguard Worker 123*1b4853f5SAndroid Build Coastguard Worker if !dst.is_writable() { 124*1b4853f5SAndroid Build Coastguard Worker return Err(ConversionError::NotWritable); 125*1b4853f5SAndroid Build Coastguard Worker } 126*1b4853f5SAndroid Build Coastguard Worker 127*1b4853f5SAndroid Build Coastguard Worker // SAFETY: 128*1b4853f5SAndroid Build Coastguard Worker // Safe because `sws_context`, `src_ref.data` and `dst_data` are all valid pointers, and 129*1b4853f5SAndroid Build Coastguard Worker // we made sure the sizes provided are within the bounds of the buffers. 130*1b4853f5SAndroid Build Coastguard Worker AvError::result(unsafe { 131*1b4853f5SAndroid Build Coastguard Worker ffi::sws_scale( 132*1b4853f5SAndroid Build Coastguard Worker self.sws_context, 133*1b4853f5SAndroid Build Coastguard Worker src.data.as_ptr() as *const *const u8, 134*1b4853f5SAndroid Build Coastguard Worker src.linesize.as_ptr(), 135*1b4853f5SAndroid Build Coastguard Worker 0, 136*1b4853f5SAndroid Build Coastguard Worker src.height, 137*1b4853f5SAndroid Build Coastguard Worker dst.data.as_ptr(), 138*1b4853f5SAndroid Build Coastguard Worker dst.linesize.as_ptr(), 139*1b4853f5SAndroid Build Coastguard Worker ) 140*1b4853f5SAndroid Build Coastguard Worker }) 141*1b4853f5SAndroid Build Coastguard Worker .map_err(Into::into) 142*1b4853f5SAndroid Build Coastguard Worker } 143*1b4853f5SAndroid Build Coastguard Worker } 144