1use std::{
4 ffi::CString,
5 os::raw::{c_char, c_int, c_void},
6 ptr,
7};
8
9use crate::{
10 codec::{
11 video::frame::{PixelFormat, VideoFrame},
12 CodecError, Filter,
13 },
14 time::TimeBase,
15 Error,
16};
17
18extern "C" {
19 fn ffw_filtergraph_new() -> *mut c_void;
20 fn ffw_filtersource_new(
21 filter_ctx: *mut *mut c_void,
22 filter_graph: *mut c_void,
23 src_args: *const c_char,
24 ) -> c_int;
25 fn ffw_filtersink_new(filter_ctx: *mut *mut c_void, filter_graph: *mut c_void) -> c_int;
26 fn ffw_filtergraph_init(
27 filter_graph: *mut c_void,
28 buffersrc_ctx: *mut c_void,
29 buffersink_ctx: *mut c_void,
30 filters_descr: *const c_char,
31 ) -> c_int;
32 fn ffw_filtergraph_push_frame(context: *mut c_void, frame: *const c_void) -> c_int;
33 fn ffw_filtergraph_take_frame(context: *mut c_void, frame: *mut *mut c_void) -> c_int;
34 fn ffw_filtergraph_free(context: *mut c_void);
35}
36
37pub struct VideoFilterBuilder {
39 ptr: *mut c_void,
40
41 width: usize,
42 height: usize,
43 pixel_fmt: PixelFormat,
44 pixel_aspect_num: u32,
45 pixel_aspect_den: u32,
46 input_time_base: TimeBase,
47 output_time_base: Option<TimeBase>,
48}
49
50impl VideoFilterBuilder {
51 fn new(width: usize, height: usize, pixel_fmt: PixelFormat) -> Self {
53 let ptr = unsafe { ffw_filtergraph_new() };
54
55 if ptr.is_null() {
56 panic!("unable to allocate a filter graph");
57 }
58
59 Self {
60 ptr,
61
62 width,
63 height,
64 pixel_fmt,
65 pixel_aspect_num: 1,
66 pixel_aspect_den: 1,
67 input_time_base: TimeBase::MICROSECONDS,
68 output_time_base: None,
69 }
70 }
71
72 #[inline]
74 pub fn pixel_aspect_ratio(mut self, num: u32, den: u32) -> Self {
75 self.pixel_aspect_num = num;
76 self.pixel_aspect_den = den;
77 self
78 }
79
80 #[inline]
82 pub fn input_time_base(mut self, time_base: TimeBase) -> Self {
83 self.input_time_base = time_base;
84 self
85 }
86
87 #[inline]
89 pub fn output_time_base(mut self, time_base: TimeBase) -> Self {
90 self.output_time_base = Some(time_base);
91 self
92 }
93
94 pub fn build(mut self, filter_description: &str) -> Result<VideoFilter, Error> {
96 let filter_description =
97 CString::new(filter_description).expect("invalid filter description");
98
99 let output_time_base = self.output_time_base.unwrap_or(self.input_time_base);
100
101 let src_args = format!(
102 "video_size={}x{}:pix_fmt={}:time_base={}/{}:pixel_aspect={}/{}",
103 self.width,
104 self.height,
105 self.pixel_fmt.into_raw(),
106 self.input_time_base.num(),
107 self.input_time_base.den(),
108 self.pixel_aspect_num,
109 self.pixel_aspect_den,
110 );
111
112 let src_args = CString::new(src_args).unwrap();
113
114 let mut source = ptr::null_mut();
116
117 let ret = unsafe { ffw_filtersource_new(&mut source, self.ptr, src_args.as_ptr()) };
118
119 if ret < 0 {
120 return Err(Error::from_raw_error_code(ret));
121 } else if source.is_null() {
122 panic!("unable to allocate a filter source buffer");
123 }
124
125 let mut sink = ptr::null_mut();
127
128 let ret = unsafe { ffw_filtersink_new(&mut sink, self.ptr) };
129
130 if ret < 0 {
131 return Err(Error::from_raw_error_code(ret));
132 } else if sink.is_null() {
133 panic!("unable to allocate a filter sink buffer");
134 }
135
136 let ret =
138 unsafe { ffw_filtergraph_init(self.ptr, source, sink, filter_description.as_ptr()) };
139
140 if ret < 0 {
141 return Err(Error::from_raw_error_code(ret));
142 }
143
144 let res = VideoFilter {
145 ptr: std::mem::replace(&mut self.ptr, ptr::null_mut()),
146 source,
147 sink,
148 input_time_base: self.input_time_base,
149 output_time_base,
150 };
151
152 Ok(res)
153 }
154}
155
156impl Drop for VideoFilterBuilder {
157 fn drop(&mut self) {
158 unsafe { ffw_filtergraph_free(self.ptr) }
159 }
160}
161
162unsafe impl Send for VideoFilterBuilder {}
163unsafe impl Sync for VideoFilterBuilder {}
164
165pub struct VideoFilter {
167 ptr: *mut c_void,
168 source: *mut c_void,
169 sink: *mut c_void,
170 input_time_base: TimeBase,
171 output_time_base: TimeBase,
172}
173
174impl VideoFilter {
175 pub fn builder(width: usize, height: usize, pixel_fmt: PixelFormat) -> VideoFilterBuilder {
177 VideoFilterBuilder::new(width, height, pixel_fmt)
178 }
179}
180
181impl Drop for VideoFilter {
182 fn drop(&mut self) {
183 unsafe { ffw_filtergraph_free(self.ptr) }
184 }
185}
186
187unsafe impl Send for VideoFilter {}
188unsafe impl Sync for VideoFilter {}
189
190impl Filter for VideoFilter {
191 type Frame = VideoFrame;
192
193 fn try_push(&mut self, frame: VideoFrame) -> Result<(), CodecError> {
194 let frame = frame.with_time_base(self.input_time_base);
195
196 unsafe {
197 match ffw_filtergraph_push_frame(self.source, frame.as_ptr()) {
198 1 => Ok(()),
199 0 => Err(CodecError::again(
200 "all frames must be consumed before pushing a new frame",
201 )),
202 e => Err(CodecError::from_raw_error_code(e)),
203 }
204 }
205 }
206
207 fn try_flush(&mut self) -> Result<(), CodecError> {
208 unsafe {
209 match ffw_filtergraph_push_frame(self.source, ptr::null()) {
210 1 => Ok(()),
211 0 => Err(CodecError::again(
212 "all frames must be consumed before flushing",
213 )),
214 e => Err(CodecError::from_raw_error_code(e)),
215 }
216 }
217 }
218
219 fn take(&mut self) -> Result<Option<VideoFrame>, Error> {
220 let mut ptr = ptr::null_mut();
221
222 unsafe {
223 match ffw_filtergraph_take_frame(self.sink, &mut ptr) {
224 1 if ptr.is_null() => panic!("no frame received"),
225 1 => Ok(Some(VideoFrame::from_raw_ptr(ptr, self.output_time_base))),
226 0 => Ok(None),
227 e => Err(Error::from_raw_error_code(e)),
228 }
229 }
230 }
231}