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: 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    let instance = &*(ptr as *mut T::Instance);
401    let imp = instance.imp();
402
403    gst::panic_to_error!(imp, false, {
404        match imp.start() {
405            Ok(()) => true,
406            Err(err) => {
407                imp.post_error_message(err);
408                false
409            }
410        }
411    })
412    .into_glib()
413}
414
415unsafe extern "C" fn base_sink_stop<T: BaseSinkImpl>(
416    ptr: *mut ffi::GstBaseSink,
417) -> glib::ffi::gboolean {
418    let instance = &*(ptr as *mut T::Instance);
419    let imp = instance.imp();
420
421    gst::panic_to_error!(imp, false, {
422        match imp.stop() {
423            Ok(()) => true,
424            Err(err) => {
425                imp.post_error_message(err);
426                false
427            }
428        }
429    })
430    .into_glib()
431}
432
433unsafe extern "C" fn base_sink_render<T: BaseSinkImpl>(
434    ptr: *mut ffi::GstBaseSink,
435    buffer: *mut gst::ffi::GstBuffer,
436) -> gst::ffi::GstFlowReturn {
437    let instance = &*(ptr as *mut T::Instance);
438    let imp = instance.imp();
439    let buffer = from_glib_borrow(buffer);
440
441    gst::panic_to_error!(imp, gst::FlowReturn::Error, { imp.render(&buffer).into() }).into_glib()
442}
443
444unsafe extern "C" fn base_sink_prepare<T: BaseSinkImpl>(
445    ptr: *mut ffi::GstBaseSink,
446    buffer: *mut gst::ffi::GstBuffer,
447) -> gst::ffi::GstFlowReturn {
448    let instance = &*(ptr as *mut T::Instance);
449    let imp = instance.imp();
450    let buffer = from_glib_borrow(buffer);
451
452    gst::panic_to_error!(imp, gst::FlowReturn::Error, { imp.prepare(&buffer).into() }).into_glib()
453}
454
455unsafe extern "C" fn base_sink_render_list<T: BaseSinkImpl>(
456    ptr: *mut ffi::GstBaseSink,
457    list: *mut gst::ffi::GstBufferList,
458) -> gst::ffi::GstFlowReturn {
459    let instance = &*(ptr as *mut T::Instance);
460    let imp = instance.imp();
461    let list = from_glib_borrow(list);
462
463    gst::panic_to_error!(imp, gst::FlowReturn::Error, {
464        imp.render_list(&list).into()
465    })
466    .into_glib()
467}
468
469unsafe extern "C" fn base_sink_prepare_list<T: BaseSinkImpl>(
470    ptr: *mut ffi::GstBaseSink,
471    list: *mut gst::ffi::GstBufferList,
472) -> gst::ffi::GstFlowReturn {
473    let instance = &*(ptr as *mut T::Instance);
474    let imp = instance.imp();
475    let list = from_glib_borrow(list);
476
477    gst::panic_to_error!(imp, gst::FlowReturn::Error, {
478        imp.prepare_list(&list).into()
479    })
480    .into_glib()
481}
482
483unsafe extern "C" fn base_sink_query<T: BaseSinkImpl>(
484    ptr: *mut ffi::GstBaseSink,
485    query_ptr: *mut gst::ffi::GstQuery,
486) -> glib::ffi::gboolean {
487    let instance = &*(ptr as *mut T::Instance);
488    let imp = instance.imp();
489    let query = gst::QueryRef::from_mut_ptr(query_ptr);
490
491    gst::panic_to_error!(imp, false, { BaseSinkImpl::query(imp, query) }).into_glib()
492}
493
494unsafe extern "C" fn base_sink_event<T: BaseSinkImpl>(
495    ptr: *mut ffi::GstBaseSink,
496    event_ptr: *mut gst::ffi::GstEvent,
497) -> glib::ffi::gboolean {
498    let instance = &*(ptr as *mut T::Instance);
499    let imp = instance.imp();
500
501    gst::panic_to_error!(imp, false, { imp.event(from_glib_full(event_ptr)) }).into_glib()
502}
503
504unsafe extern "C" fn base_sink_get_caps<T: BaseSinkImpl>(
505    ptr: *mut ffi::GstBaseSink,
506    filter: *mut gst::ffi::GstCaps,
507) -> *mut gst::ffi::GstCaps {
508    let instance = &*(ptr as *mut T::Instance);
509    let imp = instance.imp();
510    let filter = Option::<gst::Caps>::from_glib_borrow(filter);
511
512    gst::panic_to_error!(imp, None, { imp.caps(filter.as_ref().as_ref()) })
513        .map(|caps| caps.into_glib_ptr())
514        .unwrap_or(ptr::null_mut())
515}
516
517unsafe extern "C" fn base_sink_set_caps<T: BaseSinkImpl>(
518    ptr: *mut ffi::GstBaseSink,
519    caps: *mut gst::ffi::GstCaps,
520) -> glib::ffi::gboolean {
521    let instance = &*(ptr as *mut T::Instance);
522    let imp = instance.imp();
523    let caps = from_glib_borrow(caps);
524
525    gst::panic_to_error!(imp, false, {
526        match imp.set_caps(&caps) {
527            Ok(()) => true,
528            Err(err) => {
529                err.log_with_imp(imp);
530                false
531            }
532        }
533    })
534    .into_glib()
535}
536
537unsafe extern "C" fn base_sink_fixate<T: BaseSinkImpl>(
538    ptr: *mut ffi::GstBaseSink,
539    caps: *mut gst::ffi::GstCaps,
540) -> *mut gst::ffi::GstCaps {
541    let instance = &*(ptr as *mut T::Instance);
542    let imp = instance.imp();
543    let caps = from_glib_full(caps);
544
545    gst::panic_to_error!(imp, gst::Caps::new_empty(), { imp.fixate(caps) }).into_glib_ptr()
546}
547
548unsafe extern "C" fn base_sink_unlock<T: BaseSinkImpl>(
549    ptr: *mut ffi::GstBaseSink,
550) -> glib::ffi::gboolean {
551    let instance = &*(ptr as *mut T::Instance);
552    let imp = instance.imp();
553
554    gst::panic_to_error!(imp, false, {
555        match imp.unlock() {
556            Ok(()) => true,
557            Err(err) => {
558                imp.post_error_message(err);
559                false
560            }
561        }
562    })
563    .into_glib()
564}
565
566unsafe extern "C" fn base_sink_unlock_stop<T: BaseSinkImpl>(
567    ptr: *mut ffi::GstBaseSink,
568) -> glib::ffi::gboolean {
569    let instance = &*(ptr as *mut T::Instance);
570    let imp = instance.imp();
571
572    gst::panic_to_error!(imp, false, {
573        match imp.unlock_stop() {
574            Ok(()) => true,
575            Err(err) => {
576                imp.post_error_message(err);
577                false
578            }
579        }
580    })
581    .into_glib()
582}
583
584unsafe extern "C" fn base_sink_propose_allocation<T: BaseSinkImpl>(
585    ptr: *mut ffi::GstBaseSink,
586    query: *mut gst::ffi::GstQuery,
587) -> glib::ffi::gboolean {
588    let instance = &*(ptr as *mut T::Instance);
589    let imp = instance.imp();
590    let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
591        gst::QueryViewMut::Allocation(allocation) => allocation,
592        _ => unreachable!(),
593    };
594
595    gst::panic_to_error!(imp, false, {
596        match imp.propose_allocation(query) {
597            Ok(()) => true,
598            Err(err) => {
599                err.log_with_imp_and_level(imp, gst::DebugLevel::Info);
600                false
601            }
602        }
603    })
604    .into_glib()
605}