Skip to main content

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