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