ac_ffmpeg/codec/video/
scaler.rs

1//! Video frame scaler.
2
3use std::os::raw::{c_int, c_void};
4
5use crate::{
6    codec::video::{PixelFormat, VideoFrame},
7    Error,
8};
9
10const ALG_ID_FAST_BILINEAR: usize = 0;
11const ALG_ID_BILINEAR: usize = 1;
12const ALG_ID_BICUBIC: usize = 2;
13
14extern "C" {
15    fn ffw_frame_scaler_new(
16        sformat: c_int,
17        swidth: c_int,
18        sheight: c_int,
19        tformat: c_int,
20        twidth: c_int,
21        theight: c_int,
22        flags: c_int,
23    ) -> *mut c_void;
24
25    fn ffw_frame_scaler_scale(scaler: *mut c_void, src: *const c_void) -> *mut c_void;
26
27    fn ffw_frame_scaler_free(scaler: *mut c_void);
28
29    fn ffw_alg_id_to_flags(id: usize) -> c_int;
30}
31
32/// Scaling algorithm.
33#[derive(Debug, Copy, Clone, Eq, PartialEq)]
34pub enum Algorithm {
35    FastBilinear,
36    Bilinear,
37    Bicubic,
38}
39
40impl Algorithm {
41    /// Get algorithm ID.
42    fn id(self) -> usize {
43        match self {
44            Algorithm::FastBilinear => ALG_ID_FAST_BILINEAR,
45            Algorithm::Bilinear => ALG_ID_BILINEAR,
46            Algorithm::Bicubic => ALG_ID_BICUBIC,
47        }
48    }
49}
50
51/// Builder for a video frame scaler.
52pub struct VideoFrameScalerBuilder {
53    sformat: c_int,
54    swidth: c_int,
55    sheight: c_int,
56
57    tformat: Option<c_int>,
58    twidth: c_int,
59    theight: c_int,
60
61    flags: c_int,
62}
63
64impl VideoFrameScalerBuilder {
65    /// Create a new video frame scaler builder.
66    fn new() -> Self {
67        let default_algorithm = Algorithm::Bicubic;
68
69        let flags = unsafe { ffw_alg_id_to_flags(default_algorithm.id()) };
70
71        Self {
72            sformat: -1,
73            swidth: 0,
74            sheight: 0,
75
76            tformat: None,
77            twidth: 0,
78            theight: 0,
79
80            flags,
81        }
82    }
83
84    /// Set source pixel format.
85    pub fn source_pixel_format(mut self, format: PixelFormat) -> Self {
86        self.sformat = format.into_raw();
87        self
88    }
89
90    /// Set source frame width.
91    pub fn source_width(mut self, width: usize) -> Self {
92        self.swidth = width as _;
93        self
94    }
95
96    /// Set source frame height.
97    pub fn source_height(mut self, height: usize) -> Self {
98        self.sheight = height as _;
99        self
100    }
101
102    /// Set target pixel format. The default is equal to the source format.
103    pub fn target_pixel_format(mut self, format: PixelFormat) -> Self {
104        self.tformat = Some(format.into_raw());
105        self
106    }
107
108    /// Set target frame width.
109    pub fn target_width(mut self, width: usize) -> Self {
110        self.twidth = width as _;
111        self
112    }
113
114    /// Set target frame height.
115    pub fn target_height(mut self, height: usize) -> Self {
116        self.theight = height as _;
117        self
118    }
119
120    /// Set scaling algorithm. The default is bicubic.
121    pub fn algorithm(mut self, algorithm: Algorithm) -> Self {
122        self.flags = unsafe { ffw_alg_id_to_flags(algorithm.id()) };
123
124        self
125    }
126
127    /// Build the video frame scaler.
128    pub fn build(self) -> Result<VideoFrameScaler, Error> {
129        let tformat = self.tformat.unwrap_or(self.sformat);
130
131        if self.sformat < 0 {
132            return Err(Error::new("invalid source format"));
133        } else if tformat < 0 {
134            return Err(Error::new("invalid target format"));
135        } else if self.swidth < 1 {
136            return Err(Error::new("invalid source width"));
137        } else if self.sheight < 1 {
138            return Err(Error::new("invalid source height"));
139        } else if self.twidth < 1 {
140            return Err(Error::new("invalid target width"));
141        } else if self.theight < 1 {
142            return Err(Error::new("invalid target height"));
143        }
144
145        let ptr = unsafe {
146            ffw_frame_scaler_new(
147                self.sformat,
148                self.swidth,
149                self.sheight,
150                tformat,
151                self.twidth,
152                self.theight,
153                self.flags,
154            )
155        };
156
157        if ptr.is_null() {
158            return Err(Error::new("unable to create a frame scaler"));
159        }
160
161        let res = VideoFrameScaler {
162            ptr,
163
164            sformat: PixelFormat::from_raw(self.sformat),
165            swidth: self.swidth as _,
166            sheight: self.sheight as _,
167        };
168
169        Ok(res)
170    }
171}
172
173/// Video frame scaler.
174pub struct VideoFrameScaler {
175    ptr: *mut c_void,
176
177    sformat: PixelFormat,
178    swidth: usize,
179    sheight: usize,
180}
181
182impl VideoFrameScaler {
183    /// Get a frame scaler builder.
184    pub fn builder() -> VideoFrameScalerBuilder {
185        VideoFrameScalerBuilder::new()
186    }
187
188    /// Scale a given frame.
189    pub fn scale(&mut self, frame: &VideoFrame) -> Result<VideoFrame, Error> {
190        if self.swidth != frame.width() {
191            return Err(Error::new("frame width does not match"));
192        } else if self.sheight != frame.height() {
193            return Err(Error::new("frame height does not match"));
194        } else if self.sformat != frame.pixel_format() {
195            return Err(Error::new("frame pixel format does not match"));
196        }
197
198        let res = unsafe { ffw_frame_scaler_scale(self.ptr, frame.as_ptr()) };
199
200        if res.is_null() {
201            panic!("unable to scale a frame");
202        }
203
204        let frame = unsafe { VideoFrame::from_raw_ptr(res, frame.time_base()) };
205
206        Ok(frame)
207    }
208}
209
210impl Drop for VideoFrameScaler {
211    fn drop(&mut self) {
212        unsafe { ffw_frame_scaler_free(self.ptr) }
213    }
214}
215
216unsafe impl Send for VideoFrameScaler {}
217unsafe impl Sync for VideoFrameScaler {}