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