Skip to main content

gstreamer_base/subclass/
base_sink.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::*, translate::*};
6use gst::subclass::prelude::*;
7
8use crate::{BaseSink, ffi};
9
10pub trait BaseSinkImpl: ElementImpl + ObjectSubclass<Type: IsA<BaseSink>> {
11    /// Start processing. Ideal for opening resources in the subclass
12    fn start(&self) -> Result<(), gst::ErrorMessage> {
13        self.parent_start()
14    }
15
16    /// Stop processing. Subclasses should use this to close resources.
17    fn stop(&self) -> Result<(), gst::ErrorMessage> {
18        self.parent_stop()
19    }
20
21    /// Called when a buffer should be presented or output, at the
22    ///  correct moment if the [`BaseSink`][crate::BaseSink] has been set to sync to the clock.
23    fn render(&self, buffer: &gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
24        self.parent_render(buffer)
25    }
26
27    /// Called to prepare the buffer for `render` and `preroll`. This
28    ///  function is called before synchronisation is performed.
29    fn prepare(&self, buffer: &gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
30        self.parent_prepare(buffer)
31    }
32
33    /// Same as `render` but used with buffer lists instead of
34    ///  buffers.
35    fn render_list(&self, list: &gst::BufferList) -> Result<gst::FlowSuccess, gst::FlowError> {
36        self.parent_render_list(list)
37    }
38
39    /// Called to prepare the buffer list for `render_list`. This
40    ///  function is called before synchronisation is performed.
41    fn prepare_list(&self, list: &gst::BufferList) -> Result<gst::FlowSuccess, gst::FlowError> {
42        self.parent_prepare_list(list)
43    }
44
45    /// perform a `GstQuery` on the element.
46    fn query(&self, query: &mut gst::QueryRef) -> bool {
47        BaseSinkImplExt::parent_query(self, query)
48    }
49
50    /// Override this to handle events arriving on the sink pad
51    fn event(&self, event: gst::Event) -> bool {
52        self.parent_event(event)
53    }
54
55    /// Called to get sink pad caps from the subclass.
56    fn caps(&self, filter: Option<&gst::Caps>) -> Option<gst::Caps> {
57        self.parent_caps(filter)
58    }
59
60    /// Notify subclass of changed caps
61    fn set_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
62        self.parent_set_caps(caps)
63    }
64
65    /// Only useful in pull mode. Implement if you have
66    ///  ideas about what should be the default values for the caps you support.
67    fn fixate(&self, caps: gst::Caps) -> gst::Caps {
68        self.parent_fixate(caps)
69    }
70
71    /// Unlock any pending access to the resource. Subclasses should
72    ///  unblock any blocked function ASAP and call [`BaseSinkExt::wait_preroll()`][crate::prelude::BaseSinkExt::wait_preroll()]
73    fn unlock(&self) -> Result<(), gst::ErrorMessage> {
74        self.parent_unlock()
75    }
76
77    /// Clear the previous unlock request. Subclasses should clear
78    ///  any state they set during `GstBaseSinkClass::unlock`, and be ready to
79    ///  continue where they left off after [`BaseSinkExt::wait_preroll()`][crate::prelude::BaseSinkExt::wait_preroll()],
80    ///  [`BaseSinkExt::wait()`][crate::prelude::BaseSinkExt::wait()] or `gst_wait_sink_wait_clock()` return or
81    ///  `GstBaseSinkClass::render` is called again.
82    fn unlock_stop(&self) -> Result<(), gst::ErrorMessage> {
83        self.parent_unlock_stop()
84    }
85
86    /// configure the allocation query
87    fn propose_allocation(
88        &self,
89        query: &mut gst::query::Allocation,
90    ) -> Result<(), gst::LoggableError> {
91        self.parent_propose_allocation(query)
92    }
93}
94
95pub trait BaseSinkImplExt: BaseSinkImpl {
96    fn parent_start(&self) -> Result<(), gst::ErrorMessage> {
97        unsafe {
98            let data = Self::type_data();
99            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
100            (*parent_class)
101                .start
102                .map(|f| {
103                    if from_glib(f(self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0)) {
104                        Ok(())
105                    } else {
106                        Err(gst::error_msg!(
107                            gst::CoreError::StateChange,
108                            ["Parent function `start` failed"]
109                        ))
110                    }
111                })
112                .unwrap_or(Ok(()))
113        }
114    }
115
116    fn parent_stop(&self) -> Result<(), gst::ErrorMessage> {
117        unsafe {
118            let data = Self::type_data();
119            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
120            (*parent_class)
121                .stop
122                .map(|f| {
123                    if from_glib(f(self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0)) {
124                        Ok(())
125                    } else {
126                        Err(gst::error_msg!(
127                            gst::CoreError::StateChange,
128                            ["Parent function `stop` failed"]
129                        ))
130                    }
131                })
132                .unwrap_or(Ok(()))
133        }
134    }
135
136    fn parent_render(&self, buffer: &gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
137        unsafe {
138            let data = Self::type_data();
139            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
140            (*parent_class)
141                .render
142                .map(|f| {
143                    try_from_glib(f(
144                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
145                        buffer.to_glib_none().0,
146                    ))
147                })
148                .unwrap_or(Ok(gst::FlowSuccess::Ok))
149        }
150    }
151
152    fn parent_prepare(&self, buffer: &gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
153        unsafe {
154            let data = Self::type_data();
155            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
156            (*parent_class)
157                .prepare
158                .map(|f| {
159                    try_from_glib(f(
160                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
161                        buffer.to_glib_none().0,
162                    ))
163                })
164                .unwrap_or(Ok(gst::FlowSuccess::Ok))
165        }
166    }
167
168    fn parent_render_list(
169        &self,
170        list: &gst::BufferList,
171    ) -> Result<gst::FlowSuccess, gst::FlowError>;
172
173    fn parent_prepare_list(
174        &self,
175        list: &gst::BufferList,
176    ) -> Result<gst::FlowSuccess, gst::FlowError>;
177
178    fn parent_query(&self, query: &mut gst::QueryRef) -> bool {
179        unsafe {
180            let data = Self::type_data();
181            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
182            (*parent_class)
183                .query
184                .map(|f| {
185                    from_glib(f(
186                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
187                        query.as_mut_ptr(),
188                    ))
189                })
190                .unwrap_or(false)
191        }
192    }
193
194    fn parent_event(&self, event: gst::Event) -> bool {
195        unsafe {
196            let data = Self::type_data();
197            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
198            (*parent_class)
199                .event
200                .map(|f| {
201                    from_glib(f(
202                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
203                        event.into_glib_ptr(),
204                    ))
205                })
206                .unwrap_or(true)
207        }
208    }
209
210    fn parent_caps(&self, filter: Option<&gst::Caps>) -> Option<gst::Caps> {
211        unsafe {
212            let data = Self::type_data();
213            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
214
215            (*parent_class)
216                .get_caps
217                .map(|f| {
218                    from_glib_full(f(
219                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
220                        filter.to_glib_none().0,
221                    ))
222                })
223                .unwrap_or(None)
224        }
225    }
226
227    fn parent_set_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
228        unsafe {
229            let data = Self::type_data();
230            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
231            (*parent_class)
232                .set_caps
233                .map(|f| {
234                    gst::result_from_gboolean!(
235                        f(
236                            self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
237                            caps.to_glib_none().0
238                        ),
239                        gst::CAT_RUST,
240                        "Parent function `set_caps` failed"
241                    )
242                })
243                .unwrap_or(Ok(()))
244        }
245    }
246
247    fn parent_fixate(&self, caps: gst::Caps) -> gst::Caps {
248        unsafe {
249            let data = Self::type_data();
250            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
251
252            match (*parent_class).fixate {
253                Some(fixate) => from_glib_full(fixate(
254                    self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
255                    caps.into_glib_ptr(),
256                )),
257                None => caps,
258            }
259        }
260    }
261
262    fn parent_unlock(&self) -> Result<(), gst::ErrorMessage> {
263        unsafe {
264            let data = Self::type_data();
265            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
266            (*parent_class)
267                .unlock
268                .map(|f| {
269                    if from_glib(f(self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0)) {
270                        Ok(())
271                    } else {
272                        Err(gst::error_msg!(
273                            gst::CoreError::Failed,
274                            ["Parent function `unlock` failed"]
275                        ))
276                    }
277                })
278                .unwrap_or(Ok(()))
279        }
280    }
281
282    fn parent_unlock_stop(&self) -> Result<(), gst::ErrorMessage> {
283        unsafe {
284            let data = Self::type_data();
285            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
286            (*parent_class)
287                .unlock_stop
288                .map(|f| {
289                    if from_glib(f(self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0)) {
290                        Ok(())
291                    } else {
292                        Err(gst::error_msg!(
293                            gst::CoreError::Failed,
294                            ["Parent function `unlock_stop` failed"]
295                        ))
296                    }
297                })
298                .unwrap_or(Ok(()))
299        }
300    }
301
302    fn parent_propose_allocation(
303        &self,
304        query: &mut gst::query::Allocation,
305    ) -> Result<(), gst::LoggableError> {
306        unsafe {
307            let data = Self::type_data();
308            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
309            (*parent_class)
310                .propose_allocation
311                .map(|f| {
312                    gst::result_from_gboolean!(
313                        f(
314                            self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
315                            query.as_mut_ptr(),
316                        ),
317                        gst::CAT_RUST,
318                        "Parent function `propose_allocation` failed",
319                    )
320                })
321                .unwrap_or(Ok(()))
322        }
323    }
324}
325
326impl<T: BaseSinkImpl> BaseSinkImplExt for T {
327    fn parent_render_list(
328        &self,
329        list: &gst::BufferList,
330    ) -> Result<gst::FlowSuccess, gst::FlowError> {
331        unsafe {
332            let data = Self::type_data();
333            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
334            (*parent_class)
335                .render_list
336                .map(|f| {
337                    try_from_glib(f(
338                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
339                        list.to_glib_none().0,
340                    ))
341                })
342                .unwrap_or_else(|| {
343                    for buffer in list.iter() {
344                        self.render(&from_glib_borrow(buffer.as_ptr()))?;
345                    }
346                    Ok(gst::FlowSuccess::Ok)
347                })
348        }
349    }
350
351    fn parent_prepare_list(
352        &self,
353        list: &gst::BufferList,
354    ) -> Result<gst::FlowSuccess, gst::FlowError> {
355        unsafe {
356            let data = Self::type_data();
357            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
358            (*parent_class)
359                .prepare_list
360                .map(|f| {
361                    try_from_glib(f(
362                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
363                        list.to_glib_none().0,
364                    ))
365                })
366                .unwrap_or_else(|| {
367                    for buffer in list.iter() {
368                        self.prepare(&from_glib_borrow(buffer.as_ptr()))?;
369                    }
370                    Ok(gst::FlowSuccess::Ok)
371                })
372        }
373    }
374}
375
376unsafe impl<T: BaseSinkImpl> IsSubclassable<T> for BaseSink {
377    fn class_init(klass: &mut glib::Class<Self>) {
378        Self::parent_class_init::<T>(klass);
379        let klass = klass.as_mut();
380        klass.start = Some(base_sink_start::<T>);
381        klass.stop = Some(base_sink_stop::<T>);
382        klass.render = Some(base_sink_render::<T>);
383        klass.render_list = Some(base_sink_render_list::<T>);
384        klass.prepare = Some(base_sink_prepare::<T>);
385        klass.prepare_list = Some(base_sink_prepare_list::<T>);
386        klass.query = Some(base_sink_query::<T>);
387        klass.event = Some(base_sink_event::<T>);
388        klass.get_caps = Some(base_sink_get_caps::<T>);
389        klass.set_caps = Some(base_sink_set_caps::<T>);
390        klass.fixate = Some(base_sink_fixate::<T>);
391        klass.unlock = Some(base_sink_unlock::<T>);
392        klass.unlock_stop = Some(base_sink_unlock_stop::<T>);
393        klass.propose_allocation = Some(base_sink_propose_allocation::<T>);
394    }
395}
396
397unsafe extern "C" fn base_sink_start<T: BaseSinkImpl>(
398    ptr: *mut ffi::GstBaseSink,
399) -> glib::ffi::gboolean {
400    unsafe {
401        let instance = &*(ptr as *mut T::Instance);
402        let imp = instance.imp();
403
404        gst::panic_to_error!(imp, false, {
405            match imp.start() {
406                Ok(()) => true,
407                Err(err) => {
408                    imp.post_error_message(err);
409                    false
410                }
411            }
412        })
413        .into_glib()
414    }
415}
416
417unsafe extern "C" fn base_sink_stop<T: BaseSinkImpl>(
418    ptr: *mut ffi::GstBaseSink,
419) -> glib::ffi::gboolean {
420    unsafe {
421        let instance = &*(ptr as *mut T::Instance);
422        let imp = instance.imp();
423
424        gst::panic_to_error!(imp, false, {
425            match imp.stop() {
426                Ok(()) => true,
427                Err(err) => {
428                    imp.post_error_message(err);
429                    false
430                }
431            }
432        })
433        .into_glib()
434    }
435}
436
437unsafe extern "C" fn base_sink_render<T: BaseSinkImpl>(
438    ptr: *mut ffi::GstBaseSink,
439    buffer: *mut gst::ffi::GstBuffer,
440) -> gst::ffi::GstFlowReturn {
441    unsafe {
442        let instance = &*(ptr as *mut T::Instance);
443        let imp = instance.imp();
444        let buffer = from_glib_borrow(buffer);
445
446        gst::panic_to_error!(imp, gst::FlowReturn::Error, { imp.render(&buffer).into() })
447            .into_glib()
448    }
449}
450
451unsafe extern "C" fn base_sink_prepare<T: BaseSinkImpl>(
452    ptr: *mut ffi::GstBaseSink,
453    buffer: *mut gst::ffi::GstBuffer,
454) -> gst::ffi::GstFlowReturn {
455    unsafe {
456        let instance = &*(ptr as *mut T::Instance);
457        let imp = instance.imp();
458        let buffer = from_glib_borrow(buffer);
459
460        gst::panic_to_error!(imp, gst::FlowReturn::Error, { imp.prepare(&buffer).into() })
461            .into_glib()
462    }
463}
464
465unsafe extern "C" fn base_sink_render_list<T: BaseSinkImpl>(
466    ptr: *mut ffi::GstBaseSink,
467    list: *mut gst::ffi::GstBufferList,
468) -> gst::ffi::GstFlowReturn {
469    unsafe {
470        let instance = &*(ptr as *mut T::Instance);
471        let imp = instance.imp();
472        let list = from_glib_borrow(list);
473
474        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
475            imp.render_list(&list).into()
476        })
477        .into_glib()
478    }
479}
480
481unsafe extern "C" fn base_sink_prepare_list<T: BaseSinkImpl>(
482    ptr: *mut ffi::GstBaseSink,
483    list: *mut gst::ffi::GstBufferList,
484) -> gst::ffi::GstFlowReturn {
485    unsafe {
486        let instance = &*(ptr as *mut T::Instance);
487        let imp = instance.imp();
488        let list = from_glib_borrow(list);
489
490        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
491            imp.prepare_list(&list).into()
492        })
493        .into_glib()
494    }
495}
496
497unsafe extern "C" fn base_sink_query<T: BaseSinkImpl>(
498    ptr: *mut ffi::GstBaseSink,
499    query_ptr: *mut gst::ffi::GstQuery,
500) -> glib::ffi::gboolean {
501    unsafe {
502        let instance = &*(ptr as *mut T::Instance);
503        let imp = instance.imp();
504        let query = gst::QueryRef::from_mut_ptr(query_ptr);
505
506        gst::panic_to_error!(imp, false, { BaseSinkImpl::query(imp, query) }).into_glib()
507    }
508}
509
510unsafe extern "C" fn base_sink_event<T: BaseSinkImpl>(
511    ptr: *mut ffi::GstBaseSink,
512    event_ptr: *mut gst::ffi::GstEvent,
513) -> glib::ffi::gboolean {
514    unsafe {
515        let instance = &*(ptr as *mut T::Instance);
516        let imp = instance.imp();
517
518        gst::panic_to_error!(imp, false, { imp.event(from_glib_full(event_ptr)) }).into_glib()
519    }
520}
521
522unsafe extern "C" fn base_sink_get_caps<T: BaseSinkImpl>(
523    ptr: *mut ffi::GstBaseSink,
524    filter: *mut gst::ffi::GstCaps,
525) -> *mut gst::ffi::GstCaps {
526    unsafe {
527        let instance = &*(ptr as *mut T::Instance);
528        let imp = instance.imp();
529        let filter = Option::<gst::Caps>::from_glib_borrow(filter);
530
531        gst::panic_to_error!(imp, None, { imp.caps(filter.as_ref().as_ref()) })
532            .map(|caps| caps.into_glib_ptr())
533            .unwrap_or(ptr::null_mut())
534    }
535}
536
537unsafe extern "C" fn base_sink_set_caps<T: BaseSinkImpl>(
538    ptr: *mut ffi::GstBaseSink,
539    caps: *mut gst::ffi::GstCaps,
540) -> glib::ffi::gboolean {
541    unsafe {
542        let instance = &*(ptr as *mut T::Instance);
543        let imp = instance.imp();
544        let caps = from_glib_borrow(caps);
545
546        gst::panic_to_error!(imp, false, {
547            match imp.set_caps(&caps) {
548                Ok(()) => true,
549                Err(err) => {
550                    err.log_with_imp(imp);
551                    false
552                }
553            }
554        })
555        .into_glib()
556    }
557}
558
559unsafe extern "C" fn base_sink_fixate<T: BaseSinkImpl>(
560    ptr: *mut ffi::GstBaseSink,
561    caps: *mut gst::ffi::GstCaps,
562) -> *mut gst::ffi::GstCaps {
563    unsafe {
564        let instance = &*(ptr as *mut T::Instance);
565        let imp = instance.imp();
566        let caps = from_glib_full(caps);
567
568        gst::panic_to_error!(imp, gst::Caps::new_empty(), { imp.fixate(caps) }).into_glib_ptr()
569    }
570}
571
572unsafe extern "C" fn base_sink_unlock<T: BaseSinkImpl>(
573    ptr: *mut ffi::GstBaseSink,
574) -> glib::ffi::gboolean {
575    unsafe {
576        let instance = &*(ptr as *mut T::Instance);
577        let imp = instance.imp();
578
579        gst::panic_to_error!(imp, false, {
580            match imp.unlock() {
581                Ok(()) => true,
582                Err(err) => {
583                    imp.post_error_message(err);
584                    false
585                }
586            }
587        })
588        .into_glib()
589    }
590}
591
592unsafe extern "C" fn base_sink_unlock_stop<T: BaseSinkImpl>(
593    ptr: *mut ffi::GstBaseSink,
594) -> glib::ffi::gboolean {
595    unsafe {
596        let instance = &*(ptr as *mut T::Instance);
597        let imp = instance.imp();
598
599        gst::panic_to_error!(imp, false, {
600            match imp.unlock_stop() {
601                Ok(()) => true,
602                Err(err) => {
603                    imp.post_error_message(err);
604                    false
605                }
606            }
607        })
608        .into_glib()
609    }
610}
611
612unsafe extern "C" fn base_sink_propose_allocation<T: BaseSinkImpl>(
613    ptr: *mut ffi::GstBaseSink,
614    query: *mut gst::ffi::GstQuery,
615) -> glib::ffi::gboolean {
616    unsafe {
617        let instance = &*(ptr as *mut T::Instance);
618        let imp = instance.imp();
619        let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
620            gst::QueryViewMut::Allocation(allocation) => allocation,
621            _ => unreachable!(),
622        };
623
624        gst::panic_to_error!(imp, false, {
625            match imp.propose_allocation(query) {
626                Ok(()) => true,
627                Err(err) => {
628                    err.log_with_imp_and_level(imp, gst::DebugLevel::Info);
629                    false
630                }
631            }
632        })
633        .into_glib()
634    }
635}