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