Skip to main content

gstreamer_video/subclass/
video_filter.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::translate::*;
4use gst_base::{prelude::*, subclass::prelude::*};
5
6use crate::{VideoFilter, VideoFrameExt, VideoFrameRef, VideoInfo, ffi};
7
8pub trait VideoFilterImpl: BaseTransformImpl + ObjectSubclass<Type: IsA<VideoFilter>> {
9    /// function to be called with the negotiated caps and video infos
10    fn set_info(
11        &self,
12        incaps: &gst::Caps,
13        in_info: &VideoInfo,
14        outcaps: &gst::Caps,
15        out_info: &VideoInfo,
16    ) -> Result<(), gst::LoggableError> {
17        self.parent_set_info(incaps, in_info, outcaps, out_info)
18    }
19
20    /// transform a video frame
21    fn transform_frame(
22        &self,
23        inframe: &VideoFrameRef<&gst::BufferRef>,
24        outframe: &mut VideoFrameRef<&mut gst::BufferRef>,
25    ) -> Result<gst::FlowSuccess, gst::FlowError> {
26        self.parent_transform_frame(inframe, outframe)
27    }
28
29    /// transform a video frame in place
30    fn transform_frame_ip(
31        &self,
32        frame: &mut VideoFrameRef<&mut gst::BufferRef>,
33    ) -> Result<gst::FlowSuccess, gst::FlowError> {
34        self.parent_transform_frame_ip(frame)
35    }
36
37    fn transform_frame_ip_passthrough(
38        &self,
39        frame: &VideoFrameRef<&gst::BufferRef>,
40    ) -> Result<gst::FlowSuccess, gst::FlowError> {
41        self.parent_transform_frame_ip_passthrough(frame)
42    }
43}
44
45pub trait VideoFilterImplExt: VideoFilterImpl {
46    fn parent_set_info(
47        &self,
48        incaps: &gst::Caps,
49        in_info: &VideoInfo,
50        outcaps: &gst::Caps,
51        out_info: &VideoInfo,
52    ) -> Result<(), gst::LoggableError> {
53        unsafe {
54            let data = Self::type_data();
55            let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoFilterClass;
56            (*parent_class)
57                .set_info
58                .map(|f| {
59                    gst::result_from_gboolean!(
60                        f(
61                            self.obj().unsafe_cast_ref::<VideoFilter>().to_glib_none().0,
62                            incaps.to_glib_none().0,
63                            mut_override(in_info.to_glib_none().0),
64                            outcaps.to_glib_none().0,
65                            mut_override(out_info.to_glib_none().0),
66                        ),
67                        gst::CAT_RUST,
68                        "Parent function `set_info` failed"
69                    )
70                })
71                .unwrap_or(Ok(()))
72        }
73    }
74
75    fn parent_transform_frame(
76        &self,
77        inframe: &VideoFrameRef<&gst::BufferRef>,
78        outframe: &mut VideoFrameRef<&mut gst::BufferRef>,
79    ) -> Result<gst::FlowSuccess, gst::FlowError> {
80        unsafe {
81            let data = Self::type_data();
82            let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoFilterClass;
83            (*parent_class)
84                .transform_frame
85                .map(|f| {
86                    try_from_glib(f(
87                        self.obj().unsafe_cast_ref::<VideoFilter>().to_glib_none().0,
88                        mut_override(inframe.as_ptr()),
89                        outframe.as_mut_ptr(),
90                    ))
91                })
92                .unwrap_or_else(|| {
93                    if !self
94                        .obj()
95                        .unsafe_cast_ref::<gst_base::BaseTransform>()
96                        .is_in_place()
97                    {
98                        Err(gst::FlowError::NotSupported)
99                    } else {
100                        unreachable!(
101                            "parent `transform_frame` called while transform operates in-place"
102                        );
103                    }
104                })
105        }
106    }
107
108    fn parent_transform_frame_ip(
109        &self,
110        frame: &mut VideoFrameRef<&mut gst::BufferRef>,
111    ) -> Result<gst::FlowSuccess, gst::FlowError> {
112        unsafe {
113            let data = Self::type_data();
114            let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoFilterClass;
115            let f = (*parent_class).transform_frame_ip.unwrap_or_else(|| {
116                if self
117                    .obj()
118                    .unsafe_cast_ref::<gst_base::BaseTransform>()
119                    .is_in_place()
120                {
121                    panic!(concat!(
122                        "Missing parent function `transform_frame_ip`. Required because ",
123                        "transform operates in-place"
124                    ));
125                } else {
126                    unreachable!(
127                        "parent `transform_frame` called while transform doesn't operate in-place"
128                    );
129                }
130            });
131
132            try_from_glib(f(
133                self.obj().unsafe_cast_ref::<VideoFilter>().to_glib_none().0,
134                frame.as_mut_ptr(),
135            ))
136        }
137    }
138
139    fn parent_transform_frame_ip_passthrough(
140        &self,
141        frame: &VideoFrameRef<&gst::BufferRef>,
142    ) -> Result<gst::FlowSuccess, gst::FlowError> {
143        unsafe {
144            let data = Self::type_data();
145            let parent_class = data.as_ref().parent_class() as *mut ffi::GstVideoFilterClass;
146            let f = (*parent_class).transform_frame_ip.unwrap_or_else(|| {
147                if self
148                    .obj()
149                    .unsafe_cast_ref::<gst_base::BaseTransform>()
150                    .is_in_place()
151                {
152                    panic!(concat!(
153                        "Missing parent function `transform_frame_ip`. Required because ",
154                        "transform operates in-place (passthrough mode)"
155                    ));
156                } else {
157                    unreachable!(concat!(
158                        "parent `transform_frame_ip` called ",
159                        "while transform doesn't operate in-place (passthrough mode)"
160                    ));
161                }
162            });
163
164            try_from_glib(f(
165                self.obj().unsafe_cast_ref::<VideoFilter>().to_glib_none().0,
166                mut_override(frame.as_ptr()),
167            ))
168        }
169    }
170}
171
172impl<T: VideoFilterImpl> VideoFilterImplExt for T {}
173
174unsafe impl<T: VideoFilterImpl> IsSubclassable<T> for VideoFilter {
175    fn class_init(klass: &mut glib::Class<Self>) {
176        use gst_base::subclass::base_transform::BaseTransformMode;
177
178        Self::parent_class_init::<T>(klass);
179
180        let klass = klass.as_mut();
181        klass.set_info = Some(video_filter_set_info::<T>);
182
183        match T::MODE {
184            BaseTransformMode::AlwaysInPlace => {
185                klass.transform_frame = None;
186                klass.transform_frame_ip = Some(video_filter_transform_frame_ip::<T>);
187            }
188            BaseTransformMode::NeverInPlace => {
189                klass.transform_frame = Some(video_filter_transform_frame::<T>);
190                klass.transform_frame_ip = None;
191            }
192            BaseTransformMode::Both => {
193                klass.transform_frame = Some(video_filter_transform_frame::<T>);
194                klass.transform_frame_ip = Some(video_filter_transform_frame_ip::<T>);
195            }
196        }
197    }
198}
199
200unsafe extern "C" fn video_filter_set_info<T: VideoFilterImpl>(
201    ptr: *mut ffi::GstVideoFilter,
202    incaps: *mut gst::ffi::GstCaps,
203    in_info: *mut ffi::GstVideoInfo,
204    outcaps: *mut gst::ffi::GstCaps,
205    out_info: *mut ffi::GstVideoInfo,
206) -> glib::ffi::gboolean {
207    unsafe {
208        let instance = &*(ptr as *mut T::Instance);
209        let imp = instance.imp();
210
211        gst::panic_to_error!(imp, false, {
212            match imp.set_info(
213                &from_glib_borrow(incaps),
214                &from_glib_none(in_info),
215                &from_glib_borrow(outcaps),
216                &from_glib_none(out_info),
217            ) {
218                Ok(()) => true,
219                Err(err) => {
220                    err.log_with_imp(imp);
221                    false
222                }
223            }
224        })
225        .into_glib()
226    }
227}
228
229unsafe extern "C" fn video_filter_transform_frame<T: VideoFilterImpl>(
230    ptr: *mut ffi::GstVideoFilter,
231    inframe: *mut ffi::GstVideoFrame,
232    outframe: *mut ffi::GstVideoFrame,
233) -> gst::ffi::GstFlowReturn {
234    unsafe {
235        let instance = &*(ptr as *mut T::Instance);
236        let imp = instance.imp();
237
238        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
239            imp.transform_frame(
240                &VideoFrameRef::from_glib_borrow(inframe),
241                &mut VideoFrameRef::from_glib_borrow_mut(outframe),
242            )
243            .into()
244        })
245        .into_glib()
246    }
247}
248
249unsafe extern "C" fn video_filter_transform_frame_ip<T: VideoFilterImpl>(
250    ptr: *mut ffi::GstVideoFilter,
251    frame: *mut ffi::GstVideoFrame,
252) -> gst::ffi::GstFlowReturn {
253    unsafe {
254        let instance = &*(ptr as *mut T::Instance);
255        let imp = instance.imp();
256
257        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
258            if from_glib(gst_base::ffi::gst_base_transform_is_passthrough(
259                ptr as *mut gst_base::ffi::GstBaseTransform,
260            )) {
261                imp.transform_frame_ip_passthrough(&VideoFrameRef::from_glib_borrow(frame))
262                    .into()
263            } else {
264                imp.transform_frame_ip(&mut VideoFrameRef::from_glib_borrow_mut(frame))
265                    .into()
266            }
267        })
268        .into_glib()
269    }
270}