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