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    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, ..) {
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>(klass);
188        let klass = 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 = &*(ptr as *mut T::Instance);
200    let imp = instance.imp();
201    let buffer = gst::BufferRef::from_mut_ptr(buffer);
202
203    gst::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 = &*(ptr as *mut T::Instance);
214    let imp = instance.imp();
215    // FIXME: Wrong signature in -sys bindings
216    // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3
217    let buffer_ptr = buffer_ptr as *mut *mut gst::ffi::GstBuffer;
218
219    gst::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(passed_buffer, gst::BUFFER_COPY_METADATA, ..) {
286                            Ok(_) => gst::FlowReturn::Ok,
287                            Err(_) => {
288                                gst::error!(
289                                    gst::CAT_RUST,
290                                    imp = imp,
291                                    "Failed to copy buffer metadata"
292                                );
293
294                                gst::FlowReturn::Error
295                            }
296                        }
297                    } else {
298                        gst::FlowReturn::Ok
299                    }
300                } else {
301                    *buffer_ptr = new_buffer.into_glib_ptr();
302                    gst::FlowReturn::Ok
303                }
304            }
305            Ok(CreateSuccess::NewBufferList(new_buffer_list)) => {
306                if buffer.is_some()
307                    || imp.obj().unsafe_cast_ref::<PushSrc>().src_pad().mode() == gst::PadMode::Pull
308                {
309                    panic!("Buffer lists can only be returned in push mode");
310                }
311
312                *buffer_ptr = ptr::null_mut();
313
314                // Store it in the instance data so that in the end base_src_create() can
315                // submit it.
316                *instance_data.pending_buffer_list.borrow_mut() = Some(new_buffer_list);
317
318                gst::FlowReturn::Ok
319            }
320            Ok(CreateSuccess::FilledBuffer) => {
321                // Clear any pending buffer list
322                *instance_data.pending_buffer_list.borrow_mut() = None;
323
324                gst::FlowReturn::Ok
325            }
326            Err(err) => gst::FlowReturn::from(err),
327        }
328    })
329    .into_glib()
330}