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