1 // Copyright (C) 2017-2019 Sebastian Dröge <sebastian@centricular.com>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 use libc;
10
11 use glib_sys;
12 use gst_sys;
13
14 use super::prelude::*;
15 use glib;
16 use glib::prelude::*;
17 use glib::subclass::prelude::*;
18 use glib::translate::*;
19 use prelude::*;
20
21 use Element;
22 use ElementClass;
23 use Event;
24 use PadTemplate;
25 use QueryRef;
26 use StateChange;
27 use StateChangeError;
28 use StateChangeReturn;
29 use StateChangeSuccess;
30
31 pub trait ElementImpl: ElementImplExt + ObjectImpl + Send + Sync + 'static {
change_state( &self, element: &::Element, transition: StateChange, ) -> Result<StateChangeSuccess, StateChangeError>32 fn change_state(
33 &self,
34 element: &::Element,
35 transition: StateChange,
36 ) -> Result<StateChangeSuccess, StateChangeError> {
37 self.parent_change_state(element, transition)
38 }
39
request_new_pad( &self, element: &::Element, templ: &::PadTemplate, name: Option<String>, caps: Option<&::Caps>, ) -> Option<::Pad>40 fn request_new_pad(
41 &self,
42 element: &::Element,
43 templ: &::PadTemplate,
44 name: Option<String>,
45 caps: Option<&::Caps>,
46 ) -> Option<::Pad> {
47 self.parent_request_new_pad(element, templ, name, caps)
48 }
49
release_pad(&self, element: &::Element, pad: &::Pad)50 fn release_pad(&self, element: &::Element, pad: &::Pad) {
51 self.parent_release_pad(element, pad)
52 }
53
send_event(&self, element: &::Element, event: Event) -> bool54 fn send_event(&self, element: &::Element, event: Event) -> bool {
55 self.parent_send_event(element, event)
56 }
57
query(&self, element: &::Element, query: &mut QueryRef) -> bool58 fn query(&self, element: &::Element, query: &mut QueryRef) -> bool {
59 self.parent_query(element, query)
60 }
61
set_context(&self, element: &::Element, context: &::Context)62 fn set_context(&self, element: &::Element, context: &::Context) {
63 self.parent_set_context(element, context)
64 }
65
set_clock(&self, element: &::Element, clock: Option<&::Clock>) -> bool66 fn set_clock(&self, element: &::Element, clock: Option<&::Clock>) -> bool {
67 self.parent_set_clock(element, clock)
68 }
69
provide_clock(&self, element: &::Element) -> Option<::Clock>70 fn provide_clock(&self, element: &::Element) -> Option<::Clock> {
71 self.parent_provide_clock(element)
72 }
73 }
74
75 pub trait ElementImplExt {
parent_change_state( &self, element: &::Element, transition: StateChange, ) -> Result<StateChangeSuccess, StateChangeError>76 fn parent_change_state(
77 &self,
78 element: &::Element,
79 transition: StateChange,
80 ) -> Result<StateChangeSuccess, StateChangeError>;
81
parent_request_new_pad( &self, element: &::Element, templ: &::PadTemplate, name: Option<String>, caps: Option<&::Caps>, ) -> Option<::Pad>82 fn parent_request_new_pad(
83 &self,
84 element: &::Element,
85 templ: &::PadTemplate,
86 name: Option<String>,
87 caps: Option<&::Caps>,
88 ) -> Option<::Pad>;
89
parent_release_pad(&self, element: &::Element, pad: &::Pad)90 fn parent_release_pad(&self, element: &::Element, pad: &::Pad);
91
parent_send_event(&self, element: &::Element, event: Event) -> bool92 fn parent_send_event(&self, element: &::Element, event: Event) -> bool;
93
parent_query(&self, element: &::Element, query: &mut QueryRef) -> bool94 fn parent_query(&self, element: &::Element, query: &mut QueryRef) -> bool;
95
parent_set_context(&self, element: &::Element, context: &::Context)96 fn parent_set_context(&self, element: &::Element, context: &::Context);
97
parent_set_clock(&self, element: &::Element, clock: Option<&::Clock>) -> bool98 fn parent_set_clock(&self, element: &::Element, clock: Option<&::Clock>) -> bool;
99
parent_provide_clock(&self, element: &::Element) -> Option<::Clock>100 fn parent_provide_clock(&self, element: &::Element) -> Option<::Clock>;
101
catch_panic< R, F: FnOnce(&Self) -> R, G: FnOnce() -> R, P: IsA<::Element> + IsA<glib::Object> + glib::value::SetValue, >( &self, element: &P, fallback: G, f: F, ) -> R102 fn catch_panic<
103 R,
104 F: FnOnce(&Self) -> R,
105 G: FnOnce() -> R,
106 P: IsA<::Element> + IsA<glib::Object> + glib::value::SetValue,
107 >(
108 &self,
109 element: &P,
110 fallback: G,
111 f: F,
112 ) -> R;
113
catch_panic_pad_function<R, F: FnOnce(&Self, &::Element) -> R, G: FnOnce() -> R>( parent: Option<&::Object>, fallback: G, f: F, ) -> R114 fn catch_panic_pad_function<R, F: FnOnce(&Self, &::Element) -> R, G: FnOnce() -> R>(
115 parent: Option<&::Object>,
116 fallback: G,
117 f: F,
118 ) -> R;
119 }
120
121 impl<T: ElementImpl + ObjectSubclass> ElementImplExt for T
122 where
123 T::Instance: PanicPoison,
124 {
parent_change_state( &self, element: &::Element, transition: StateChange, ) -> Result<StateChangeSuccess, StateChangeError>125 fn parent_change_state(
126 &self,
127 element: &::Element,
128 transition: StateChange,
129 ) -> Result<StateChangeSuccess, StateChangeError> {
130 unsafe {
131 let data = self.get_type_data();
132 let parent_class = data.as_ref().get_parent_class() as *mut gst_sys::GstElementClass;
133
134 let f = (*parent_class)
135 .change_state
136 .expect("Missing parent function `change_state`");
137 StateChangeReturn::from_glib(f(element.to_glib_none().0, transition.to_glib()))
138 .into_result()
139 }
140 }
141
parent_request_new_pad( &self, element: &::Element, templ: &::PadTemplate, name: Option<String>, caps: Option<&::Caps>, ) -> Option<::Pad>142 fn parent_request_new_pad(
143 &self,
144 element: &::Element,
145 templ: &::PadTemplate,
146 name: Option<String>,
147 caps: Option<&::Caps>,
148 ) -> Option<::Pad> {
149 unsafe {
150 let data = self.get_type_data();
151 let parent_class = data.as_ref().get_parent_class() as *mut gst_sys::GstElementClass;
152
153 (*parent_class)
154 .request_new_pad
155 .map(|f| {
156 from_glib_none(f(
157 element.to_glib_none().0,
158 templ.to_glib_none().0,
159 name.to_glib_full(),
160 caps.to_glib_none().0,
161 ))
162 })
163 .unwrap_or(None)
164 }
165 }
166
parent_release_pad(&self, element: &::Element, pad: &::Pad)167 fn parent_release_pad(&self, element: &::Element, pad: &::Pad) {
168 unsafe {
169 let data = self.get_type_data();
170 let parent_class = data.as_ref().get_parent_class() as *mut gst_sys::GstElementClass;
171
172 (*parent_class)
173 .release_pad
174 .map(|f| f(element.to_glib_none().0, pad.to_glib_none().0))
175 .unwrap_or(())
176 }
177 }
178
parent_send_event(&self, element: &::Element, event: Event) -> bool179 fn parent_send_event(&self, element: &::Element, event: Event) -> bool {
180 unsafe {
181 let data = self.get_type_data();
182 let parent_class = data.as_ref().get_parent_class() as *mut gst_sys::GstElementClass;
183
184 (*parent_class)
185 .send_event
186 .map(|f| from_glib(f(element.to_glib_none().0, event.into_ptr())))
187 .unwrap_or(false)
188 }
189 }
190
parent_query(&self, element: &::Element, query: &mut QueryRef) -> bool191 fn parent_query(&self, element: &::Element, query: &mut QueryRef) -> bool {
192 unsafe {
193 let data = self.get_type_data();
194 let parent_class = data.as_ref().get_parent_class() as *mut gst_sys::GstElementClass;
195
196 (*parent_class)
197 .query
198 .map(|f| from_glib(f(element.to_glib_none().0, query.as_mut_ptr())))
199 .unwrap_or(false)
200 }
201 }
202
parent_set_context(&self, element: &::Element, context: &::Context)203 fn parent_set_context(&self, element: &::Element, context: &::Context) {
204 unsafe {
205 let data = self.get_type_data();
206 let parent_class = data.as_ref().get_parent_class() as *mut gst_sys::GstElementClass;
207
208 (*parent_class)
209 .set_context
210 .map(|f| f(element.to_glib_none().0, context.to_glib_none().0))
211 .unwrap_or(())
212 }
213 }
214
parent_set_clock(&self, element: &::Element, clock: Option<&::Clock>) -> bool215 fn parent_set_clock(&self, element: &::Element, clock: Option<&::Clock>) -> bool {
216 unsafe {
217 let data = self.get_type_data();
218 let parent_class = data.as_ref().get_parent_class() as *mut gst_sys::GstElementClass;
219
220 (*parent_class)
221 .set_clock
222 .map(|f| from_glib(f(element.to_glib_none().0, clock.to_glib_none().0)))
223 .unwrap_or(false)
224 }
225 }
226
parent_provide_clock(&self, element: &::Element) -> Option<::Clock>227 fn parent_provide_clock(&self, element: &::Element) -> Option<::Clock> {
228 unsafe {
229 let data = self.get_type_data();
230 let parent_class = data.as_ref().get_parent_class() as *mut gst_sys::GstElementClass;
231
232 (*parent_class)
233 .provide_clock
234 .map(|f| from_glib_none(f(element.to_glib_none().0)))
235 .unwrap_or(None)
236 }
237 }
238
catch_panic< R, F: FnOnce(&Self) -> R, G: FnOnce() -> R, P: IsA<::Element> + IsA<glib::Object> + glib::value::SetValue, >( &self, element: &P, fallback: G, f: F, ) -> R239 fn catch_panic<
240 R,
241 F: FnOnce(&Self) -> R,
242 G: FnOnce() -> R,
243 P: IsA<::Element> + IsA<glib::Object> + glib::value::SetValue,
244 >(
245 &self,
246 element: &P,
247 fallback: G,
248 f: F,
249 ) -> R {
250 unsafe {
251 assert!(element.get_type().is_a(&T::get_type()));
252 let ptr: *mut gst_sys::GstElement = element.as_ptr() as *mut _;
253 let instance = &*(ptr as *mut T::Instance);
254 let imp = instance.get_impl();
255
256 gst_panic_to_error!(element, &instance.panicked(), fallback(), { f(&imp) })
257 }
258 }
259
catch_panic_pad_function<R, F: FnOnce(&Self, &::Element) -> R, G: FnOnce() -> R>( parent: Option<&::Object>, fallback: G, f: F, ) -> R260 fn catch_panic_pad_function<R, F: FnOnce(&Self, &::Element) -> R, G: FnOnce() -> R>(
261 parent: Option<&::Object>,
262 fallback: G,
263 f: F,
264 ) -> R {
265 unsafe {
266 let wrap = parent
267 .as_ref()
268 .unwrap()
269 .downcast_ref::<::Element>()
270 .unwrap();
271 assert!(wrap.get_type().is_a(&T::get_type()));
272 let ptr: *mut gst_sys::GstElement = wrap.to_glib_none().0;
273 let instance = &*(ptr as *mut T::Instance);
274 let imp = instance.get_impl();
275
276 gst_panic_to_error!(wrap, &instance.panicked(), fallback(), { f(&imp, &wrap) })
277 }
278 }
279 }
280
281 pub unsafe trait ElementClassSubclassExt: Sized + 'static {
add_pad_template(&mut self, pad_template: PadTemplate)282 fn add_pad_template(&mut self, pad_template: PadTemplate) {
283 unsafe {
284 gst_sys::gst_element_class_add_pad_template(
285 self as *mut Self as *mut gst_sys::GstElementClass,
286 pad_template.to_glib_none().0,
287 );
288 }
289 }
290
set_metadata( &mut self, long_name: &str, classification: &str, description: &str, author: &str, )291 fn set_metadata(
292 &mut self,
293 long_name: &str,
294 classification: &str,
295 description: &str,
296 author: &str,
297 ) {
298 unsafe {
299 gst_sys::gst_element_class_set_metadata(
300 self as *mut Self as *mut gst_sys::GstElementClass,
301 long_name.to_glib_none().0,
302 classification.to_glib_none().0,
303 description.to_glib_none().0,
304 author.to_glib_none().0,
305 );
306 }
307 }
308
add_metadata(&mut self, key: &str, value: &str)309 fn add_metadata(&mut self, key: &str, value: &str) {
310 unsafe {
311 gst_sys::gst_element_class_add_metadata(
312 self as *mut Self as *mut gst_sys::GstElementClass,
313 key.to_glib_none().0,
314 value.to_glib_none().0,
315 );
316 }
317 }
318 }
319
320 unsafe impl ElementClassSubclassExt for ElementClass {}
321
322 unsafe impl<T: ObjectSubclass + ElementImpl> IsSubclassable<T> for ElementClass
323 where
324 <T as ObjectSubclass>::Instance: PanicPoison,
325 {
override_vfuncs(&mut self)326 fn override_vfuncs(&mut self) {
327 <glib::ObjectClass as IsSubclassable<T>>::override_vfuncs(self);
328
329 unsafe {
330 let klass = &mut *(self as *mut Self as *mut gst_sys::GstElementClass);
331 klass.change_state = Some(element_change_state::<T>);
332 klass.request_new_pad = Some(element_request_new_pad::<T>);
333 klass.release_pad = Some(element_release_pad::<T>);
334 klass.send_event = Some(element_send_event::<T>);
335 klass.query = Some(element_query::<T>);
336 klass.set_context = Some(element_set_context::<T>);
337 klass.set_clock = Some(element_set_clock::<T>);
338 klass.provide_clock = Some(element_provide_clock::<T>);
339 }
340 }
341 }
342
element_change_state<T: ObjectSubclass>( ptr: *mut gst_sys::GstElement, transition: gst_sys::GstStateChange, ) -> gst_sys::GstStateChangeReturn where T: ElementImpl, T::Instance: PanicPoison,343 unsafe extern "C" fn element_change_state<T: ObjectSubclass>(
344 ptr: *mut gst_sys::GstElement,
345 transition: gst_sys::GstStateChange,
346 ) -> gst_sys::GstStateChangeReturn
347 where
348 T: ElementImpl,
349 T::Instance: PanicPoison,
350 {
351 let instance = &*(ptr as *mut T::Instance);
352 let imp = instance.get_impl();
353 let wrap: Element = from_glib_borrow(ptr);
354
355 // *Never* fail downwards state changes, this causes bugs in GStreamer
356 // and leads to crashes and deadlocks.
357 let transition = from_glib(transition);
358 let fallback = match transition {
359 StateChange::PlayingToPaused | StateChange::PausedToReady | StateChange::ReadyToNull => {
360 StateChangeReturn::Success
361 }
362 _ => StateChangeReturn::Failure,
363 };
364
365 gst_panic_to_error!(&wrap, &instance.panicked(), fallback, {
366 imp.change_state(&wrap, transition).into()
367 })
368 .to_glib()
369 }
370
element_request_new_pad<T: ObjectSubclass>( ptr: *mut gst_sys::GstElement, templ: *mut gst_sys::GstPadTemplate, name: *const libc::c_char, caps: *const gst_sys::GstCaps, ) -> *mut gst_sys::GstPad where T: ElementImpl, T::Instance: PanicPoison,371 unsafe extern "C" fn element_request_new_pad<T: ObjectSubclass>(
372 ptr: *mut gst_sys::GstElement,
373 templ: *mut gst_sys::GstPadTemplate,
374 name: *const libc::c_char,
375 caps: *const gst_sys::GstCaps,
376 ) -> *mut gst_sys::GstPad
377 where
378 T: ElementImpl,
379 T::Instance: PanicPoison,
380 {
381 let instance = &*(ptr as *mut T::Instance);
382 let imp = instance.get_impl();
383 let wrap: Element = from_glib_borrow(ptr);
384
385 let caps = Option::<::Caps>::from_glib_borrow(caps);
386
387 // XXX: This is effectively unsafe but the best we can do
388 // See https://bugzilla.gnome.org/show_bug.cgi?id=791193
389 let pad = gst_panic_to_error!(&wrap, &instance.panicked(), None, {
390 imp.request_new_pad(
391 &wrap,
392 &from_glib_borrow(templ),
393 from_glib_none(name),
394 caps.as_ref(),
395 )
396 });
397
398 // Ensure that the pad is owned by the element now, if a pad was returned
399 if let Some(ref pad) = pad {
400 assert_eq!(
401 pad.get_parent(),
402 Some(::Object::from_glib_borrow(ptr as *mut gst_sys::GstObject))
403 );
404 }
405
406 pad.to_glib_none().0
407 }
408
element_release_pad<T: ObjectSubclass>( ptr: *mut gst_sys::GstElement, pad: *mut gst_sys::GstPad, ) where T: ElementImpl, T::Instance: PanicPoison,409 unsafe extern "C" fn element_release_pad<T: ObjectSubclass>(
410 ptr: *mut gst_sys::GstElement,
411 pad: *mut gst_sys::GstPad,
412 ) where
413 T: ElementImpl,
414 T::Instance: PanicPoison,
415 {
416 let instance = &*(ptr as *mut T::Instance);
417 let imp = instance.get_impl();
418 let wrap: Element = from_glib_borrow(ptr);
419
420 // If we get a floating reference passed simply return here. It can't be stored inside this
421 // element, and if we continued to use it we would take ownership of this floating reference.
422 if gobject_sys::g_object_is_floating(pad as *mut gobject_sys::GObject) != glib_sys::GFALSE {
423 return;
424 }
425
426 gst_panic_to_error!(&wrap, &instance.panicked(), (), {
427 imp.release_pad(&wrap, &from_glib_none(pad))
428 })
429 }
430
element_send_event<T: ObjectSubclass>( ptr: *mut gst_sys::GstElement, event: *mut gst_sys::GstEvent, ) -> glib_sys::gboolean where T: ElementImpl, T::Instance: PanicPoison,431 unsafe extern "C" fn element_send_event<T: ObjectSubclass>(
432 ptr: *mut gst_sys::GstElement,
433 event: *mut gst_sys::GstEvent,
434 ) -> glib_sys::gboolean
435 where
436 T: ElementImpl,
437 T::Instance: PanicPoison,
438 {
439 let instance = &*(ptr as *mut T::Instance);
440 let imp = instance.get_impl();
441 let wrap: Element = from_glib_borrow(ptr);
442
443 gst_panic_to_error!(&wrap, &instance.panicked(), false, {
444 imp.send_event(&wrap, from_glib_full(event))
445 })
446 .to_glib()
447 }
448
element_query<T: ObjectSubclass>( ptr: *mut gst_sys::GstElement, query: *mut gst_sys::GstQuery, ) -> glib_sys::gboolean where T: ElementImpl, T::Instance: PanicPoison,449 unsafe extern "C" fn element_query<T: ObjectSubclass>(
450 ptr: *mut gst_sys::GstElement,
451 query: *mut gst_sys::GstQuery,
452 ) -> glib_sys::gboolean
453 where
454 T: ElementImpl,
455 T::Instance: PanicPoison,
456 {
457 let instance = &*(ptr as *mut T::Instance);
458 let imp = instance.get_impl();
459 let wrap: Element = from_glib_borrow(ptr);
460 let query = QueryRef::from_mut_ptr(query);
461
462 gst_panic_to_error!(&wrap, &instance.panicked(), false, {
463 imp.query(&wrap, query)
464 })
465 .to_glib()
466 }
467
element_set_context<T: ObjectSubclass>( ptr: *mut gst_sys::GstElement, context: *mut gst_sys::GstContext, ) where T: ElementImpl, T::Instance: PanicPoison,468 unsafe extern "C" fn element_set_context<T: ObjectSubclass>(
469 ptr: *mut gst_sys::GstElement,
470 context: *mut gst_sys::GstContext,
471 ) where
472 T: ElementImpl,
473 T::Instance: PanicPoison,
474 {
475 let instance = &*(ptr as *mut T::Instance);
476 let imp = instance.get_impl();
477 let wrap: Element = from_glib_borrow(ptr);
478
479 gst_panic_to_error!(&wrap, &instance.panicked(), (), {
480 imp.set_context(&wrap, &from_glib_borrow(context))
481 })
482 }
483
element_set_clock<T: ObjectSubclass>( ptr: *mut gst_sys::GstElement, clock: *mut gst_sys::GstClock, ) -> glib_sys::gboolean where T: ElementImpl, T::Instance: PanicPoison,484 unsafe extern "C" fn element_set_clock<T: ObjectSubclass>(
485 ptr: *mut gst_sys::GstElement,
486 clock: *mut gst_sys::GstClock,
487 ) -> glib_sys::gboolean
488 where
489 T: ElementImpl,
490 T::Instance: PanicPoison,
491 {
492 let instance = &*(ptr as *mut T::Instance);
493 let imp = instance.get_impl();
494 let wrap: Element = from_glib_borrow(ptr);
495
496 let clock = Option::<::Clock>::from_glib_borrow(clock);
497
498 gst_panic_to_error!(&wrap, &instance.panicked(), false, {
499 imp.set_clock(&wrap, clock.as_ref())
500 })
501 .to_glib()
502 }
503
element_provide_clock<T: ObjectSubclass>( ptr: *mut gst_sys::GstElement, ) -> *mut gst_sys::GstClock where T: ElementImpl, T::Instance: PanicPoison,504 unsafe extern "C" fn element_provide_clock<T: ObjectSubclass>(
505 ptr: *mut gst_sys::GstElement,
506 ) -> *mut gst_sys::GstClock
507 where
508 T: ElementImpl,
509 T::Instance: PanicPoison,
510 {
511 let instance = &*(ptr as *mut T::Instance);
512 let imp = instance.get_impl();
513 let wrap: Element = from_glib_borrow(ptr);
514
515 gst_panic_to_error!(&wrap, &instance.panicked(), None, {
516 imp.provide_clock(&wrap)
517 })
518 .to_glib_full()
519 }
520
521 #[cfg(test)]
522 mod tests {
523 use super::*;
524 use glib;
525 use glib::subclass;
526 use std::sync::atomic;
527
528 struct TestElement {
529 srcpad: ::Pad,
530 sinkpad: ::Pad,
531 n_buffers: atomic::AtomicU32,
532 reached_playing: atomic::AtomicBool,
533 }
534
535 impl TestElement {
set_pad_functions(sinkpad: &::Pad, srcpad: &::Pad)536 fn set_pad_functions(sinkpad: &::Pad, srcpad: &::Pad) {
537 sinkpad.set_chain_function(|pad, parent, buffer| {
538 TestElement::catch_panic_pad_function(
539 parent,
540 || Err(::FlowError::Error),
541 |identity, element| identity.sink_chain(pad, element, buffer),
542 )
543 });
544 sinkpad.set_event_function(|pad, parent, event| {
545 TestElement::catch_panic_pad_function(
546 parent,
547 || false,
548 |identity, element| identity.sink_event(pad, element, event),
549 )
550 });
551 sinkpad.set_query_function(|pad, parent, query| {
552 TestElement::catch_panic_pad_function(
553 parent,
554 || false,
555 |identity, element| identity.sink_query(pad, element, query),
556 )
557 });
558
559 srcpad.set_event_function(|pad, parent, event| {
560 TestElement::catch_panic_pad_function(
561 parent,
562 || false,
563 |identity, element| identity.src_event(pad, element, event),
564 )
565 });
566 srcpad.set_query_function(|pad, parent, query| {
567 TestElement::catch_panic_pad_function(
568 parent,
569 || false,
570 |identity, element| identity.src_query(pad, element, query),
571 )
572 });
573 }
574
sink_chain( &self, _pad: &::Pad, _element: &::Element, buffer: ::Buffer, ) -> Result<::FlowSuccess, ::FlowError>575 fn sink_chain(
576 &self,
577 _pad: &::Pad,
578 _element: &::Element,
579 buffer: ::Buffer,
580 ) -> Result<::FlowSuccess, ::FlowError> {
581 self.n_buffers.fetch_add(1, atomic::Ordering::SeqCst);
582 self.srcpad.push(buffer)
583 }
584
sink_event(&self, _pad: &::Pad, _element: &::Element, event: ::Event) -> bool585 fn sink_event(&self, _pad: &::Pad, _element: &::Element, event: ::Event) -> bool {
586 self.srcpad.push_event(event)
587 }
588
sink_query(&self, _pad: &::Pad, _element: &::Element, query: &mut ::QueryRef) -> bool589 fn sink_query(&self, _pad: &::Pad, _element: &::Element, query: &mut ::QueryRef) -> bool {
590 self.srcpad.peer_query(query)
591 }
592
src_event(&self, _pad: &::Pad, _element: &::Element, event: ::Event) -> bool593 fn src_event(&self, _pad: &::Pad, _element: &::Element, event: ::Event) -> bool {
594 self.sinkpad.push_event(event)
595 }
596
src_query(&self, _pad: &::Pad, _element: &::Element, query: &mut ::QueryRef) -> bool597 fn src_query(&self, _pad: &::Pad, _element: &::Element, query: &mut ::QueryRef) -> bool {
598 self.sinkpad.peer_query(query)
599 }
600 }
601
602 impl ObjectSubclass for TestElement {
603 const NAME: &'static str = "TestElement";
604 type ParentType = ::Element;
605 type Instance = ::subclass::ElementInstanceStruct<Self>;
606 type Class = subclass::simple::ClassStruct<Self>;
607
608 glib_object_subclass!();
609
new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self610 fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
611 let templ = klass.get_pad_template("sink").unwrap();
612 let sinkpad = ::Pad::new_from_template(&templ, Some("sink"));
613 let templ = klass.get_pad_template("src").unwrap();
614 let srcpad = ::Pad::new_from_template(&templ, Some("src"));
615
616 TestElement::set_pad_functions(&sinkpad, &srcpad);
617
618 Self {
619 n_buffers: atomic::AtomicU32::new(0),
620 reached_playing: atomic::AtomicBool::new(false),
621 srcpad,
622 sinkpad,
623 }
624 }
625
class_init(klass: &mut subclass::simple::ClassStruct<Self>)626 fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
627 klass.set_metadata(
628 "Test Element",
629 "Generic",
630 "Does nothing",
631 "Sebastian Dröge <sebastian@centricular.com>",
632 );
633
634 let caps = ::Caps::new_any();
635 let src_pad_template =
636 ::PadTemplate::new("src", ::PadDirection::Src, ::PadPresence::Always, &caps)
637 .unwrap();
638 klass.add_pad_template(src_pad_template);
639
640 let sink_pad_template =
641 ::PadTemplate::new("sink", ::PadDirection::Sink, ::PadPresence::Always, &caps)
642 .unwrap();
643 klass.add_pad_template(sink_pad_template);
644 }
645 }
646
647 impl ObjectImpl for TestElement {
648 glib_object_impl!();
649
constructed(&self, obj: &glib::Object)650 fn constructed(&self, obj: &glib::Object) {
651 self.parent_constructed(obj);
652
653 let element = obj.downcast_ref::<::Element>().unwrap();
654 element.add_pad(&self.sinkpad).unwrap();
655 element.add_pad(&self.srcpad).unwrap();
656 }
657 }
658
659 impl ElementImpl for TestElement {
change_state( &self, element: &::Element, transition: ::StateChange, ) -> Result<::StateChangeSuccess, ::StateChangeError>660 fn change_state(
661 &self,
662 element: &::Element,
663 transition: ::StateChange,
664 ) -> Result<::StateChangeSuccess, ::StateChangeError> {
665 let res = self.parent_change_state(element, transition)?;
666
667 if transition == ::StateChange::PausedToPlaying {
668 self.reached_playing.store(true, atomic::Ordering::SeqCst);
669 }
670
671 Ok(res)
672 }
673 }
674
675 #[test]
test_element_subclass()676 fn test_element_subclass() {
677 ::init().unwrap();
678
679 let element = glib::Object::new(TestElement::get_type(), &[("name", &"test")])
680 .unwrap()
681 .downcast::<::Element>()
682 .unwrap();
683
684 assert_eq!(element.get_name(), "test");
685
686 assert_eq!(
687 element.get_metadata(&::ELEMENT_METADATA_LONGNAME),
688 Some("Test Element")
689 );
690
691 let pipeline = ::Pipeline::new(None);
692 let src = ::ElementFactory::make("fakesrc", None).unwrap();
693 let sink = ::ElementFactory::make("fakesink", None).unwrap();
694
695 src.set_property("num-buffers", &100i32).unwrap();
696
697 pipeline.add_many(&[&src, &element, &sink]).unwrap();
698 ::Element::link_many(&[&src, &element, &sink]).unwrap();
699
700 pipeline.set_state(::State::Playing).unwrap();
701 let bus = pipeline.get_bus().unwrap();
702
703 let eos = bus.timed_pop_filtered(::CLOCK_TIME_NONE, &[::MessageType::Eos]);
704 assert!(eos.is_some());
705
706 pipeline.set_state(::State::Null).unwrap();
707
708 let imp = TestElement::from_instance(&element);
709 assert_eq!(imp.n_buffers.load(atomic::Ordering::SeqCst), 100);
710 assert_eq!(imp.reached_playing.load(atomic::Ordering::SeqCst), true);
711 }
712 }
713