xref: /aosp_15_r20/external/crosvm/media/ffmpeg/src/swscale.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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