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 //! 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::avcodec::AvError; 12 use crate::avcodec::AvFrame; 13 use crate::avcodec::Dimensions; 14 use crate::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 impl SwConverter { 48 /// Create a new format converter that will convert frames from `src_format` to `dst_format`. 49 /// 50 /// `width` and `height` are the coded size of the frames to be converted. The source and target 51 /// must have the same size in pixels. new( width: usize, height: usize, src_pix_format: ffi::AVPixelFormat, dst_pix_format: ffi::AVPixelFormat, ) -> anyhow::Result<Self>52 pub fn new( 53 width: usize, 54 height: usize, 55 src_pix_format: ffi::AVPixelFormat, 56 dst_pix_format: ffi::AVPixelFormat, 57 ) -> anyhow::Result<Self> { 58 // SAFETY: 59 // Safe because we don't pass any non-null pointer to this function. 60 let sws_context = unsafe { 61 ffi::sws_getContext( 62 width as i32, 63 height as i32, 64 src_pix_format, 65 width as i32, 66 height as i32, 67 dst_pix_format, 68 0, 69 std::ptr::null_mut(), 70 std::ptr::null_mut(), 71 std::ptr::null_mut(), 72 ) 73 }; 74 75 if sws_context.is_null() { 76 anyhow::bail!("error while creating the SWS context") 77 } 78 79 Ok(Self { 80 sws_context, 81 src_pix_format, 82 dst_pix_format, 83 }) 84 } 85 86 /// Copy `src` into `dst` while converting its pixel format according to the parameters the 87 /// frame converter was created with. 88 /// 89 /// `dst` must be a [writable] frame with the same dimensions as `src` and the same format as 90 /// `dst_pix_format` passed to the constructor. 91 /// 92 /// Note that empty `dst` is not currently allowed as this function does not handle allocation. 93 /// 94 /// [writable]: AvFrame::is_writable convert(&mut self, src: &AvFrame, dst: &mut AvFrame) -> Result<(), ConversionError>95 pub fn convert(&mut self, src: &AvFrame, dst: &mut AvFrame) -> Result<(), ConversionError> { 96 if src.format != self.src_pix_format { 97 return Err(ConversionError::FormatMismatch { 98 frame: src.format, 99 converter: self.src_pix_format, 100 }); 101 } 102 103 if dst.format != self.dst_pix_format { 104 return Err(ConversionError::FormatMismatch { 105 frame: dst.format, 106 converter: self.dst_pix_format, 107 }); 108 } 109 110 if src.dimensions() != dst.dimensions() { 111 return Err(ConversionError::DimensionMismatch( 112 src.dimensions(), 113 dst.dimensions(), 114 )); 115 } 116 117 if !dst.is_writable() { 118 return Err(ConversionError::NotWritable); 119 } 120 121 // SAFETY: 122 // Safe because `sws_context`, `src_ref.data` and `dst_data` are all valid pointers, and 123 // we made sure the sizes provided are within the bounds of the buffers. 124 AvError::result(unsafe { 125 ffi::sws_scale( 126 self.sws_context, 127 src.data.as_ptr() as *const *const u8, 128 src.linesize.as_ptr(), 129 0, 130 src.height, 131 dst.data.as_ptr(), 132 dst.linesize.as_ptr(), 133 ) 134 }) 135 .map_err(Into::into) 136 } 137 } 138