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