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