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