1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::ptr;
4
5use glib::{prelude::*, subclass::prelude::*, translate::*};
6use gst::prelude::*;
7
8use super::base_src::{BaseSrcImpl, CreateSuccess};
9use crate::{prelude::BaseSrcExtManual, PushSrc};
10
11pub 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
25mod sealed {
26 pub trait Sealed {}
27 impl<T: super::PushSrcImplExt> Sealed for T {}
28}
29
30pub 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
183impl<T: PushSrcImpl> PushSrcImplExt for T {}
184
185unsafe 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
195unsafe 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
209unsafe 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)]
232unsafe 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