1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{borrow::Cow, future::Future, sync::atomic}; |
4 | |
5 | use glib::{subclass::prelude::*, translate::*}; |
6 | |
7 | use super::prelude::*; |
8 | use crate::{ |
9 | prelude::*, Element, Event, PadTemplate, QueryRef, StateChange, StateChangeError, |
10 | StateChangeReturn, StateChangeSuccess, |
11 | }; |
12 | |
13 | #[derive (Debug, Clone)] |
14 | pub 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 | |
22 | impl 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 | |
70 | pub 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 | |
124 | mod sealed { |
125 | pub trait Sealed {} |
126 | impl<T: super::ElementImplExt> Sealed for T {} |
127 | } |
128 | |
129 | pub 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_full(), |
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 | self.instance_data::<atomic::AtomicBool>(crate::Element::static_type()) |
290 | .expect("instance not initialized correctly" ) |
291 | } |
292 | |
293 | fn catch_panic<R, F: FnOnce(&Self) -> R, G: FnOnce() -> R>(&self, fallback: G, f: F) -> R { |
294 | panic_to_error!(self, fallback(), { f(self) }) |
295 | } |
296 | |
297 | fn catch_panic_future<R, F: FnOnce() -> R, G: Future<Output = R>>( |
298 | &self, |
299 | fallback: F, |
300 | fut: G, |
301 | ) -> CatchPanic<Self, F, G> { |
302 | CatchPanic { |
303 | self_: self.ref_counted().downgrade(), |
304 | fallback: Some(fallback), |
305 | fut, |
306 | } |
307 | } |
308 | |
309 | fn catch_panic_pad_function<R, F: FnOnce(&Self) -> R, G: FnOnce() -> R>( |
310 | parent: Option<&crate::Object>, |
311 | fallback: G, |
312 | f: F, |
313 | ) -> R { |
314 | let element = parent.unwrap().dynamic_cast_ref::<Self::Type>().unwrap(); |
315 | let imp = element.imp(); |
316 | |
317 | panic_to_error!(imp, fallback(), { f(imp) }) |
318 | } |
319 | |
320 | fn post_error_message(&self, msg: crate::ErrorMessage) { |
321 | unsafe { |
322 | self.obj() |
323 | .unsafe_cast_ref::<Element>() |
324 | .post_error_message(msg) |
325 | } |
326 | } |
327 | } |
328 | |
329 | impl<T: ElementImpl> ElementImplExt for T {} |
330 | |
331 | pin_project_lite::pin_project! { |
332 | #[must_use = "futures do nothing unless you `.await` or poll them" ] |
333 | pub struct CatchPanic<T: glib::subclass::types::ObjectSubclass, F, G> { |
334 | self_: glib::subclass::ObjectImplWeakRef<T>, |
335 | fallback: Option<F>, |
336 | #[pin] |
337 | fut: G, |
338 | } |
339 | } |
340 | |
341 | impl<R, T: ElementImpl, F: FnOnce() -> R, G: Future<Output = R>> Future for CatchPanic<T, F, G> { |
342 | type Output = R; |
343 | |
344 | fn poll( |
345 | self: std::pin::Pin<&mut Self>, |
346 | cx: &mut std::task::Context<'_>, |
347 | ) -> std::task::Poll<Self::Output> { |
348 | let this = self.project(); |
349 | |
350 | let Some(self_) = this.self_.upgrade() else { |
351 | return std::task::Poll::Ready((this |
352 | .fallback |
353 | .take() |
354 | .expect("Future polled after resolving" ))( |
355 | )); |
356 | }; |
357 | |
358 | panic_to_error!( |
359 | &*self_, |
360 | std::task::Poll::Ready(this.fallback.take().expect("Future polled after resolving" )()), |
361 | { |
362 | let fut = this.fut; |
363 | fut.poll(cx) |
364 | } |
365 | ) |
366 | } |
367 | } |
368 | |
369 | unsafe impl<T: ElementImpl> IsSubclassable<T> for Element { |
370 | fn class_init(klass: &mut glib::Class<Self>) { |
371 | Self::parent_class_init::<T>(klass); |
372 | let klass = klass.as_mut(); |
373 | klass.change_state = Some(element_change_state::<T>); |
374 | klass.request_new_pad = Some(element_request_new_pad::<T>); |
375 | klass.release_pad = Some(element_release_pad::<T>); |
376 | klass.send_event = Some(element_send_event::<T>); |
377 | klass.query = Some(element_query::<T>); |
378 | klass.set_context = Some(element_set_context::<T>); |
379 | klass.set_clock = Some(element_set_clock::<T>); |
380 | klass.provide_clock = Some(element_provide_clock::<T>); |
381 | klass.post_message = Some(element_post_message::<T>); |
382 | |
383 | unsafe { |
384 | for pad_template in T::pad_templates() { |
385 | ffi::gst_element_class_add_pad_template(klass, pad_template.to_glib_none().0); |
386 | } |
387 | |
388 | if let Some(metadata) = T::metadata() { |
389 | ffi::gst_element_class_set_metadata( |
390 | klass, |
391 | metadata.long_name.to_glib_none().0, |
392 | metadata.classification.to_glib_none().0, |
393 | metadata.description.to_glib_none().0, |
394 | metadata.author.to_glib_none().0, |
395 | ); |
396 | |
397 | for (key, value) in &metadata.additional[..] { |
398 | ffi::gst_element_class_add_metadata( |
399 | klass, |
400 | key.to_glib_none().0, |
401 | value.to_glib_none().0, |
402 | ); |
403 | } |
404 | } |
405 | } |
406 | } |
407 | |
408 | fn instance_init(instance: &mut glib::subclass::InitializingObject<T>) { |
409 | Self::parent_instance_init::<T>(instance); |
410 | |
411 | instance.set_instance_data(Self::static_type(), atomic::AtomicBool::new(false)); |
412 | } |
413 | } |
414 | |
415 | unsafe extern "C" fn element_change_state<T: ElementImpl>( |
416 | ptr: *mut ffi::GstElement, |
417 | transition: ffi::GstStateChange, |
418 | ) -> ffi::GstStateChangeReturn { |
419 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
420 | let imp: &T = instance.imp(); |
421 | |
422 | // *Never* fail downwards state changes, this causes bugs in GStreamer |
423 | // and leads to crashes and deadlocks. |
424 | let transition: StateChange = from_glib(val:transition); |
425 | let fallback: StateChangeReturn = match transition { |
426 | StateChange::PlayingToPaused | StateChange::PausedToReady | StateChange::ReadyToNull => { |
427 | StateChangeReturn::Success |
428 | } |
429 | _ => StateChangeReturn::Failure, |
430 | }; |
431 | |
432 | panic_to_error!(imp, fallback, { imp.change_state(transition).into() }).into_glib() |
433 | } |
434 | |
435 | unsafe extern "C" fn element_request_new_pad<T: ElementImpl>( |
436 | ptr: *mut ffi::GstElement, |
437 | templ: *mut ffi::GstPadTemplate, |
438 | name: *const libc::c_char, |
439 | caps: *const ffi::GstCaps, |
440 | ) -> *mut ffi::GstPad { |
441 | let instance = &*(ptr as *mut T::Instance); |
442 | let imp = instance.imp(); |
443 | |
444 | let caps = Option::<crate::Caps>::from_glib_borrow(caps); |
445 | let name = Option::<String>::from_glib_none(name); |
446 | |
447 | // XXX: This is effectively unsafe but the best we can do |
448 | // See https://bugzilla.gnome.org/show_bug.cgi?id=791193 |
449 | let pad = panic_to_error!(imp, None, { |
450 | imp.request_new_pad( |
451 | &from_glib_borrow(templ), |
452 | name.as_deref(), |
453 | caps.as_ref().as_ref(), |
454 | ) |
455 | }); |
456 | |
457 | // Ensure that the pad is owned by the element now, if a pad was returned |
458 | if let Some(ref pad) = pad { |
459 | assert_eq!( |
460 | pad.parent().as_ref(), |
461 | Some(&*crate::Object::from_glib_borrow( |
462 | ptr as *mut ffi::GstObject |
463 | )) |
464 | ); |
465 | } |
466 | |
467 | pad.to_glib_none().0 |
468 | } |
469 | |
470 | unsafe extern "C" fn element_release_pad<T: ElementImpl>( |
471 | ptr: *mut ffi::GstElement, |
472 | pad: *mut ffi::GstPad, |
473 | ) { |
474 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
475 | let imp: &T = instance.imp(); |
476 | |
477 | // If we get a floating reference passed simply return here. It can't be stored inside this |
478 | // element, and if we continued to use it we would take ownership of this floating reference. |
479 | if glib::gobject_ffi::g_object_is_floating(object:pad as *mut glib::gobject_ffi::GObject) |
480 | != glib::ffi::GFALSE |
481 | { |
482 | return; |
483 | } |
484 | |
485 | panic_to_error!(imp, (), { imp.release_pad(&from_glib_none(pad)) }) |
486 | } |
487 | |
488 | unsafe extern "C" fn element_send_event<T: ElementImpl>( |
489 | ptr: *mut ffi::GstElement, |
490 | event: *mut ffi::GstEvent, |
491 | ) -> glib::ffi::gboolean { |
492 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
493 | let imp: &T = instance.imp(); |
494 | |
495 | panic_to_error!(imp, false, { imp.send_event(from_glib_full(event)) }).into_glib() |
496 | } |
497 | |
498 | unsafe extern "C" fn element_query<T: ElementImpl>( |
499 | ptr: *mut ffi::GstElement, |
500 | query: *mut ffi::GstQuery, |
501 | ) -> glib::ffi::gboolean { |
502 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
503 | let imp: &T = instance.imp(); |
504 | let query: &mut QueryRef = QueryRef::from_mut_ptr(query); |
505 | |
506 | panic_to_error!(imp, false, { imp.query(query) }).into_glib() |
507 | } |
508 | |
509 | unsafe extern "C" fn element_set_context<T: ElementImpl>( |
510 | ptr: *mut ffi::GstElement, |
511 | context: *mut ffi::GstContext, |
512 | ) { |
513 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
514 | let imp: &T = instance.imp(); |
515 | |
516 | panic_to_error!(imp, (), { imp.set_context(&from_glib_borrow(context)) }) |
517 | } |
518 | |
519 | unsafe extern "C" fn element_set_clock<T: ElementImpl>( |
520 | ptr: *mut ffi::GstElement, |
521 | clock: *mut ffi::GstClock, |
522 | ) -> glib::ffi::gboolean { |
523 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
524 | let imp: &T = instance.imp(); |
525 | |
526 | let clock: Borrowed = Option::<crate::Clock>::from_glib_borrow(_ptr:clock); |
527 | |
528 | panic_to_error!(imp, false, { imp.set_clock(clock.as_ref().as_ref()) }).into_glib() |
529 | } |
530 | |
531 | unsafe extern "C" fn element_provide_clock<T: ElementImpl>( |
532 | ptr: *mut ffi::GstElement, |
533 | ) -> *mut ffi::GstClock { |
534 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
535 | let imp: &T = instance.imp(); |
536 | |
537 | panic_to_error!(imp, None, { imp.provide_clock() }).into_glib_ptr() |
538 | } |
539 | |
540 | unsafe extern "C" fn element_post_message<T: ElementImpl>( |
541 | ptr: *mut ffi::GstElement, |
542 | msg: *mut ffi::GstMessage, |
543 | ) -> glib::ffi::gboolean { |
544 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
545 | let imp: &T = instance.imp(); |
546 | |
547 | // Can't catch panics here as posting the error message would cause |
548 | // this code to be called again recursively forever. |
549 | imp.post_message(msg:from_glib_full(ptr:msg)).into_glib() |
550 | } |
551 | |
552 | #[cfg (test)] |
553 | mod tests { |
554 | use std::sync::atomic; |
555 | |
556 | use super::*; |
557 | use crate::ElementFactory; |
558 | |
559 | pub mod imp { |
560 | use super::*; |
561 | |
562 | pub struct TestElement { |
563 | pub(super) srcpad: crate::Pad, |
564 | pub(super) sinkpad: crate::Pad, |
565 | pub(super) n_buffers: atomic::AtomicU32, |
566 | pub(super) reached_playing: atomic::AtomicBool, |
567 | } |
568 | |
569 | impl TestElement { |
570 | fn sink_chain( |
571 | &self, |
572 | _pad: &crate::Pad, |
573 | buffer: crate::Buffer, |
574 | ) -> Result<crate::FlowSuccess, crate::FlowError> { |
575 | self.n_buffers.fetch_add(1, atomic::Ordering::SeqCst); |
576 | self.srcpad.push(buffer) |
577 | } |
578 | |
579 | fn sink_event(&self, _pad: &crate::Pad, event: crate::Event) -> bool { |
580 | self.srcpad.push_event(event) |
581 | } |
582 | |
583 | fn sink_query(&self, _pad: &crate::Pad, query: &mut crate::QueryRef) -> bool { |
584 | self.srcpad.peer_query(query) |
585 | } |
586 | |
587 | fn src_event(&self, _pad: &crate::Pad, event: crate::Event) -> bool { |
588 | self.sinkpad.push_event(event) |
589 | } |
590 | |
591 | fn src_query(&self, _pad: &crate::Pad, query: &mut crate::QueryRef) -> bool { |
592 | self.sinkpad.peer_query(query) |
593 | } |
594 | } |
595 | |
596 | #[glib::object_subclass ] |
597 | impl ObjectSubclass for TestElement { |
598 | const NAME: &'static str = "TestElement" ; |
599 | type Type = super::TestElement; |
600 | type ParentType = Element; |
601 | |
602 | fn with_class(klass: &Self::Class) -> Self { |
603 | let templ = klass.pad_template("sink" ).unwrap(); |
604 | let sinkpad = crate::Pad::builder_from_template(&templ) |
605 | .chain_function(|pad, parent, buffer| { |
606 | TestElement::catch_panic_pad_function( |
607 | parent, |
608 | || Err(crate::FlowError::Error), |
609 | |identity| identity.sink_chain(pad, buffer), |
610 | ) |
611 | }) |
612 | .event_function(|pad, parent, event| { |
613 | TestElement::catch_panic_pad_function( |
614 | parent, |
615 | || false, |
616 | |identity| identity.sink_event(pad, event), |
617 | ) |
618 | }) |
619 | .query_function(|pad, parent, query| { |
620 | TestElement::catch_panic_pad_function( |
621 | parent, |
622 | || false, |
623 | |identity| identity.sink_query(pad, query), |
624 | ) |
625 | }) |
626 | .build(); |
627 | |
628 | let templ = klass.pad_template("src" ).unwrap(); |
629 | let srcpad = crate::Pad::builder_from_template(&templ) |
630 | .event_function(|pad, parent, event| { |
631 | TestElement::catch_panic_pad_function( |
632 | parent, |
633 | || false, |
634 | |identity| identity.src_event(pad, event), |
635 | ) |
636 | }) |
637 | .query_function(|pad, parent, query| { |
638 | TestElement::catch_panic_pad_function( |
639 | parent, |
640 | || false, |
641 | |identity| identity.src_query(pad, query), |
642 | ) |
643 | }) |
644 | .build(); |
645 | |
646 | Self { |
647 | n_buffers: atomic::AtomicU32::new(0), |
648 | reached_playing: atomic::AtomicBool::new(false), |
649 | srcpad, |
650 | sinkpad, |
651 | } |
652 | } |
653 | } |
654 | |
655 | impl ObjectImpl for TestElement { |
656 | fn constructed(&self) { |
657 | self.parent_constructed(); |
658 | |
659 | let element = self.obj(); |
660 | element.add_pad(&self.sinkpad).unwrap(); |
661 | element.add_pad(&self.srcpad).unwrap(); |
662 | } |
663 | } |
664 | |
665 | impl GstObjectImpl for TestElement {} |
666 | |
667 | impl ElementImpl for TestElement { |
668 | fn metadata() -> Option<&'static ElementMetadata> { |
669 | use glib::once_cell::sync::Lazy; |
670 | static ELEMENT_METADATA: Lazy<ElementMetadata> = Lazy::new(|| { |
671 | ElementMetadata::new( |
672 | "Test Element" , |
673 | "Generic" , |
674 | "Does nothing" , |
675 | "Sebastian Dröge <sebastian@centricular.com>" , |
676 | ) |
677 | }); |
678 | |
679 | Some(&*ELEMENT_METADATA) |
680 | } |
681 | |
682 | fn pad_templates() -> &'static [PadTemplate] { |
683 | use glib::once_cell::sync::Lazy; |
684 | static PAD_TEMPLATES: Lazy<Vec<PadTemplate>> = Lazy::new(|| { |
685 | let caps = crate::Caps::new_any(); |
686 | vec![ |
687 | PadTemplate::new( |
688 | "src" , |
689 | crate::PadDirection::Src, |
690 | crate::PadPresence::Always, |
691 | &caps, |
692 | ) |
693 | .unwrap(), |
694 | PadTemplate::new( |
695 | "sink" , |
696 | crate::PadDirection::Sink, |
697 | crate::PadPresence::Always, |
698 | &caps, |
699 | ) |
700 | .unwrap(), |
701 | ] |
702 | }); |
703 | |
704 | PAD_TEMPLATES.as_ref() |
705 | } |
706 | |
707 | fn change_state( |
708 | &self, |
709 | transition: crate::StateChange, |
710 | ) -> Result<crate::StateChangeSuccess, crate::StateChangeError> { |
711 | let res = self.parent_change_state(transition)?; |
712 | |
713 | if transition == crate::StateChange::PausedToPlaying { |
714 | self.reached_playing.store(true, atomic::Ordering::SeqCst); |
715 | } |
716 | |
717 | Ok(res) |
718 | } |
719 | } |
720 | } |
721 | |
722 | glib::wrapper! { |
723 | pub struct TestElement(ObjectSubclass<imp::TestElement>) @extends Element, crate::Object; |
724 | } |
725 | |
726 | impl TestElement { |
727 | pub fn new(name: Option<&str>) -> Self { |
728 | glib::Object::builder().property("name" , name).build() |
729 | } |
730 | } |
731 | |
732 | #[test ] |
733 | fn test_element_subclass() { |
734 | crate::init().unwrap(); |
735 | |
736 | let element = TestElement::new(Some("test" )); |
737 | |
738 | assert_eq!(element.name(), "test" ); |
739 | |
740 | assert_eq!( |
741 | element.metadata(crate::ELEMENT_METADATA_LONGNAME), |
742 | Some("Test Element" ) |
743 | ); |
744 | |
745 | let pipeline = crate::Pipeline::new(); |
746 | let src = ElementFactory::make("fakesrc" ) |
747 | .property("num-buffers" , 100i32) |
748 | .build() |
749 | .unwrap(); |
750 | let sink = ElementFactory::make("fakesink" ).build().unwrap(); |
751 | |
752 | pipeline |
753 | .add_many([&src, element.upcast_ref(), &sink]) |
754 | .unwrap(); |
755 | Element::link_many([&src, element.upcast_ref(), &sink]).unwrap(); |
756 | |
757 | pipeline.set_state(crate::State::Playing).unwrap(); |
758 | let bus = pipeline.bus().unwrap(); |
759 | |
760 | let eos = bus.timed_pop_filtered(crate::ClockTime::NONE, &[crate::MessageType::Eos]); |
761 | assert!(eos.is_some()); |
762 | |
763 | pipeline.set_state(crate::State::Null).unwrap(); |
764 | |
765 | let imp = element.imp(); |
766 | assert_eq!(imp.n_buffers.load(atomic::Ordering::SeqCst), 100); |
767 | assert!(imp.reached_playing.load(atomic::Ordering::SeqCst)); |
768 | } |
769 | } |
770 | |