Skip to main content

gstreamer/subclass/
element.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{borrow::Cow, future::Future, sync::atomic};
4
5use glib::{subclass::prelude::*, translate::*};
6
7use super::prelude::*;
8use crate::{
9    Element, Event, PadTemplate, QueryRef, StateChange, StateChangeError, StateChangeReturn,
10    StateChangeSuccess, ffi, prelude::*,
11};
12
13#[derive(Debug, Clone)]
14pub struct ElementMetadata {
15    long_name: Cow<'static, str>,
16    classification: Cow<'static, str>,
17    description: Cow<'static, str>,
18    author: Cow<'static, str>,
19    additional: Cow<'static, [(Cow<'static, str>, Cow<'static, str>)]>,
20}
21
22impl ElementMetadata {
23    pub fn new(long_name: &str, classification: &str, description: &str, author: &str) -> Self {
24        Self {
25            long_name: Cow::Owned(long_name.into()),
26            classification: Cow::Owned(classification.into()),
27            description: Cow::Owned(description.into()),
28            author: Cow::Owned(author.into()),
29            additional: Cow::Borrowed(&[]),
30        }
31    }
32
33    pub fn with_additional(
34        long_name: &str,
35        classification: &str,
36        description: &str,
37        author: &str,
38        additional: &[(&str, &str)],
39    ) -> Self {
40        Self {
41            long_name: Cow::Owned(long_name.into()),
42            classification: Cow::Owned(classification.into()),
43            description: Cow::Owned(description.into()),
44            author: Cow::Owned(author.into()),
45            additional: additional
46                .iter()
47                .copied()
48                .map(|(key, value)| (Cow::Owned(key.into()), Cow::Owned(value.into())))
49                .collect(),
50        }
51    }
52
53    pub const fn with_cow(
54        long_name: Cow<'static, str>,
55        classification: Cow<'static, str>,
56        description: Cow<'static, str>,
57        author: Cow<'static, str>,
58        additional: Cow<'static, [(Cow<'static, str>, Cow<'static, str>)]>,
59    ) -> Self {
60        Self {
61            long_name,
62            classification,
63            description,
64            author,
65            additional,
66        }
67    }
68}
69
70pub trait ElementImpl: GstObjectImpl + ObjectSubclass<Type: IsA<Element>> {
71    fn metadata() -> Option<&'static ElementMetadata> {
72        None
73    }
74
75    fn pad_templates() -> &'static [PadTemplate] {
76        &[]
77    }
78
79    /// Perform `transition` on `element`.
80    ///
81    /// This function must be called with STATE_LOCK held and is mainly used
82    /// internally.
83    /// ## `transition`
84    /// the requested transition
85    ///
86    /// # Returns
87    ///
88    /// the [`StateChangeReturn`][crate::StateChangeReturn] of the state transition.
89    fn change_state(
90        &self,
91        transition: StateChange,
92    ) -> Result<StateChangeSuccess, StateChangeError> {
93        self.parent_change_state(transition)
94    }
95
96    /// Retrieves a request pad from the element according to the provided template.
97    /// Pad templates can be looked up using
98    /// [`ElementFactory::static_pad_templates()`][crate::ElementFactory::static_pad_templates()].
99    ///
100    /// The pad should be released with [`ElementExt::release_request_pad()`][crate::prelude::ElementExt::release_request_pad()].
101    /// ## `templ`
102    /// a [`PadTemplate`][crate::PadTemplate] of which we want a pad of.
103    /// ## `name`
104    /// the name of the request [`Pad`][crate::Pad]
105    /// to retrieve. Can be [`None`].
106    /// ## `caps`
107    /// the caps of the pad we want to
108    /// request. Can be [`None`].
109    ///
110    /// # Returns
111    ///
112    /// requested [`Pad`][crate::Pad] if found,
113    ///  otherwise [`None`]. Release after usage.
114    fn request_new_pad(
115        &self,
116        templ: &crate::PadTemplate,
117        name: Option<&str>,
118        caps: Option<&crate::Caps>,
119    ) -> Option<crate::Pad> {
120        self.parent_request_new_pad(templ, name, caps)
121    }
122
123    /// called when a request pad is to be released
124    fn release_pad(&self, pad: &crate::Pad) {
125        self.parent_release_pad(pad)
126    }
127
128    /// Sends an event to an element. If the element doesn't implement an
129    /// event handler, the event will be pushed on a random linked sink pad for
130    /// downstream events or a random linked source pad for upstream events.
131    ///
132    /// This function takes ownership of the provided event so you should
133    /// `gst_event_ref()` it if you want to reuse the event after this call.
134    ///
135    /// MT safe.
136    /// ## `event`
137    /// the [`Event`][crate::Event] to send to the element.
138    ///
139    /// # Returns
140    ///
141    /// [`true`] if the event was handled. Events that trigger a preroll (such
142    /// as flushing seeks and steps) will emit `GST_MESSAGE_ASYNC_DONE`.
143    fn send_event(&self, event: Event) -> bool {
144        self.parent_send_event(event)
145    }
146
147    /// Performs a query on the given element.
148    ///
149    /// For elements that don't implement a query handler, this function
150    /// forwards the query to a random srcpad or to the peer of a
151    /// random linked sinkpad of this element.
152    ///
153    /// Please note that some queries might need a running pipeline to work.
154    /// ## `query`
155    /// the [`Query`][crate::Query].
156    ///
157    /// # Returns
158    ///
159    /// [`true`] if the query could be performed.
160    ///
161    /// MT safe.
162    fn query(&self, query: &mut QueryRef) -> bool {
163        self.parent_query(query)
164    }
165
166    /// Sets the context of the element. Increases the refcount of the context.
167    ///
168    /// MT safe.
169    /// ## `context`
170    /// the [`Context`][crate::Context] to set.
171    fn set_context(&self, context: &crate::Context) {
172        self.parent_set_context(context)
173    }
174
175    /// Sets the clock for the element. This function increases the
176    /// refcount on the clock. Any previously set clock on the object
177    /// is unreffed.
178    /// ## `clock`
179    /// the [`Clock`][crate::Clock] to set for the element.
180    ///
181    /// # Returns
182    ///
183    /// [`true`] if the element accepted the clock. An element can refuse a
184    /// clock when it, for example, is not able to slave its internal clock to the
185    /// `clock` or when it requires a specific clock to operate.
186    ///
187    /// MT safe.
188    fn set_clock(&self, clock: Option<&crate::Clock>) -> bool {
189        self.parent_set_clock(clock)
190    }
191
192    ///  state. Some elements can provide a clock in other states.
193    ///
194    /// # Returns
195    ///
196    /// the GstClock provided by the
197    /// element or [`None`] if no clock could be provided. Unref after usage.
198    ///
199    /// MT safe.
200    fn provide_clock(&self) -> Option<crate::Clock> {
201        self.parent_provide_clock()
202    }
203
204    /// Post a message on the element's [`Bus`][crate::Bus]. This function takes ownership of the
205    /// message; if you want to access the message after this call, you should add an
206    /// additional reference before calling.
207    /// ## `message`
208    /// a [`Message`][crate::Message] to post
209    ///
210    /// # Returns
211    ///
212    /// [`true`] if the message was successfully posted. The function returns
213    /// [`false`] if the element did not have a bus.
214    ///
215    /// MT safe.
216    fn post_message(&self, msg: crate::Message) -> bool {
217        self.parent_post_message(msg)
218    }
219}
220
221pub trait ElementImplExt: ElementImpl {
222    fn parent_change_state(
223        &self,
224        transition: StateChange,
225    ) -> Result<StateChangeSuccess, StateChangeError> {
226        unsafe {
227            let data = Self::type_data();
228            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
229
230            let f = (*parent_class)
231                .change_state
232                .expect("Missing parent function `change_state`");
233            try_from_glib(f(
234                self.obj().unsafe_cast_ref::<Element>().to_glib_none().0,
235                transition.into_glib(),
236            ))
237        }
238    }
239
240    fn parent_request_new_pad(
241        &self,
242        templ: &crate::PadTemplate,
243        name: Option<&str>,
244        caps: Option<&crate::Caps>,
245    ) -> Option<crate::Pad> {
246        unsafe {
247            let data = Self::type_data();
248            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
249
250            (*parent_class)
251                .request_new_pad
252                .map(|f| {
253                    from_glib_none(f(
254                        self.obj().unsafe_cast_ref::<Element>().to_glib_none().0,
255                        templ.to_glib_none().0,
256                        name.to_glib_none().0,
257                        caps.to_glib_none().0,
258                    ))
259                })
260                .unwrap_or(None)
261        }
262    }
263
264    fn parent_release_pad(&self, pad: &crate::Pad) {
265        unsafe {
266            let data = Self::type_data();
267            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
268
269            (*parent_class)
270                .release_pad
271                .map(|f| {
272                    f(
273                        self.obj().unsafe_cast_ref::<Element>().to_glib_none().0,
274                        pad.to_glib_none().0,
275                    )
276                })
277                .unwrap_or(())
278        }
279    }
280
281    fn parent_send_event(&self, event: Event) -> bool {
282        unsafe {
283            let data = Self::type_data();
284            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
285
286            (*parent_class)
287                .send_event
288                .map(|f| {
289                    from_glib(f(
290                        self.obj().unsafe_cast_ref::<Element>().to_glib_none().0,
291                        event.into_glib_ptr(),
292                    ))
293                })
294                .unwrap_or(false)
295        }
296    }
297
298    fn parent_query(&self, query: &mut QueryRef) -> bool {
299        unsafe {
300            let data = Self::type_data();
301            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
302
303            (*parent_class)
304                .query
305                .map(|f| {
306                    from_glib(f(
307                        self.obj().unsafe_cast_ref::<Element>().to_glib_none().0,
308                        query.as_mut_ptr(),
309                    ))
310                })
311                .unwrap_or(false)
312        }
313    }
314
315    fn parent_set_context(&self, context: &crate::Context) {
316        unsafe {
317            let data = Self::type_data();
318            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
319
320            (*parent_class)
321                .set_context
322                .map(|f| {
323                    f(
324                        self.obj().unsafe_cast_ref::<Element>().to_glib_none().0,
325                        context.to_glib_none().0,
326                    )
327                })
328                .unwrap_or(())
329        }
330    }
331
332    fn parent_set_clock(&self, clock: Option<&crate::Clock>) -> bool {
333        unsafe {
334            let data = Self::type_data();
335            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
336
337            (*parent_class)
338                .set_clock
339                .map(|f| {
340                    from_glib(f(
341                        self.obj().unsafe_cast_ref::<Element>().to_glib_none().0,
342                        clock.to_glib_none().0,
343                    ))
344                })
345                .unwrap_or(false)
346        }
347    }
348
349    fn parent_provide_clock(&self) -> Option<crate::Clock> {
350        unsafe {
351            let data = Self::type_data();
352            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
353
354            (*parent_class)
355                .provide_clock
356                .map(|f| {
357                    from_glib_none(f(self.obj().unsafe_cast_ref::<Element>().to_glib_none().0))
358                })
359                .unwrap_or(None)
360        }
361    }
362
363    fn parent_post_message(&self, msg: crate::Message) -> bool {
364        unsafe {
365            let data = Self::type_data();
366            let parent_class = data.as_ref().parent_class() as *mut ffi::GstElementClass;
367
368            if let Some(f) = (*parent_class).post_message {
369                from_glib(f(
370                    self.obj().unsafe_cast_ref::<Element>().to_glib_none().0,
371                    msg.into_glib_ptr(),
372                ))
373            } else {
374                false
375            }
376        }
377    }
378
379    #[inline(never)]
380    fn panicked(&self) -> &atomic::AtomicBool {
381        #[cfg(panic = "abort")]
382        {
383            static DUMMY: atomic::AtomicBool = atomic::AtomicBool::new(false);
384            &DUMMY
385        }
386        #[cfg(not(panic = "abort"))]
387        {
388            self.instance_data::<atomic::AtomicBool>(crate::Element::static_type())
389                .expect("instance not initialized correctly")
390        }
391    }
392
393    fn catch_panic<R, F: FnOnce(&Self) -> R, G: FnOnce() -> R>(&self, fallback: G, f: F) -> R {
394        element_panic_to_error!(self, fallback(), { f(self) })
395    }
396
397    fn catch_panic_future<R, F: FnOnce() -> R, G: Future<Output = R>>(
398        &self,
399        fallback: F,
400        fut: G,
401    ) -> CatchPanic<Self, F, G> {
402        CatchPanic {
403            self_: self.ref_counted().downgrade(),
404            fallback: Some(fallback),
405            fut,
406        }
407    }
408
409    fn catch_panic_pad_function<R, F: FnOnce(&Self) -> R, G: FnOnce() -> R>(
410        parent: Option<&crate::Object>,
411        fallback: G,
412        f: F,
413    ) -> R {
414        let element = parent.unwrap().dynamic_cast_ref::<Self::Type>().unwrap();
415        let imp = element.imp();
416
417        element_panic_to_error!(imp, fallback(), { f(imp) })
418    }
419
420    fn post_error_message(&self, msg: crate::ErrorMessage) {
421        unsafe {
422            self.obj()
423                .unsafe_cast_ref::<Element>()
424                .post_error_message(msg)
425        }
426    }
427}
428
429impl<T: ElementImpl> ElementImplExt for T {}
430
431pin_project_lite::pin_project! {
432    #[must_use = "futures do nothing unless you `.await` or poll them"]
433    pub struct CatchPanic<T: glib::subclass::types::ObjectSubclass, F, G> {
434        self_: glib::subclass::ObjectImplWeakRef<T>,
435        fallback: Option<F>,
436        #[pin]
437        fut: G,
438    }
439}
440
441impl<R, T: ElementImpl, F: FnOnce() -> R, G: Future<Output = R>> Future for CatchPanic<T, F, G> {
442    type Output = R;
443
444    fn poll(
445        self: std::pin::Pin<&mut Self>,
446        cx: &mut std::task::Context<'_>,
447    ) -> std::task::Poll<Self::Output> {
448        let this = self.project();
449
450        let Some(self_) = this.self_.upgrade() else {
451            return std::task::Poll::Ready((this
452                .fallback
453                .take()
454                .expect("Future polled after resolving"))(
455            ));
456        };
457
458        element_panic_to_error!(
459            &*self_,
460            std::task::Poll::Ready(this.fallback.take().expect("Future polled after resolving")()),
461            {
462                let fut = this.fut;
463                fut.poll(cx)
464            }
465        )
466    }
467}
468
469unsafe impl<T: ElementImpl> IsSubclassable<T> for Element {
470    fn class_init(klass: &mut glib::Class<Self>) {
471        Self::parent_class_init::<T>(klass);
472        let klass = klass.as_mut();
473        klass.change_state = Some(element_change_state::<T>);
474        klass.request_new_pad = Some(element_request_new_pad::<T>);
475        klass.release_pad = Some(element_release_pad::<T>);
476        klass.send_event = Some(element_send_event::<T>);
477        klass.query = Some(element_query::<T>);
478        klass.set_context = Some(element_set_context::<T>);
479        klass.set_clock = Some(element_set_clock::<T>);
480        klass.provide_clock = Some(element_provide_clock::<T>);
481        klass.post_message = Some(element_post_message::<T>);
482
483        unsafe {
484            for pad_template in T::pad_templates() {
485                ffi::gst_element_class_add_pad_template(klass, pad_template.to_glib_none().0);
486            }
487
488            if let Some(metadata) = T::metadata() {
489                ffi::gst_element_class_set_metadata(
490                    klass,
491                    metadata.long_name.to_glib_none().0,
492                    metadata.classification.to_glib_none().0,
493                    metadata.description.to_glib_none().0,
494                    metadata.author.to_glib_none().0,
495                );
496
497                for (key, value) in &metadata.additional[..] {
498                    ffi::gst_element_class_add_metadata(
499                        klass,
500                        key.to_glib_none().0,
501                        value.to_glib_none().0,
502                    );
503                }
504            }
505        }
506    }
507
508    fn instance_init(instance: &mut glib::subclass::InitializingObject<T>) {
509        Self::parent_instance_init::<T>(instance);
510
511        #[cfg(not(panic = "abort"))]
512        instance.set_instance_data(Self::static_type(), atomic::AtomicBool::new(false));
513    }
514}
515
516unsafe extern "C" fn element_change_state<T: ElementImpl>(
517    ptr: *mut ffi::GstElement,
518    transition: ffi::GstStateChange,
519) -> ffi::GstStateChangeReturn {
520    unsafe {
521        let instance = &*(ptr as *mut T::Instance);
522        let imp = instance.imp();
523
524        // *Never* fail downwards state changes, this causes bugs in GStreamer
525        // and leads to crashes and deadlocks.
526        let transition = from_glib(transition);
527        let fallback = match transition {
528            StateChange::PlayingToPaused
529            | StateChange::PausedToReady
530            | StateChange::ReadyToNull => StateChangeReturn::Success,
531            _ => StateChangeReturn::Failure,
532        };
533
534        element_panic_to_error!(imp, fallback, {
535            StateChangeReturn::from(imp.change_state(transition))
536        })
537        .into_glib()
538    }
539}
540
541unsafe extern "C" fn element_request_new_pad<T: ElementImpl>(
542    ptr: *mut ffi::GstElement,
543    templ: *mut ffi::GstPadTemplate,
544    name: *const libc::c_char,
545    caps: *const ffi::GstCaps,
546) -> *mut ffi::GstPad {
547    unsafe {
548        let instance = &*(ptr as *mut T::Instance);
549        let imp = instance.imp();
550
551        let caps = Option::<crate::Caps>::from_glib_borrow(caps);
552        let name = Option::<String>::from_glib_none(name);
553
554        // XXX: This is effectively unsafe but the best we can do
555        // See https://bugzilla.gnome.org/show_bug.cgi?id=791193
556        let pad = element_panic_to_error!(imp, None, {
557            imp.request_new_pad(
558                &from_glib_borrow(templ),
559                name.as_deref(),
560                caps.as_ref().as_ref(),
561            )
562        });
563
564        // Ensure that the pad is owned by the element now, if a pad was returned
565        if let Some(ref pad) = pad {
566            assert_eq!(
567                pad.parent().as_ref(),
568                Some(&*crate::Object::from_glib_borrow(
569                    ptr as *mut ffi::GstObject
570                ))
571            );
572        }
573
574        pad.to_glib_none().0
575    }
576}
577
578unsafe extern "C" fn element_release_pad<T: ElementImpl>(
579    ptr: *mut ffi::GstElement,
580    pad: *mut ffi::GstPad,
581) {
582    unsafe {
583        let instance = &*(ptr as *mut T::Instance);
584        let imp = instance.imp();
585
586        // If we get a floating reference passed simply return here. It can't be stored inside this
587        // element, and if we continued to use it we would take ownership of this floating reference.
588        if glib::gobject_ffi::g_object_is_floating(pad as *mut glib::gobject_ffi::GObject)
589            != glib::ffi::GFALSE
590        {
591            return;
592        }
593
594        element_panic_to_error!(imp, (), { imp.release_pad(&from_glib_none(pad)) })
595    }
596}
597
598unsafe extern "C" fn element_send_event<T: ElementImpl>(
599    ptr: *mut ffi::GstElement,
600    event: *mut ffi::GstEvent,
601) -> glib::ffi::gboolean {
602    unsafe {
603        let instance = &*(ptr as *mut T::Instance);
604        let imp = instance.imp();
605
606        element_panic_to_error!(imp, false, { imp.send_event(from_glib_full(event)) }).into_glib()
607    }
608}
609
610unsafe extern "C" fn element_query<T: ElementImpl>(
611    ptr: *mut ffi::GstElement,
612    query: *mut ffi::GstQuery,
613) -> glib::ffi::gboolean {
614    unsafe {
615        let instance = &*(ptr as *mut T::Instance);
616        let imp = instance.imp();
617        let query = QueryRef::from_mut_ptr(query);
618
619        element_panic_to_error!(imp, false, { imp.query(query) }).into_glib()
620    }
621}
622
623unsafe extern "C" fn element_set_context<T: ElementImpl>(
624    ptr: *mut ffi::GstElement,
625    context: *mut ffi::GstContext,
626) {
627    unsafe {
628        let instance = &*(ptr as *mut T::Instance);
629        let imp = instance.imp();
630
631        element_panic_to_error!(imp, (), { imp.set_context(&from_glib_borrow(context)) })
632    }
633}
634
635unsafe extern "C" fn element_set_clock<T: ElementImpl>(
636    ptr: *mut ffi::GstElement,
637    clock: *mut ffi::GstClock,
638) -> glib::ffi::gboolean {
639    unsafe {
640        let instance = &*(ptr as *mut T::Instance);
641        let imp = instance.imp();
642
643        let clock = Option::<crate::Clock>::from_glib_borrow(clock);
644
645        element_panic_to_error!(imp, false, { imp.set_clock(clock.as_ref().as_ref()) }).into_glib()
646    }
647}
648
649unsafe extern "C" fn element_provide_clock<T: ElementImpl>(
650    ptr: *mut ffi::GstElement,
651) -> *mut ffi::GstClock {
652    unsafe {
653        let instance = &*(ptr as *mut T::Instance);
654        let imp = instance.imp();
655
656        element_panic_to_error!(imp, None, { imp.provide_clock() }).into_glib_ptr()
657    }
658}
659
660unsafe extern "C" fn element_post_message<T: ElementImpl>(
661    ptr: *mut ffi::GstElement,
662    msg: *mut ffi::GstMessage,
663) -> glib::ffi::gboolean {
664    unsafe {
665        let instance = &*(ptr as *mut T::Instance);
666        let imp = instance.imp();
667
668        // Can't catch panics here as posting the error message would cause
669        // this code to be called again recursively forever.
670        imp.post_message(from_glib_full(msg)).into_glib()
671    }
672}
673
674#[cfg(test)]
675mod tests {
676    use std::sync::{Arc, Mutex, OnceLock, atomic};
677
678    use super::*;
679    use crate::ElementFactory;
680
681    pub mod imp {
682        use super::*;
683
684        pub struct TestElement {
685            pub(super) srcpad: crate::Pad,
686            pub(super) sinkpad: crate::Pad,
687            pub(super) n_buffers: atomic::AtomicU32,
688            pub(super) reached_playing: atomic::AtomicBool,
689            pub(super) array: Arc<Mutex<Vec<String>>>,
690        }
691
692        impl TestElement {
693            fn sink_chain(
694                &self,
695                _pad: &crate::Pad,
696                buffer: crate::Buffer,
697            ) -> Result<crate::FlowSuccess, crate::FlowError> {
698                self.n_buffers.fetch_add(1, atomic::Ordering::SeqCst);
699                self.srcpad.push(buffer)
700            }
701
702            fn sink_event(&self, _pad: &crate::Pad, event: crate::Event) -> bool {
703                self.srcpad.push_event(event)
704            }
705
706            fn sink_query(&self, _pad: &crate::Pad, query: &mut crate::QueryRef) -> bool {
707                self.srcpad.peer_query(query)
708            }
709
710            fn src_event(&self, _pad: &crate::Pad, event: crate::Event) -> bool {
711                self.sinkpad.push_event(event)
712            }
713
714            fn src_query(&self, _pad: &crate::Pad, query: &mut crate::QueryRef) -> bool {
715                self.sinkpad.peer_query(query)
716            }
717        }
718
719        #[glib::object_subclass]
720        impl ObjectSubclass for TestElement {
721            const NAME: &'static str = "TestElement";
722            type Type = super::TestElement;
723            type ParentType = Element;
724
725            fn with_class(klass: &Self::Class) -> Self {
726                let templ = klass.pad_template("sink").unwrap();
727                let sinkpad = crate::Pad::builder_from_template(&templ)
728                    .chain_function(|pad, parent, buffer| {
729                        TestElement::catch_panic_pad_function(
730                            parent,
731                            || Err(crate::FlowError::Error),
732                            |identity| identity.sink_chain(pad, buffer),
733                        )
734                    })
735                    .event_function(|pad, parent, event| {
736                        TestElement::catch_panic_pad_function(
737                            parent,
738                            || false,
739                            |identity| identity.sink_event(pad, event),
740                        )
741                    })
742                    .query_function(|pad, parent, query| {
743                        TestElement::catch_panic_pad_function(
744                            parent,
745                            || false,
746                            |identity| identity.sink_query(pad, query),
747                        )
748                    })
749                    .build();
750
751                let templ = klass.pad_template("src").unwrap();
752                let srcpad = crate::Pad::builder_from_template(&templ)
753                    .event_function(|pad, parent, event| {
754                        TestElement::catch_panic_pad_function(
755                            parent,
756                            || false,
757                            |identity| identity.src_event(pad, event),
758                        )
759                    })
760                    .query_function(|pad, parent, query| {
761                        TestElement::catch_panic_pad_function(
762                            parent,
763                            || false,
764                            |identity| identity.src_query(pad, query),
765                        )
766                    })
767                    .build();
768
769                Self {
770                    n_buffers: atomic::AtomicU32::new(0),
771                    reached_playing: atomic::AtomicBool::new(false),
772                    array: Arc::new(Mutex::new(vec![
773                        "default0".to_string(),
774                        "default1".to_string(),
775                    ])),
776                    srcpad,
777                    sinkpad,
778                }
779            }
780        }
781
782        impl ObjectImpl for TestElement {
783            fn constructed(&self) {
784                self.parent_constructed();
785
786                let element = self.obj();
787                element.add_pad(&self.sinkpad).unwrap();
788                element.add_pad(&self.srcpad).unwrap();
789            }
790
791            fn properties() -> &'static [glib::ParamSpec] {
792                static PROPERTIES: OnceLock<Vec<glib::ParamSpec>> = OnceLock::new();
793                PROPERTIES.get_or_init(|| vec![crate::ParamSpecArray::builder("array").build()])
794            }
795
796            fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
797                match pspec.name() {
798                    "array" => {
799                        let value = value.get::<crate::Array>().unwrap();
800                        let mut array = self.array.lock().unwrap();
801                        array.clear();
802                        array.extend(value.iter().map(|v| v.get().unwrap()));
803                    }
804                    _ => unimplemented!(),
805                }
806            }
807
808            fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
809                match pspec.name() {
810                    "array" => crate::Array::new(&*self.array.lock().unwrap()).to_value(),
811                    _ => unimplemented!(),
812                }
813            }
814        }
815
816        impl GstObjectImpl for TestElement {}
817
818        impl ElementImpl for TestElement {
819            fn metadata() -> Option<&'static ElementMetadata> {
820                static ELEMENT_METADATA: std::sync::OnceLock<ElementMetadata> =
821                    std::sync::OnceLock::new();
822
823                Some(ELEMENT_METADATA.get_or_init(|| {
824                    ElementMetadata::new(
825                        "Test Element",
826                        "Generic",
827                        "Does nothing",
828                        "Sebastian Dröge <sebastian@centricular.com>",
829                    )
830                }))
831            }
832
833            fn pad_templates() -> &'static [PadTemplate] {
834                static PAD_TEMPLATES: std::sync::OnceLock<Vec<PadTemplate>> =
835                    std::sync::OnceLock::new();
836
837                PAD_TEMPLATES.get_or_init(|| {
838                    let caps = crate::Caps::new_any();
839                    vec![
840                        PadTemplate::new(
841                            "src",
842                            crate::PadDirection::Src,
843                            crate::PadPresence::Always,
844                            &caps,
845                        )
846                        .unwrap(),
847                        PadTemplate::new(
848                            "sink",
849                            crate::PadDirection::Sink,
850                            crate::PadPresence::Always,
851                            &caps,
852                        )
853                        .unwrap(),
854                    ]
855                })
856            }
857
858            fn change_state(
859                &self,
860                transition: crate::StateChange,
861            ) -> Result<crate::StateChangeSuccess, crate::StateChangeError> {
862                let res = self.parent_change_state(transition)?;
863
864                if transition == crate::StateChange::PausedToPlaying {
865                    self.reached_playing.store(true, atomic::Ordering::SeqCst);
866                }
867
868                Ok(res)
869            }
870        }
871    }
872
873    glib::wrapper! {
874        pub struct TestElement(ObjectSubclass<imp::TestElement>) @extends Element, crate::Object;
875    }
876
877    impl TestElement {
878        pub fn new(name: Option<&str>) -> Self {
879            glib::Object::builder().property("name", name).build()
880        }
881    }
882
883    fn plugin_init(plugin: &crate::Plugin) -> Result<(), glib::BoolError> {
884        crate::Element::register(
885            Some(plugin),
886            "testelement",
887            crate::Rank::MARGINAL,
888            TestElement::static_type(),
889        )
890    }
891
892    crate::plugin_define!(
893        rssubclasstestelem,
894        env!("CARGO_PKG_DESCRIPTION"),
895        plugin_init,
896        env!("CARGO_PKG_VERSION"),
897        "MPL-2.0",
898        env!("CARGO_PKG_NAME"),
899        env!("CARGO_PKG_NAME"),
900        env!("CARGO_PKG_REPOSITORY"),
901        "1970-01-01"
902    );
903
904    fn init() {
905        use std::sync::Once;
906        static INIT: Once = Once::new();
907
908        INIT.call_once(|| {
909            crate::init().unwrap();
910            plugin_register_static().expect("gstreamer subclass element test");
911        });
912    }
913
914    #[test]
915    fn test_element_subclass() {
916        init();
917
918        let element = TestElement::new(Some("test"));
919
920        assert_eq!(element.name(), "test");
921
922        assert_eq!(
923            element.metadata(crate::ELEMENT_METADATA_LONGNAME),
924            Some("Test Element")
925        );
926
927        let pipeline = crate::Pipeline::new();
928        let src = ElementFactory::make("fakesrc")
929            .property("num-buffers", 100i32)
930            .build()
931            .unwrap();
932        let sink = ElementFactory::make("fakesink").build().unwrap();
933
934        pipeline
935            .add_many([&src, element.upcast_ref(), &sink])
936            .unwrap();
937        Element::link_many([&src, element.upcast_ref(), &sink]).unwrap();
938
939        pipeline.set_state(crate::State::Playing).unwrap();
940        let bus = pipeline.bus().unwrap();
941
942        let eos = bus.timed_pop_filtered(crate::ClockTime::NONE, &[crate::MessageType::Eos]);
943        assert!(eos.is_some());
944
945        pipeline.set_state(crate::State::Null).unwrap();
946
947        let imp = element.imp();
948        assert_eq!(imp.n_buffers.load(atomic::Ordering::SeqCst), 100);
949        assert!(imp.reached_playing.load(atomic::Ordering::SeqCst));
950    }
951
952    #[test]
953    fn property_from_iter_if_not_empty() {
954        init();
955
956        let elem = crate::ElementFactory::make("testelement").build().unwrap();
957        assert!(
958            elem.property::<crate::Array>("array")
959                .iter()
960                .map(|val| val.get::<&str>().unwrap())
961                .eq(["default0", "default1"])
962        );
963
964        let elem = crate::ElementFactory::make("testelement")
965            .property_from_iter::<crate::Array, _>("array", ["value0", "value1"])
966            .build()
967            .unwrap();
968        assert!(
969            elem.property::<crate::Array>("array")
970                .iter()
971                .map(|val| val.get::<&str>().unwrap())
972                .eq(["value0", "value1"])
973        );
974
975        let array = Vec::<String>::new();
976        let elem = crate::ElementFactory::make("testelement")
977            .property_if_not_empty::<crate::Array, _>("array", &array)
978            .build()
979            .unwrap();
980        assert!(
981            elem.property::<crate::Array>("array")
982                .iter()
983                .map(|val| val.get::<&str>().unwrap())
984                .eq(["default0", "default1"])
985        );
986
987        let elem = crate::ElementFactory::make("testelement")
988            .property_if_not_empty::<crate::Array, _>("array", ["value0", "value1"])
989            .build()
990            .unwrap();
991        assert!(
992            elem.property::<crate::Array>("array")
993                .iter()
994                .map(|val| val.get::<&str>().unwrap())
995                .eq(["value0", "value1"])
996        );
997    }
998}