ac_ffmpeg/codec/video/
filter.rs

1//! Video filter.
2
3use 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
37/// A builder for video filters.
38pub 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    /// Create a new video filter builder.
52    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    /// Set the pixel aspect ratio (the default is `1:1`).
73    #[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    /// Set input time base (the default is `1/1000000`).
81    #[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    /// Set output time base.
88    #[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    /// Build the filtergraph.
95    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        // init source buffer
115        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        // init sink buffer
126        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        // initialize the filter graph
137        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
165/// Video filter.
166pub 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    /// Get a video filter builder.
176    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}