| 1 | // Take a look at the license at the top of the repository in the LICENSE file. |
| 2 | |
| 3 | use glib::translate::*; |
| 4 | use gst_base::{prelude::*, subclass::prelude::*}; |
| 5 | |
| 6 | use crate::{ffi, VideoFilter, VideoFrameExt, VideoFrameRef, VideoInfo}; |
| 7 | |
| 8 | pub 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 | |
| 42 | mod sealed { |
| 43 | pub trait Sealed {} |
| 44 | impl<T: super::VideoFilterImplExt> Sealed for T {} |
| 45 | } |
| 46 | |
| 47 | pub 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 | |
| 174 | impl<T: VideoFilterImpl> VideoFilterImplExt for T {} |
| 175 | |
| 176 | unsafe 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 | |
| 202 | unsafe 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 | |
| 229 | unsafe 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 | |
| 247 | unsafe 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 | |