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