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