1use thiserror::Error as ThisError;
10
11use crate::avcodec::AvError;
12use crate::avcodec::AvFrame;
13use crate::avcodec::Dimensions;
14use crate::ffi;
15
16pub 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)]
25pub 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
39impl Drop for SwConverter {
40 fn drop(&mut self) {
41 unsafe { ffi::sws_freeContext(self.sws_context) };
44 }
45}
46
47impl SwConverter {
48 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 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 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 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}