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