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