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(
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 unsafe {
424 let instance = &*(ptr as *mut T::Instance);
425 let imp = instance.imp();
426
427 let transition = from_glib(transition);
430 let fallback = match transition {
431 StateChange::PlayingToPaused
432 | StateChange::PausedToReady
433 | StateChange::ReadyToNull => StateChangeReturn::Success,
434 _ => StateChangeReturn::Failure,
435 };
436
437 panic_to_error!(imp, fallback, {
438 StateChangeReturn::from(imp.change_state(transition))
439 })
440 .into_glib()
441 }
442}
443
444unsafe extern "C" fn element_request_new_pad<T: ElementImpl>(
445 ptr: *mut ffi::GstElement,
446 templ: *mut ffi::GstPadTemplate,
447 name: *const libc::c_char,
448 caps: *const ffi::GstCaps,
449) -> *mut ffi::GstPad {
450 unsafe {
451 let instance = &*(ptr as *mut T::Instance);
452 let imp = instance.imp();
453
454 let caps = Option::<crate::Caps>::from_glib_borrow(caps);
455 let name = Option::<String>::from_glib_none(name);
456
457 let pad = panic_to_error!(imp, None, {
460 imp.request_new_pad(
461 &from_glib_borrow(templ),
462 name.as_deref(),
463 caps.as_ref().as_ref(),
464 )
465 });
466
467 if let Some(ref pad) = pad {
469 assert_eq!(
470 pad.parent().as_ref(),
471 Some(&*crate::Object::from_glib_borrow(
472 ptr as *mut ffi::GstObject
473 ))
474 );
475 }
476
477 pad.to_glib_none().0
478 }
479}
480
481unsafe extern "C" fn element_release_pad<T: ElementImpl>(
482 ptr: *mut ffi::GstElement,
483 pad: *mut ffi::GstPad,
484) {
485 unsafe {
486 let instance = &*(ptr as *mut T::Instance);
487 let imp = instance.imp();
488
489 if glib::gobject_ffi::g_object_is_floating(pad as *mut glib::gobject_ffi::GObject)
492 != glib::ffi::GFALSE
493 {
494 return;
495 }
496
497 panic_to_error!(imp, (), { imp.release_pad(&from_glib_none(pad)) })
498 }
499}
500
501unsafe extern "C" fn element_send_event<T: ElementImpl>(
502 ptr: *mut ffi::GstElement,
503 event: *mut ffi::GstEvent,
504) -> glib::ffi::gboolean {
505 unsafe {
506 let instance = &*(ptr as *mut T::Instance);
507 let imp = instance.imp();
508
509 panic_to_error!(imp, false, { imp.send_event(from_glib_full(event)) }).into_glib()
510 }
511}
512
513unsafe extern "C" fn element_query<T: ElementImpl>(
514 ptr: *mut ffi::GstElement,
515 query: *mut ffi::GstQuery,
516) -> glib::ffi::gboolean {
517 unsafe {
518 let instance = &*(ptr as *mut T::Instance);
519 let imp = instance.imp();
520 let query = QueryRef::from_mut_ptr(query);
521
522 panic_to_error!(imp, false, { imp.query(query) }).into_glib()
523 }
524}
525
526unsafe extern "C" fn element_set_context<T: ElementImpl>(
527 ptr: *mut ffi::GstElement,
528 context: *mut ffi::GstContext,
529) {
530 unsafe {
531 let instance = &*(ptr as *mut T::Instance);
532 let imp = instance.imp();
533
534 panic_to_error!(imp, (), { imp.set_context(&from_glib_borrow(context)) })
535 }
536}
537
538unsafe extern "C" fn element_set_clock<T: ElementImpl>(
539 ptr: *mut ffi::GstElement,
540 clock: *mut ffi::GstClock,
541) -> glib::ffi::gboolean {
542 unsafe {
543 let instance = &*(ptr as *mut T::Instance);
544 let imp = instance.imp();
545
546 let clock = Option::<crate::Clock>::from_glib_borrow(clock);
547
548 panic_to_error!(imp, false, { imp.set_clock(clock.as_ref().as_ref()) }).into_glib()
549 }
550}
551
552unsafe extern "C" fn element_provide_clock<T: ElementImpl>(
553 ptr: *mut ffi::GstElement,
554) -> *mut ffi::GstClock {
555 unsafe {
556 let instance = &*(ptr as *mut T::Instance);
557 let imp = instance.imp();
558
559 panic_to_error!(imp, None, { imp.provide_clock() }).into_glib_ptr()
560 }
561}
562
563unsafe extern "C" fn element_post_message<T: ElementImpl>(
564 ptr: *mut ffi::GstElement,
565 msg: *mut ffi::GstMessage,
566) -> glib::ffi::gboolean {
567 unsafe {
568 let instance = &*(ptr as *mut T::Instance);
569 let imp = instance.imp();
570
571 imp.post_message(from_glib_full(msg)).into_glib()
574 }
575}
576
577#[cfg(test)]
578mod tests {
579 use std::sync::{Arc, Mutex, OnceLock, atomic};
580
581 use super::*;
582 use crate::ElementFactory;
583
584 pub mod imp {
585 use super::*;
586
587 pub struct TestElement {
588 pub(super) srcpad: crate::Pad,
589 pub(super) sinkpad: crate::Pad,
590 pub(super) n_buffers: atomic::AtomicU32,
591 pub(super) reached_playing: atomic::AtomicBool,
592 pub(super) array: Arc<Mutex<Vec<String>>>,
593 }
594
595 impl TestElement {
596 fn sink_chain(
597 &self,
598 _pad: &crate::Pad,
599 buffer: crate::Buffer,
600 ) -> Result<crate::FlowSuccess, crate::FlowError> {
601 self.n_buffers.fetch_add(1, atomic::Ordering::SeqCst);
602 self.srcpad.push(buffer)
603 }
604
605 fn sink_event(&self, _pad: &crate::Pad, event: crate::Event) -> bool {
606 self.srcpad.push_event(event)
607 }
608
609 fn sink_query(&self, _pad: &crate::Pad, query: &mut crate::QueryRef) -> bool {
610 self.srcpad.peer_query(query)
611 }
612
613 fn src_event(&self, _pad: &crate::Pad, event: crate::Event) -> bool {
614 self.sinkpad.push_event(event)
615 }
616
617 fn src_query(&self, _pad: &crate::Pad, query: &mut crate::QueryRef) -> bool {
618 self.sinkpad.peer_query(query)
619 }
620 }
621
622 #[glib::object_subclass]
623 impl ObjectSubclass for TestElement {
624 const NAME: &'static str = "TestElement";
625 type Type = super::TestElement;
626 type ParentType = Element;
627
628 fn with_class(klass: &Self::Class) -> Self {
629 let templ = klass.pad_template("sink").unwrap();
630 let sinkpad = crate::Pad::builder_from_template(&templ)
631 .chain_function(|pad, parent, buffer| {
632 TestElement::catch_panic_pad_function(
633 parent,
634 || Err(crate::FlowError::Error),
635 |identity| identity.sink_chain(pad, buffer),
636 )
637 })
638 .event_function(|pad, parent, event| {
639 TestElement::catch_panic_pad_function(
640 parent,
641 || false,
642 |identity| identity.sink_event(pad, event),
643 )
644 })
645 .query_function(|pad, parent, query| {
646 TestElement::catch_panic_pad_function(
647 parent,
648 || false,
649 |identity| identity.sink_query(pad, query),
650 )
651 })
652 .build();
653
654 let templ = klass.pad_template("src").unwrap();
655 let srcpad = crate::Pad::builder_from_template(&templ)
656 .event_function(|pad, parent, event| {
657 TestElement::catch_panic_pad_function(
658 parent,
659 || false,
660 |identity| identity.src_event(pad, event),
661 )
662 })
663 .query_function(|pad, parent, query| {
664 TestElement::catch_panic_pad_function(
665 parent,
666 || false,
667 |identity| identity.src_query(pad, query),
668 )
669 })
670 .build();
671
672 Self {
673 n_buffers: atomic::AtomicU32::new(0),
674 reached_playing: atomic::AtomicBool::new(false),
675 array: Arc::new(Mutex::new(vec![
676 "default0".to_string(),
677 "default1".to_string(),
678 ])),
679 srcpad,
680 sinkpad,
681 }
682 }
683 }
684
685 impl ObjectImpl for TestElement {
686 fn constructed(&self) {
687 self.parent_constructed();
688
689 let element = self.obj();
690 element.add_pad(&self.sinkpad).unwrap();
691 element.add_pad(&self.srcpad).unwrap();
692 }
693
694 fn properties() -> &'static [glib::ParamSpec] {
695 static PROPERTIES: OnceLock<Vec<glib::ParamSpec>> = OnceLock::new();
696 PROPERTIES.get_or_init(|| vec![crate::ParamSpecArray::builder("array").build()])
697 }
698
699 fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
700 match pspec.name() {
701 "array" => {
702 let value = value.get::<crate::Array>().unwrap();
703 let mut array = self.array.lock().unwrap();
704 array.clear();
705 array.extend(value.iter().map(|v| v.get().unwrap()));
706 }
707 _ => unimplemented!(),
708 }
709 }
710
711 fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
712 match pspec.name() {
713 "array" => crate::Array::new(&*self.array.lock().unwrap()).to_value(),
714 _ => unimplemented!(),
715 }
716 }
717 }
718
719 impl GstObjectImpl for TestElement {}
720
721 impl ElementImpl for TestElement {
722 fn metadata() -> Option<&'static ElementMetadata> {
723 static ELEMENT_METADATA: std::sync::OnceLock<ElementMetadata> =
724 std::sync::OnceLock::new();
725
726 Some(ELEMENT_METADATA.get_or_init(|| {
727 ElementMetadata::new(
728 "Test Element",
729 "Generic",
730 "Does nothing",
731 "Sebastian Dröge <[email protected]>",
732 )
733 }))
734 }
735
736 fn pad_templates() -> &'static [PadTemplate] {
737 static PAD_TEMPLATES: std::sync::OnceLock<Vec<PadTemplate>> =
738 std::sync::OnceLock::new();
739
740 PAD_TEMPLATES.get_or_init(|| {
741 let caps = crate::Caps::new_any();
742 vec![
743 PadTemplate::new(
744 "src",
745 crate::PadDirection::Src,
746 crate::PadPresence::Always,
747 &caps,
748 )
749 .unwrap(),
750 PadTemplate::new(
751 "sink",
752 crate::PadDirection::Sink,
753 crate::PadPresence::Always,
754 &caps,
755 )
756 .unwrap(),
757 ]
758 })
759 }
760
761 fn change_state(
762 &self,
763 transition: crate::StateChange,
764 ) -> Result<crate::StateChangeSuccess, crate::StateChangeError> {
765 let res = self.parent_change_state(transition)?;
766
767 if transition == crate::StateChange::PausedToPlaying {
768 self.reached_playing.store(true, atomic::Ordering::SeqCst);
769 }
770
771 Ok(res)
772 }
773 }
774 }
775
776 glib::wrapper! {
777 pub struct TestElement(ObjectSubclass<imp::TestElement>) @extends Element, crate::Object;
778 }
779
780 impl TestElement {
781 pub fn new(name: Option<&str>) -> Self {
782 glib::Object::builder().property("name", name).build()
783 }
784 }
785
786 fn plugin_init(plugin: &crate::Plugin) -> Result<(), glib::BoolError> {
787 crate::Element::register(
788 Some(plugin),
789 "testelement",
790 crate::Rank::MARGINAL,
791 TestElement::static_type(),
792 )
793 }
794
795 crate::plugin_define!(
796 rssubclasstestelem,
797 env!("CARGO_PKG_DESCRIPTION"),
798 plugin_init,
799 env!("CARGO_PKG_VERSION"),
800 "MPL-2.0",
801 env!("CARGO_PKG_NAME"),
802 env!("CARGO_PKG_NAME"),
803 env!("CARGO_PKG_REPOSITORY"),
804 "1970-01-01"
805 );
806
807 fn init() {
808 use std::sync::Once;
809 static INIT: Once = Once::new();
810
811 INIT.call_once(|| {
812 crate::init().unwrap();
813 plugin_register_static().expect("gstreamer subclass element test");
814 });
815 }
816
817 #[test]
818 fn test_element_subclass() {
819 init();
820
821 let element = TestElement::new(Some("test"));
822
823 assert_eq!(element.name(), "test");
824
825 assert_eq!(
826 element.metadata(crate::ELEMENT_METADATA_LONGNAME),
827 Some("Test Element")
828 );
829
830 let pipeline = crate::Pipeline::new();
831 let src = ElementFactory::make("fakesrc")
832 .property("num-buffers", 100i32)
833 .build()
834 .unwrap();
835 let sink = ElementFactory::make("fakesink").build().unwrap();
836
837 pipeline
838 .add_many([&src, element.upcast_ref(), &sink])
839 .unwrap();
840 Element::link_many([&src, element.upcast_ref(), &sink]).unwrap();
841
842 pipeline.set_state(crate::State::Playing).unwrap();
843 let bus = pipeline.bus().unwrap();
844
845 let eos = bus.timed_pop_filtered(crate::ClockTime::NONE, &[crate::MessageType::Eos]);
846 assert!(eos.is_some());
847
848 pipeline.set_state(crate::State::Null).unwrap();
849
850 let imp = element.imp();
851 assert_eq!(imp.n_buffers.load(atomic::Ordering::SeqCst), 100);
852 assert!(imp.reached_playing.load(atomic::Ordering::SeqCst));
853 }
854
855 #[test]
856 fn property_from_iter_if_not_empty() {
857 init();
858
859 let elem = crate::ElementFactory::make("testelement").build().unwrap();
860 assert!(
861 elem.property::<crate::Array>("array")
862 .iter()
863 .map(|val| val.get::<&str>().unwrap())
864 .eq(["default0", "default1"])
865 );
866
867 let elem = crate::ElementFactory::make("testelement")
868 .property_from_iter::<crate::Array, _>("array", ["value0", "value1"])
869 .build()
870 .unwrap();
871 assert!(
872 elem.property::<crate::Array>("array")
873 .iter()
874 .map(|val| val.get::<&str>().unwrap())
875 .eq(["value0", "value1"])
876 );
877
878 let array = Vec::<String>::new();
879 let elem = crate::ElementFactory::make("testelement")
880 .property_if_not_empty::<crate::Array, _>("array", &array)
881 .build()
882 .unwrap();
883 assert!(
884 elem.property::<crate::Array>("array")
885 .iter()
886 .map(|val| val.get::<&str>().unwrap())
887 .eq(["default0", "default1"])
888 );
889
890 let elem = crate::ElementFactory::make("testelement")
891 .property_if_not_empty::<crate::Array, _>("array", ["value0", "value1"])
892 .build()
893 .unwrap();
894 assert!(
895 elem.property::<crate::Array>("array")
896 .iter()
897 .map(|val| val.get::<&str>().unwrap())
898 .eq(["value0", "value1"])
899 );
900 }
901}