gstreamer_base/subclass/
push_src.rs

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