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