use thiserror::Error as ThisError;
use crate::avcodec::AvError;
use crate::avcodec::AvFrame;
use crate::avcodec::Dimensions;
use crate::ffi;
pub struct SwConverter {
sws_context: *mut ffi::SwsContext,
src_pix_format: ffi::AVPixelFormat,
dst_pix_format: ffi::AVPixelFormat,
}
#[derive(Debug, ThisError)]
pub enum ConversionError {
#[error("AvFrame's format {frame} does not match converter {converter} configuration")]
FormatMismatch {
frame: ffi::AVPixelFormat,
converter: ffi::AVPixelFormat,
},
#[error("source AvFrame's dimension {0:?} does not match destination's {1:?}")]
DimensionMismatch(Dimensions, Dimensions),
#[error("destination AvFrame needs to be refcounted with refcount=1")]
NotWritable,
#[error("error during conversion with libswscale: {0}")]
AvError(#[from] AvError),
}
impl Drop for SwConverter {
fn drop(&mut self) {
unsafe { ffi::sws_freeContext(self.sws_context) };
}
}
impl SwConverter {
pub fn new(
width: usize,
height: usize,
src_pix_format: ffi::AVPixelFormat,
dst_pix_format: ffi::AVPixelFormat,
) -> anyhow::Result<Self> {
let sws_context = unsafe {
ffi::sws_getContext(
width as i32,
height as i32,
src_pix_format,
width as i32,
height as i32,
dst_pix_format,
0,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
)
};
if sws_context.is_null() {
anyhow::bail!("error while creating the SWS context")
}
Ok(Self {
sws_context,
src_pix_format,
dst_pix_format,
})
}
pub fn convert(&mut self, src: &AvFrame, dst: &mut AvFrame) -> Result<(), ConversionError> {
if src.format != self.src_pix_format {
return Err(ConversionError::FormatMismatch {
frame: src.format,
converter: self.src_pix_format,
});
}
if dst.format != self.dst_pix_format {
return Err(ConversionError::FormatMismatch {
frame: dst.format,
converter: self.dst_pix_format,
});
}
if src.dimensions() != dst.dimensions() {
return Err(ConversionError::DimensionMismatch(
src.dimensions(),
dst.dimensions(),
));
}
if !dst.is_writable() {
return Err(ConversionError::NotWritable);
}
AvError::result(unsafe {
ffi::sws_scale(
self.sws_context,
src.data.as_ptr() as *const *const u8,
src.linesize.as_ptr(),
0,
src.height,
dst.data.as_ptr(),
dst.linesize.as_ptr(),
)
})
.map_err(Into::into)
}
}