1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::ptr; |
4 | |
5 | use glib::{prelude::*, subclass::prelude::*, translate::*}; |
6 | use gst::prelude::*; |
7 | |
8 | use super::base_src::{BaseSrcImpl, CreateSuccess}; |
9 | use crate::{prelude::BaseSrcExtManual, PushSrc}; |
10 | |
11 | pub trait PushSrcImpl: PushSrcImplExt + BaseSrcImpl { |
12 | fn fill(&self, buffer: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> { |
13 | PushSrcImplExt::parent_fill(self, buffer) |
14 | } |
15 | |
16 | fn alloc(&self) -> Result<gst::Buffer, gst::FlowError> { |
17 | PushSrcImplExt::parent_alloc(self) |
18 | } |
19 | |
20 | fn create(&self, buffer: Option<&mut gst::BufferRef>) -> Result<CreateSuccess, gst::FlowError> { |
21 | PushSrcImplExt::parent_create(self, buffer) |
22 | } |
23 | } |
24 | |
25 | mod sealed { |
26 | pub trait Sealed {} |
27 | impl<T: super::PushSrcImplExt> Sealed for T {} |
28 | } |
29 | |
30 | pub trait PushSrcImplExt: sealed::Sealed + ObjectSubclass { |
31 | fn parent_fill(&self, buffer: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> { |
32 | unsafe { |
33 | let data = Self::type_data(); |
34 | let parent_class = data.as_ref().parent_class() as *mut ffi::GstPushSrcClass; |
35 | (*parent_class) |
36 | .fill |
37 | .map(|f| { |
38 | try_from_glib(f( |
39 | self.obj().unsafe_cast_ref::<PushSrc>().to_glib_none().0, |
40 | buffer.as_mut_ptr(), |
41 | )) |
42 | }) |
43 | .unwrap_or(Err(gst::FlowError::NotSupported)) |
44 | } |
45 | } |
46 | |
47 | fn parent_alloc(&self) -> Result<gst::Buffer, gst::FlowError> { |
48 | unsafe { |
49 | let data = Self::type_data(); |
50 | let parent_class = data.as_ref().parent_class() as *mut ffi::GstPushSrcClass; |
51 | (*parent_class) |
52 | .alloc |
53 | .map(|f| { |
54 | let mut buffer_ptr: *mut gst::ffi::GstBuffer = ptr::null_mut(); |
55 | |
56 | // FIXME: Wrong signature in -sys bindings |
57 | // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3 |
58 | let buffer_ref = &mut buffer_ptr as *mut _ as *mut gst::ffi::GstBuffer; |
59 | |
60 | gst::FlowSuccess::try_from_glib(f( |
61 | self.obj().unsafe_cast_ref::<PushSrc>().to_glib_none().0, |
62 | buffer_ref, |
63 | )) |
64 | .map(|_| from_glib_full(buffer_ref)) |
65 | }) |
66 | .unwrap_or(Err(gst::FlowError::NotSupported)) |
67 | } |
68 | } |
69 | |
70 | fn parent_create( |
71 | &self, |
72 | mut buffer: Option<&mut gst::BufferRef>, |
73 | ) -> Result<CreateSuccess, gst::FlowError> { |
74 | unsafe { |
75 | let data = Self::type_data(); |
76 | let parent_class = data.as_ref().parent_class() as *mut ffi::GstPushSrcClass; |
77 | (*parent_class) |
78 | .create |
79 | .map(|f| { |
80 | let instance = self.obj(); |
81 | let instance = instance.unsafe_cast_ref::<PushSrc>(); |
82 | let orig_buffer_ptr = buffer |
83 | .as_mut() |
84 | .map(|b| b.as_mut_ptr()) |
85 | .unwrap_or(ptr::null_mut()); |
86 | let mut buffer_ptr = orig_buffer_ptr; |
87 | |
88 | // FIXME: Wrong signature in -sys bindings |
89 | // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3 |
90 | let buffer_ref = &mut buffer_ptr as *mut _ as *mut gst::ffi::GstBuffer; |
91 | let instance_data = self.instance_data::<super::base_src::InstanceData>(crate::BaseSrc::static_type()).unwrap(); |
92 | |
93 | if let Err(err) = gst::FlowSuccess::try_from_glib( |
94 | f( |
95 | instance.to_glib_none().0, |
96 | buffer_ref, |
97 | ) |
98 | ) { |
99 | *instance_data.pending_buffer_list.borrow_mut() = None; |
100 | return Err(err); |
101 | } |
102 | |
103 | let pending_buffer_list = instance_data.pending_buffer_list.borrow_mut().take(); |
104 | if pending_buffer_list.is_some() && |
105 | (buffer.is_some() || instance.src_pad().mode() == gst::PadMode::Pull) { |
106 | panic!("Buffer lists can only be returned in push mode" ); |
107 | } |
108 | |
109 | let pending_buffer_list = instance_data.pending_buffer_list.borrow_mut().take(); |
110 | if buffer_ptr.is_null() && pending_buffer_list.is_none() { |
111 | gst::error!( |
112 | gst::CAT_RUST, |
113 | obj: instance, |
114 | "No buffer and no buffer list returned" |
115 | ); |
116 | return Err(gst::FlowError::Error); |
117 | } |
118 | |
119 | if !buffer_ptr.is_null() && pending_buffer_list.is_some() { |
120 | gst::error!( |
121 | gst::CAT_RUST, |
122 | obj: instance, |
123 | "Both buffer and buffer list returned" |
124 | ); |
125 | return Err(gst::FlowError::Error); |
126 | } |
127 | |
128 | if let Some(passed_buffer) = buffer { |
129 | if buffer_ptr != orig_buffer_ptr { |
130 | let new_buffer = gst::Buffer::from_glib_full(buffer_ptr); |
131 | |
132 | gst::debug!( |
133 | gst::CAT_PERFORMANCE, |
134 | obj: instance, |
135 | "Returned new buffer from parent create function, copying into passed buffer" |
136 | ); |
137 | |
138 | let mut map = match passed_buffer.map_writable() { |
139 | Ok(map) => map, |
140 | Err(_) => { |
141 | gst::error!( |
142 | gst::CAT_RUST, |
143 | obj: instance, |
144 | "Failed to map passed buffer writable" |
145 | ); |
146 | return Err(gst::FlowError::Error); |
147 | } |
148 | }; |
149 | |
150 | let copied_size = new_buffer.copy_to_slice(0, &mut map); |
151 | drop(map); |
152 | |
153 | if let Err(copied_size) = copied_size { |
154 | passed_buffer.set_size(copied_size); |
155 | } |
156 | |
157 | match new_buffer.copy_into(passed_buffer, gst::BUFFER_COPY_METADATA, 0, None) { |
158 | Ok(_) => Ok(CreateSuccess::FilledBuffer), |
159 | Err(_) => { |
160 | gst::error!( |
161 | gst::CAT_RUST, |
162 | obj: instance, |
163 | "Failed to copy buffer metadata" |
164 | ); |
165 | |
166 | Err(gst::FlowError::Error) |
167 | } |
168 | } |
169 | } else { |
170 | Ok(CreateSuccess::FilledBuffer) |
171 | } |
172 | } else if let Some(buffer_list) = pending_buffer_list { |
173 | Ok(CreateSuccess::NewBufferList(buffer_list)) |
174 | } else { |
175 | Ok(CreateSuccess::NewBuffer(from_glib_full(buffer_ptr))) |
176 | } |
177 | }) |
178 | .unwrap_or(Err(gst::FlowError::NotSupported)) |
179 | } |
180 | } |
181 | } |
182 | |
183 | impl<T: PushSrcImpl> PushSrcImplExt for T {} |
184 | |
185 | unsafe impl<T: PushSrcImpl> IsSubclassable<T> for PushSrc { |
186 | fn class_init(klass: &mut glib::Class<Self>) { |
187 | Self::parent_class_init::<T>(class:klass); |
188 | let klass: &mut GstPushSrcClass = klass.as_mut(); |
189 | klass.fill = Some(push_src_fill::<T>); |
190 | klass.alloc = Some(push_src_alloc::<T>); |
191 | klass.create = Some(push_src_create::<T>); |
192 | } |
193 | } |
194 | |
195 | unsafe extern "C" fn push_src_fill<T: PushSrcImpl>( |
196 | ptr: *mut ffi::GstPushSrc, |
197 | buffer: *mut gst::ffi::GstBuffer, |
198 | ) -> gst::ffi::GstFlowReturn { |
199 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
200 | let imp: &T = instance.imp(); |
201 | let buffer: &mut BufferRef = gst::BufferRef::from_mut_ptr(buffer); |
202 | |
203 | gstFlowReturn::panic_to_error!(imp, gst::FlowReturn::Error, { |
204 | PushSrcImpl::fill(imp, buffer).into() |
205 | }) |
206 | .into_glib() |
207 | } |
208 | |
209 | unsafe extern "C" fn push_src_alloc<T: PushSrcImpl>( |
210 | ptr: *mut ffi::GstPushSrc, |
211 | buffer_ptr: *mut gst::ffi::GstBuffer, |
212 | ) -> gst::ffi::GstFlowReturn { |
213 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
214 | let imp: &T = instance.imp(); |
215 | // FIXME: Wrong signature in -sys bindings |
216 | // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3 |
217 | let buffer_ptr: *mut *mut GstBuffer = buffer_ptr as *mut *mut gst::ffi::GstBuffer; |
218 | |
219 | gstFlowReturn::panic_to_error!(imp, gst::FlowReturn::Error, { |
220 | match PushSrcImpl::alloc(imp) { |
221 | Ok(buffer) => { |
222 | *buffer_ptr = buffer.into_glib_ptr(); |
223 | gst::FlowReturn::Ok |
224 | } |
225 | Err(err) => gst::FlowReturn::from(err), |
226 | } |
227 | }) |
228 | .into_glib() |
229 | } |
230 | |
231 | #[allow (clippy::needless_option_as_deref)] |
232 | unsafe extern "C" fn push_src_create<T: PushSrcImpl>( |
233 | ptr: *mut ffi::GstPushSrc, |
234 | buffer_ptr: *mut gst::ffi::GstBuffer, |
235 | ) -> gst::ffi::GstFlowReturn { |
236 | let instance = &*(ptr as *mut T::Instance); |
237 | let imp = instance.imp(); |
238 | // FIXME: Wrong signature in -sys bindings |
239 | // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3 |
240 | let buffer_ptr = buffer_ptr as *mut *mut gst::ffi::GstBuffer; |
241 | |
242 | let mut buffer = if (*buffer_ptr).is_null() { |
243 | None |
244 | } else { |
245 | Some(gst::BufferRef::from_mut_ptr(*buffer_ptr)) |
246 | }; |
247 | |
248 | let instance_data = imp |
249 | .instance_data::<super::base_src::InstanceData>(crate::BaseSrc::static_type()) |
250 | .unwrap(); |
251 | |
252 | gst::panic_to_error!(imp, gst::FlowReturn::Error, { |
253 | match PushSrcImpl::create(imp, buffer.as_deref_mut()) { |
254 | Ok(CreateSuccess::NewBuffer(new_buffer)) => { |
255 | // Clear any pending buffer list |
256 | *instance_data.pending_buffer_list.borrow_mut() = None; |
257 | |
258 | if let Some(passed_buffer) = buffer { |
259 | if passed_buffer.as_ptr() != new_buffer.as_ptr() { |
260 | gst::debug!( |
261 | gst::CAT_PERFORMANCE, |
262 | imp: imp, |
263 | "Returned new buffer from create function, copying into passed buffer" |
264 | ); |
265 | |
266 | let mut map = match passed_buffer.map_writable() { |
267 | Ok(map) => map, |
268 | Err(_) => { |
269 | gst::error!( |
270 | gst::CAT_RUST, |
271 | imp: imp, |
272 | "Failed to map passed buffer writable" |
273 | ); |
274 | return gst::FlowReturn::Error; |
275 | } |
276 | }; |
277 | |
278 | let copied_size = new_buffer.copy_to_slice(0, &mut map); |
279 | drop(map); |
280 | |
281 | if let Err(copied_size) = copied_size { |
282 | passed_buffer.set_size(copied_size); |
283 | } |
284 | |
285 | match new_buffer.copy_into( |
286 | passed_buffer, |
287 | gst::BUFFER_COPY_METADATA, |
288 | 0, |
289 | None, |
290 | ) { |
291 | Ok(_) => gst::FlowReturn::Ok, |
292 | Err(_) => { |
293 | gst::error!( |
294 | gst::CAT_RUST, |
295 | imp: imp, |
296 | "Failed to copy buffer metadata" |
297 | ); |
298 | |
299 | gst::FlowReturn::Error |
300 | } |
301 | } |
302 | } else { |
303 | gst::FlowReturn::Ok |
304 | } |
305 | } else { |
306 | *buffer_ptr = new_buffer.into_glib_ptr(); |
307 | gst::FlowReturn::Ok |
308 | } |
309 | } |
310 | Ok(CreateSuccess::NewBufferList(new_buffer_list)) => { |
311 | if buffer.is_some() |
312 | || imp.obj().unsafe_cast_ref::<PushSrc>().src_pad().mode() == gst::PadMode::Pull |
313 | { |
314 | panic!("Buffer lists can only be returned in push mode" ); |
315 | } |
316 | |
317 | *buffer_ptr = ptr::null_mut(); |
318 | |
319 | // Store it in the instance data so that in the end base_src_create() can |
320 | // submit it. |
321 | *instance_data.pending_buffer_list.borrow_mut() = Some(new_buffer_list); |
322 | |
323 | gst::FlowReturn::Ok |
324 | } |
325 | Ok(CreateSuccess::FilledBuffer) => { |
326 | // Clear any pending buffer list |
327 | *instance_data.pending_buffer_list.borrow_mut() = None; |
328 | |
329 | gst::FlowReturn::Ok |
330 | } |
331 | Err(err) => gst::FlowReturn::from(err), |
332 | } |
333 | }) |
334 | .into_glib() |
335 | } |
336 | |