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