ac_ffmpeg/codec/video/
scaler.rsuse std::os::raw::{c_int, c_void};
use crate::{
codec::video::{PixelFormat, VideoFrame},
Error,
};
const ALG_ID_FAST_BILINEAR: usize = 0;
const ALG_ID_BILINEAR: usize = 1;
const ALG_ID_BICUBIC: usize = 2;
extern "C" {
fn ffw_frame_scaler_new(
sformat: c_int,
swidth: c_int,
sheight: c_int,
tformat: c_int,
twidth: c_int,
theight: c_int,
flags: c_int,
) -> *mut c_void;
fn ffw_frame_scaler_scale(scaler: *mut c_void, src: *const c_void) -> *mut c_void;
fn ffw_frame_scaler_free(scaler: *mut c_void);
fn ffw_alg_id_to_flags(id: usize) -> c_int;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Algorithm {
FastBilinear,
Bilinear,
Bicubic,
}
impl Algorithm {
fn id(self) -> usize {
match self {
Algorithm::FastBilinear => ALG_ID_FAST_BILINEAR,
Algorithm::Bilinear => ALG_ID_BILINEAR,
Algorithm::Bicubic => ALG_ID_BICUBIC,
}
}
}
pub struct VideoFrameScalerBuilder {
sformat: c_int,
swidth: c_int,
sheight: c_int,
tformat: Option<c_int>,
twidth: c_int,
theight: c_int,
flags: c_int,
}
impl VideoFrameScalerBuilder {
fn new() -> Self {
let default_algorithm = Algorithm::Bicubic;
let flags = unsafe { ffw_alg_id_to_flags(default_algorithm.id()) };
Self {
sformat: -1,
swidth: 0,
sheight: 0,
tformat: None,
twidth: 0,
theight: 0,
flags,
}
}
pub fn source_pixel_format(mut self, format: PixelFormat) -> Self {
self.sformat = format.into_raw();
self
}
pub fn source_width(mut self, width: usize) -> Self {
self.swidth = width as _;
self
}
pub fn source_height(mut self, height: usize) -> Self {
self.sheight = height as _;
self
}
pub fn target_pixel_format(mut self, format: PixelFormat) -> Self {
self.tformat = Some(format.into_raw());
self
}
pub fn target_width(mut self, width: usize) -> Self {
self.twidth = width as _;
self
}
pub fn target_height(mut self, height: usize) -> Self {
self.theight = height as _;
self
}
pub fn algorithm(mut self, algorithm: Algorithm) -> Self {
self.flags = unsafe { ffw_alg_id_to_flags(algorithm.id()) };
self
}
pub fn build(self) -> Result<VideoFrameScaler, Error> {
let tformat = self.tformat.unwrap_or(self.sformat);
if self.sformat < 0 {
return Err(Error::new("invalid source format"));
} else if tformat < 0 {
return Err(Error::new("invalid target format"));
} else if self.swidth < 1 {
return Err(Error::new("invalid source width"));
} else if self.sheight < 1 {
return Err(Error::new("invalid source height"));
} else if self.twidth < 1 {
return Err(Error::new("invalid target width"));
} else if self.theight < 1 {
return Err(Error::new("invalid target height"));
}
let ptr = unsafe {
ffw_frame_scaler_new(
self.sformat,
self.swidth,
self.sheight,
tformat,
self.twidth,
self.theight,
self.flags,
)
};
if ptr.is_null() {
return Err(Error::new("unable to create a frame scaler"));
}
let res = VideoFrameScaler {
ptr,
sformat: PixelFormat::from_raw(self.sformat),
swidth: self.swidth as _,
sheight: self.sheight as _,
};
Ok(res)
}
}
pub struct VideoFrameScaler {
ptr: *mut c_void,
sformat: PixelFormat,
swidth: usize,
sheight: usize,
}
impl VideoFrameScaler {
pub fn builder() -> VideoFrameScalerBuilder {
VideoFrameScalerBuilder::new()
}
pub fn scale(&mut self, frame: &VideoFrame) -> Result<VideoFrame, Error> {
if self.swidth != frame.width() {
return Err(Error::new("frame width does not match"));
} else if self.sheight != frame.height() {
return Err(Error::new("frame height does not match"));
} else if self.sformat != frame.pixel_format() {
return Err(Error::new("frame pixel format does not match"));
}
let res = unsafe { ffw_frame_scaler_scale(self.ptr, frame.as_ptr()) };
if res.is_null() {
panic!("unable to scale a frame");
}
let frame = unsafe { VideoFrame::from_raw_ptr(res, frame.time_base()) };
Ok(frame)
}
}
impl Drop for VideoFrameScaler {
fn drop(&mut self) {
unsafe { ffw_frame_scaler_free(self.ptr) }
}
}
unsafe impl Send for VideoFrameScaler {}
unsafe impl Sync for VideoFrameScaler {}