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