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