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::{ffi, BaseSink};
9
10pub trait BaseSinkImpl: BaseSinkImplExt + ElementImpl {
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
95mod sealed {
96    pub trait Sealed {}
97    impl<T: super::BaseSinkImplExt> Sealed for T {}
98}
99
100pub trait BaseSinkImplExt: sealed::Sealed + ObjectSubclass {
101    fn parent_start(&self) -> Result<(), gst::ErrorMessage> {
102        unsafe {
103            let data = Self::type_data();
104            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
105            (*parent_class)
106                .start
107                .map(|f| {
108                    if from_glib(f(self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0)) {
109                        Ok(())
110                    } else {
111                        Err(gst::error_msg!(
112                            gst::CoreError::StateChange,
113                            ["Parent function `start` failed"]
114                        ))
115                    }
116                })
117                .unwrap_or(Ok(()))
118        }
119    }
120
121    fn parent_stop(&self) -> Result<(), gst::ErrorMessage> {
122        unsafe {
123            let data = Self::type_data();
124            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
125            (*parent_class)
126                .stop
127                .map(|f| {
128                    if from_glib(f(self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0)) {
129                        Ok(())
130                    } else {
131                        Err(gst::error_msg!(
132                            gst::CoreError::StateChange,
133                            ["Parent function `stop` failed"]
134                        ))
135                    }
136                })
137                .unwrap_or(Ok(()))
138        }
139    }
140
141    fn parent_render(&self, buffer: &gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
142        unsafe {
143            let data = Self::type_data();
144            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
145            (*parent_class)
146                .render
147                .map(|f| {
148                    try_from_glib(f(
149                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
150                        buffer.to_glib_none().0,
151                    ))
152                })
153                .unwrap_or(Ok(gst::FlowSuccess::Ok))
154        }
155    }
156
157    fn parent_prepare(&self, buffer: &gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
158        unsafe {
159            let data = Self::type_data();
160            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
161            (*parent_class)
162                .prepare
163                .map(|f| {
164                    try_from_glib(f(
165                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
166                        buffer.to_glib_none().0,
167                    ))
168                })
169                .unwrap_or(Ok(gst::FlowSuccess::Ok))
170        }
171    }
172
173    fn parent_render_list(
174        &self,
175        list: &gst::BufferList,
176    ) -> Result<gst::FlowSuccess, gst::FlowError>;
177
178    fn parent_prepare_list(
179        &self,
180        list: &gst::BufferList,
181    ) -> Result<gst::FlowSuccess, gst::FlowError>;
182
183    fn parent_query(&self, query: &mut gst::QueryRef) -> bool {
184        unsafe {
185            let data = Self::type_data();
186            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
187            (*parent_class)
188                .query
189                .map(|f| {
190                    from_glib(f(
191                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
192                        query.as_mut_ptr(),
193                    ))
194                })
195                .unwrap_or(false)
196        }
197    }
198
199    fn parent_event(&self, event: gst::Event) -> bool {
200        unsafe {
201            let data = Self::type_data();
202            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
203            (*parent_class)
204                .event
205                .map(|f| {
206                    from_glib(f(
207                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
208                        event.into_glib_ptr(),
209                    ))
210                })
211                .unwrap_or(true)
212        }
213    }
214
215    fn parent_caps(&self, filter: Option<&gst::Caps>) -> Option<gst::Caps> {
216        unsafe {
217            let data = Self::type_data();
218            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
219
220            (*parent_class)
221                .get_caps
222                .map(|f| {
223                    from_glib_full(f(
224                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
225                        filter.to_glib_none().0,
226                    ))
227                })
228                .unwrap_or(None)
229        }
230    }
231
232    fn parent_set_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
233        unsafe {
234            let data = Self::type_data();
235            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
236            (*parent_class)
237                .set_caps
238                .map(|f| {
239                    gst::result_from_gboolean!(
240                        f(
241                            self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
242                            caps.to_glib_none().0
243                        ),
244                        gst::CAT_RUST,
245                        "Parent function `set_caps` failed"
246                    )
247                })
248                .unwrap_or(Ok(()))
249        }
250    }
251
252    fn parent_fixate(&self, caps: gst::Caps) -> gst::Caps {
253        unsafe {
254            let data = Self::type_data();
255            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
256
257            match (*parent_class).fixate {
258                Some(fixate) => from_glib_full(fixate(
259                    self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
260                    caps.into_glib_ptr(),
261                )),
262                None => caps,
263            }
264        }
265    }
266
267    fn parent_unlock(&self) -> Result<(), gst::ErrorMessage> {
268        unsafe {
269            let data = Self::type_data();
270            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
271            (*parent_class)
272                .unlock
273                .map(|f| {
274                    if from_glib(f(self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0)) {
275                        Ok(())
276                    } else {
277                        Err(gst::error_msg!(
278                            gst::CoreError::Failed,
279                            ["Parent function `unlock` failed"]
280                        ))
281                    }
282                })
283                .unwrap_or(Ok(()))
284        }
285    }
286
287    fn parent_unlock_stop(&self) -> Result<(), gst::ErrorMessage> {
288        unsafe {
289            let data = Self::type_data();
290            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
291            (*parent_class)
292                .unlock_stop
293                .map(|f| {
294                    if from_glib(f(self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0)) {
295                        Ok(())
296                    } else {
297                        Err(gst::error_msg!(
298                            gst::CoreError::Failed,
299                            ["Parent function `unlock_stop` failed"]
300                        ))
301                    }
302                })
303                .unwrap_or(Ok(()))
304        }
305    }
306
307    fn parent_propose_allocation(
308        &self,
309        query: &mut gst::query::Allocation,
310    ) -> Result<(), gst::LoggableError> {
311        unsafe {
312            let data = Self::type_data();
313            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
314            (*parent_class)
315                .propose_allocation
316                .map(|f| {
317                    gst::result_from_gboolean!(
318                        f(
319                            self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
320                            query.as_mut_ptr(),
321                        ),
322                        gst::CAT_RUST,
323                        "Parent function `propose_allocation` failed",
324                    )
325                })
326                .unwrap_or(Ok(()))
327        }
328    }
329}
330
331impl<T: BaseSinkImpl> BaseSinkImplExt for T {
332    fn parent_render_list(
333        &self,
334        list: &gst::BufferList,
335    ) -> Result<gst::FlowSuccess, gst::FlowError> {
336        unsafe {
337            let data = Self::type_data();
338            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
339            (*parent_class)
340                .render_list
341                .map(|f| {
342                    try_from_glib(f(
343                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
344                        list.to_glib_none().0,
345                    ))
346                })
347                .unwrap_or_else(|| {
348                    for buffer in list.iter() {
349                        self.render(&from_glib_borrow(buffer.as_ptr()))?;
350                    }
351                    Ok(gst::FlowSuccess::Ok)
352                })
353        }
354    }
355
356    fn parent_prepare_list(
357        &self,
358        list: &gst::BufferList,
359    ) -> Result<gst::FlowSuccess, gst::FlowError> {
360        unsafe {
361            let data = Self::type_data();
362            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseSinkClass;
363            (*parent_class)
364                .prepare_list
365                .map(|f| {
366                    try_from_glib(f(
367                        self.obj().unsafe_cast_ref::<BaseSink>().to_glib_none().0,
368                        list.to_glib_none().0,
369                    ))
370                })
371                .unwrap_or_else(|| {
372                    for buffer in list.iter() {
373                        self.prepare(&from_glib_borrow(buffer.as_ptr()))?;
374                    }
375                    Ok(gst::FlowSuccess::Ok)
376                })
377        }
378    }
379}
380
381unsafe impl<T: BaseSinkImpl> IsSubclassable<T> for BaseSink {
382    fn class_init(klass: &mut glib::Class<Self>) {
383        Self::parent_class_init::<T>(klass);
384        let klass = klass.as_mut();
385        klass.start = Some(base_sink_start::<T>);
386        klass.stop = Some(base_sink_stop::<T>);
387        klass.render = Some(base_sink_render::<T>);
388        klass.render_list = Some(base_sink_render_list::<T>);
389        klass.prepare = Some(base_sink_prepare::<T>);
390        klass.prepare_list = Some(base_sink_prepare_list::<T>);
391        klass.query = Some(base_sink_query::<T>);
392        klass.event = Some(base_sink_event::<T>);
393        klass.get_caps = Some(base_sink_get_caps::<T>);
394        klass.set_caps = Some(base_sink_set_caps::<T>);
395        klass.fixate = Some(base_sink_fixate::<T>);
396        klass.unlock = Some(base_sink_unlock::<T>);
397        klass.unlock_stop = Some(base_sink_unlock_stop::<T>);
398        klass.propose_allocation = Some(base_sink_propose_allocation::<T>);
399    }
400}
401
402unsafe extern "C" fn base_sink_start<T: BaseSinkImpl>(
403    ptr: *mut ffi::GstBaseSink,
404) -> glib::ffi::gboolean {
405    let instance = &*(ptr as *mut T::Instance);
406    let imp = instance.imp();
407
408    gst::panic_to_error!(imp, false, {
409        match imp.start() {
410            Ok(()) => true,
411            Err(err) => {
412                imp.post_error_message(err);
413                false
414            }
415        }
416    })
417    .into_glib()
418}
419
420unsafe extern "C" fn base_sink_stop<T: BaseSinkImpl>(
421    ptr: *mut ffi::GstBaseSink,
422) -> glib::ffi::gboolean {
423    let instance = &*(ptr as *mut T::Instance);
424    let imp = instance.imp();
425
426    gst::panic_to_error!(imp, false, {
427        match imp.stop() {
428            Ok(()) => true,
429            Err(err) => {
430                imp.post_error_message(err);
431                false
432            }
433        }
434    })
435    .into_glib()
436}
437
438unsafe extern "C" fn base_sink_render<T: BaseSinkImpl>(
439    ptr: *mut ffi::GstBaseSink,
440    buffer: *mut gst::ffi::GstBuffer,
441) -> gst::ffi::GstFlowReturn {
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() }).into_glib()
447}
448
449unsafe extern "C" fn base_sink_prepare<T: BaseSinkImpl>(
450    ptr: *mut ffi::GstBaseSink,
451    buffer: *mut gst::ffi::GstBuffer,
452) -> gst::ffi::GstFlowReturn {
453    let instance = &*(ptr as *mut T::Instance);
454    let imp = instance.imp();
455    let buffer = from_glib_borrow(buffer);
456
457    gst::panic_to_error!(imp, gst::FlowReturn::Error, { imp.prepare(&buffer).into() }).into_glib()
458}
459
460unsafe extern "C" fn base_sink_render_list<T: BaseSinkImpl>(
461    ptr: *mut ffi::GstBaseSink,
462    list: *mut gst::ffi::GstBufferList,
463) -> gst::ffi::GstFlowReturn {
464    let instance = &*(ptr as *mut T::Instance);
465    let imp = instance.imp();
466    let list = from_glib_borrow(list);
467
468    gst::panic_to_error!(imp, gst::FlowReturn::Error, {
469        imp.render_list(&list).into()
470    })
471    .into_glib()
472}
473
474unsafe extern "C" fn base_sink_prepare_list<T: BaseSinkImpl>(
475    ptr: *mut ffi::GstBaseSink,
476    list: *mut gst::ffi::GstBufferList,
477) -> gst::ffi::GstFlowReturn {
478    let instance = &*(ptr as *mut T::Instance);
479    let imp = instance.imp();
480    let list = from_glib_borrow(list);
481
482    gst::panic_to_error!(imp, gst::FlowReturn::Error, {
483        imp.prepare_list(&list).into()
484    })
485    .into_glib()
486}
487
488unsafe extern "C" fn base_sink_query<T: BaseSinkImpl>(
489    ptr: *mut ffi::GstBaseSink,
490    query_ptr: *mut gst::ffi::GstQuery,
491) -> glib::ffi::gboolean {
492    let instance = &*(ptr as *mut T::Instance);
493    let imp = instance.imp();
494    let query = gst::QueryRef::from_mut_ptr(query_ptr);
495
496    gst::panic_to_error!(imp, false, { BaseSinkImpl::query(imp, query) }).into_glib()
497}
498
499unsafe extern "C" fn base_sink_event<T: BaseSinkImpl>(
500    ptr: *mut ffi::GstBaseSink,
501    event_ptr: *mut gst::ffi::GstEvent,
502) -> glib::ffi::gboolean {
503    let instance = &*(ptr as *mut T::Instance);
504    let imp = instance.imp();
505
506    gst::panic_to_error!(imp, false, { imp.event(from_glib_full(event_ptr)) }).into_glib()
507}
508
509unsafe extern "C" fn base_sink_get_caps<T: BaseSinkImpl>(
510    ptr: *mut ffi::GstBaseSink,
511    filter: *mut gst::ffi::GstCaps,
512) -> *mut gst::ffi::GstCaps {
513    let instance = &*(ptr as *mut T::Instance);
514    let imp = instance.imp();
515    let filter = Option::<gst::Caps>::from_glib_borrow(filter);
516
517    gst::panic_to_error!(imp, None, { imp.caps(filter.as_ref().as_ref()) })
518        .map(|caps| caps.into_glib_ptr())
519        .unwrap_or(ptr::null_mut())
520}
521
522unsafe extern "C" fn base_sink_set_caps<T: BaseSinkImpl>(
523    ptr: *mut ffi::GstBaseSink,
524    caps: *mut gst::ffi::GstCaps,
525) -> glib::ffi::gboolean {
526    let instance = &*(ptr as *mut T::Instance);
527    let imp = instance.imp();
528    let caps = from_glib_borrow(caps);
529
530    gst::panic_to_error!(imp, false, {
531        match imp.set_caps(&caps) {
532            Ok(()) => true,
533            Err(err) => {
534                err.log_with_imp(imp);
535                false
536            }
537        }
538    })
539    .into_glib()
540}
541
542unsafe extern "C" fn base_sink_fixate<T: BaseSinkImpl>(
543    ptr: *mut ffi::GstBaseSink,
544    caps: *mut gst::ffi::GstCaps,
545) -> *mut gst::ffi::GstCaps {
546    let instance = &*(ptr as *mut T::Instance);
547    let imp = instance.imp();
548    let caps = from_glib_full(caps);
549
550    gst::panic_to_error!(imp, gst::Caps::new_empty(), { imp.fixate(caps) }).into_glib_ptr()
551}
552
553unsafe extern "C" fn base_sink_unlock<T: BaseSinkImpl>(
554    ptr: *mut ffi::GstBaseSink,
555) -> glib::ffi::gboolean {
556    let instance = &*(ptr as *mut T::Instance);
557    let imp = instance.imp();
558
559    gst::panic_to_error!(imp, false, {
560        match imp.unlock() {
561            Ok(()) => true,
562            Err(err) => {
563                imp.post_error_message(err);
564                false
565            }
566        }
567    })
568    .into_glib()
569}
570
571unsafe extern "C" fn base_sink_unlock_stop<T: BaseSinkImpl>(
572    ptr: *mut ffi::GstBaseSink,
573) -> glib::ffi::gboolean {
574    let instance = &*(ptr as *mut T::Instance);
575    let imp = instance.imp();
576
577    gst::panic_to_error!(imp, false, {
578        match imp.unlock_stop() {
579            Ok(()) => true,
580            Err(err) => {
581                imp.post_error_message(err);
582                false
583            }
584        }
585    })
586    .into_glib()
587}
588
589unsafe extern "C" fn base_sink_propose_allocation<T: BaseSinkImpl>(
590    ptr: *mut ffi::GstBaseSink,
591    query: *mut gst::ffi::GstQuery,
592) -> glib::ffi::gboolean {
593    let instance = &*(ptr as *mut T::Instance);
594    let imp = instance.imp();
595    let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
596        gst::QueryViewMut::Allocation(allocation) => allocation,
597        _ => unreachable!(),
598    };
599
600    gst::panic_to_error!(imp, false, {
601        match imp.propose_allocation(query) {
602            Ok(()) => true,
603            Err(err) => {
604                err.log_with_imp(imp);
605                false
606            }
607        }
608    })
609    .into_glib()
610}