1use crate::error::{FfmpegError, FfmpegErrorCode};
2use crate::ffi::*;
3use crate::frame::VideoFrame;
4use crate::smart_object::SmartPtr;
5use crate::AVPixelFormat;
6
7pub struct VideoScaler {
9 ptr: SmartPtr<SwsContext>,
10 frame: VideoFrame,
11 pixel_format: AVPixelFormat,
12 width: i32,
13 height: i32,
14}
15
16unsafe impl Send for VideoScaler {}
18
19impl VideoScaler {
20 pub fn new(
22 input_width: i32,
23 input_height: i32,
24 incoming_pixel_fmt: AVPixelFormat,
25 width: i32,
26 height: i32,
27 pixel_format: AVPixelFormat,
28 ) -> Result<Self, FfmpegError> {
29 let ptr = unsafe {
31 sws_getContext(
32 input_width,
33 input_height,
34 incoming_pixel_fmt.into(),
35 width,
36 height,
37 pixel_format.into(),
38 SWS_BILINEAR as i32,
39 std::ptr::null_mut(),
40 std::ptr::null_mut(),
41 std::ptr::null(),
42 )
43 };
44
45 let destructor = |ptr: &mut *mut SwsContext| {
46 unsafe {
48 sws_freeContext(*ptr);
49 }
50
51 *ptr = std::ptr::null_mut();
52 };
53
54 let ptr = unsafe { SmartPtr::wrap_non_null(ptr, destructor) }.ok_or(FfmpegError::Alloc)?;
56
57 let mut frame = VideoFrame::new()?;
58
59 frame.set_width(width as usize);
60 frame.set_height(height as usize);
61 frame.set_format(pixel_format.into());
62
63 unsafe { frame.alloc_frame_buffer(Some(32)) }.expect("Failed to allocate frame buffer");
65
66 Ok(Self {
67 ptr,
68 frame,
69 pixel_format,
70 width,
71 height,
72 })
73 }
74
75 pub const fn pixel_format(&self) -> AVPixelFormat {
77 self.pixel_format
78 }
79
80 pub const fn width(&self) -> i32 {
82 self.width
83 }
84
85 pub const fn height(&self) -> i32 {
87 self.height
88 }
89
90 pub fn process<'a>(&'a mut self, frame: &VideoFrame) -> Result<&'a VideoFrame, FfmpegError> {
92 let frame_ptr = unsafe { frame.as_ptr().as_ref().unwrap() };
94 let self_frame_ptr = unsafe { self.frame.as_ptr().as_ref().unwrap() };
96
97 FfmpegErrorCode(unsafe {
99 sws_scale(
100 self.ptr.as_mut_ptr(),
101 frame_ptr.data.as_ptr() as *const *const u8,
102 frame_ptr.linesize.as_ptr(),
103 0,
104 frame_ptr.height,
105 self_frame_ptr.data.as_ptr(),
106 self_frame_ptr.linesize.as_ptr(),
107 )
108 })
109 .result()?;
110
111 self.frame.set_dts(frame.dts());
113 self.frame.set_pts(frame.pts());
114 self.frame.set_duration(frame.duration());
115 self.frame.set_time_base(frame.time_base());
116
117 Ok(&self.frame)
118 }
119}
120
121#[cfg(test)]
122#[cfg_attr(all(test, coverage_nightly), coverage(off))]
123mod tests {
124 use insta::assert_debug_snapshot;
125 use rand::Rng;
126
127 use crate::frame::VideoFrame;
128 use crate::scaler::{AVPixelFormat, VideoScaler};
129
130 #[test]
131 fn test_scalar_new() {
132 let input_width = 1920;
133 let input_height = 1080;
134 let incoming_pixel_fmt = AVPixelFormat::Yuv420p;
135 let output_width = 1280;
136 let output_height = 720;
137 let output_pixel_fmt = AVPixelFormat::Rgb24;
138 let scalar = VideoScaler::new(
139 input_width,
140 input_height,
141 incoming_pixel_fmt,
142 output_width,
143 output_height,
144 output_pixel_fmt,
145 );
146
147 assert!(scalar.is_ok(), "Expected Scalar::new to succeed");
148 let scalar = scalar.unwrap();
149
150 assert_eq!(
151 scalar.width(),
152 output_width,
153 "Expected Scalar width to match the output width"
154 );
155 assert_eq!(
156 scalar.height(),
157 output_height,
158 "Expected Scalar height to match the output height"
159 );
160 assert_eq!(
161 scalar.pixel_format(),
162 output_pixel_fmt,
163 "Expected Scalar pixel format to match the output pixel format"
164 );
165 }
166
167 #[test]
168 fn test_scalar_process() {
169 let input_width = 1920;
170 let input_height = 1080;
171 let incoming_pixel_fmt = AVPixelFormat::Yuv420p;
172 let output_width = 1280;
173 let output_height = 720;
174 let output_pixel_fmt = AVPixelFormat::Rgb24;
175
176 let mut scalar = VideoScaler::new(
177 input_width,
178 input_height,
179 incoming_pixel_fmt,
180 output_width,
181 output_height,
182 output_pixel_fmt,
183 )
184 .expect("Failed to create Scalar");
185
186 let mut input_frame: VideoFrame = VideoFrame::new().expect("Failed to create Frame");
187 input_frame.set_width(input_width as usize);
189 input_frame.set_height(input_height as usize);
190 input_frame.set_format(incoming_pixel_fmt.into());
191
192 unsafe { input_frame.alloc_frame_buffer(Some(32)) }.expect("Failed to allocate frame buffer");
194
195 let mut rng = rand::rng();
197
198 for y in 0..input_height {
199 let y = (y * input_frame.linesize(0).unwrap()) as usize;
201 let row = &mut input_frame.data_mut(0).unwrap()[y..y + input_width as usize];
202 rng.fill(row);
203 }
204
205 let half_height = (input_height + 1) / 2;
206 let half_width = (input_width + 1) / 2;
207
208 for y in 0..half_height {
209 let y = (y * input_frame.linesize(1).unwrap()) as usize;
210 let row = &mut input_frame.data_mut(1).unwrap()[y..y + half_width as usize];
211 rng.fill(row);
212 }
213
214 for y in 0..half_height {
215 let y = (y * input_frame.linesize(2).unwrap()) as usize;
216 let row = &mut input_frame.data_mut(2).unwrap()[y..y + half_width as usize];
217 rng.fill(row);
218 }
219
220 let result = scalar.process(&input_frame);
221
222 assert!(
223 result.is_ok(),
224 "Expected Scalar::process to succeed, but got error: {:?}",
225 result
226 );
227
228 let output_frame = result.unwrap();
229 assert_debug_snapshot!(output_frame, @r"
230 VideoFrame {
231 width: 1280,
232 height: 720,
233 sample_aspect_ratio: Rational {
234 numerator: 0,
235 denominator: 1,
236 },
237 pts: None,
238 dts: None,
239 duration: Some(
240 0,
241 ),
242 best_effort_timestamp: None,
243 time_base: Rational {
244 numerator: 0,
245 denominator: 1,
246 },
247 format: AVPixelFormat::Rgb24,
248 is_audio: false,
249 is_video: true,
250 is_keyframe: false,
251 }
252 ");
253 }
254}