• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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