1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{mem, ptr};
4
5use glib::translate::*;
6use gst::subclass::prelude::*;
7
8use crate::{prelude::*, BaseTransform};
9
10#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11pub enum BaseTransformMode {
12 AlwaysInPlace,
13 NeverInPlace,
14 Both,
15}
16
17pub trait BaseTransformImpl: BaseTransformImplExt + ElementImpl {
18 const MODE: BaseTransformMode;
19 const PASSTHROUGH_ON_SAME_CAPS: bool;
20 const TRANSFORM_IP_ON_PASSTHROUGH: bool;
21
22 fn start(&self) -> Result<(), gst::ErrorMessage> {
23 self.parent_start()
24 }
25
26 fn stop(&self) -> Result<(), gst::ErrorMessage> {
27 self.parent_stop()
28 }
29
30 fn transform_caps(
31 &self,
32 direction: gst::PadDirection,
33 caps: &gst::Caps,
34 filter: Option<&gst::Caps>,
35 ) -> Option<gst::Caps> {
36 self.parent_transform_caps(direction, caps, filter)
37 }
38
39 fn fixate_caps(
40 &self,
41 direction: gst::PadDirection,
42 caps: &gst::Caps,
43 othercaps: gst::Caps,
44 ) -> gst::Caps {
45 self.parent_fixate_caps(direction, caps, othercaps)
46 }
47
48 fn set_caps(&self, incaps: &gst::Caps, outcaps: &gst::Caps) -> Result<(), gst::LoggableError> {
49 self.parent_set_caps(incaps, outcaps)
50 }
51
52 fn accept_caps(&self, direction: gst::PadDirection, caps: &gst::Caps) -> bool {
53 self.parent_accept_caps(direction, caps)
54 }
55
56 fn query(&self, direction: gst::PadDirection, query: &mut gst::QueryRef) -> bool {
57 BaseTransformImplExt::parent_query(self, direction, query)
58 }
59
60 fn transform_size(
61 &self,
62 direction: gst::PadDirection,
63 caps: &gst::Caps,
64 size: usize,
65 othercaps: &gst::Caps,
66 ) -> Option<usize> {
67 self.parent_transform_size(direction, caps, size, othercaps)
68 }
69
70 fn unit_size(&self, caps: &gst::Caps) -> Option<usize> {
71 self.parent_unit_size(caps)
72 }
73
74 fn sink_event(&self, event: gst::Event) -> bool {
75 self.parent_sink_event(event)
76 }
77
78 fn src_event(&self, event: gst::Event) -> bool {
79 self.parent_src_event(event)
80 }
81
82 fn prepare_output_buffer(
83 &self,
84 inbuf: InputBuffer,
85 ) -> Result<PrepareOutputBufferSuccess, gst::FlowError> {
86 self.parent_prepare_output_buffer(inbuf)
87 }
88
89 fn transform(
90 &self,
91 inbuf: &gst::Buffer,
92 outbuf: &mut gst::BufferRef,
93 ) -> Result<gst::FlowSuccess, gst::FlowError> {
94 self.parent_transform(inbuf, outbuf)
95 }
96
97 fn transform_ip(&self, buf: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
98 self.parent_transform_ip(buf)
99 }
100
101 fn transform_ip_passthrough(
102 &self,
103 buf: &gst::Buffer,
104 ) -> Result<gst::FlowSuccess, gst::FlowError> {
105 self.parent_transform_ip_passthrough(buf)
106 }
107
108 fn propose_allocation(
109 &self,
110 decide_query: Option<&gst::query::Allocation>,
111 query: &mut gst::query::Allocation,
112 ) -> Result<(), gst::LoggableError> {
113 self.parent_propose_allocation(decide_query, query)
114 }
115
116 fn decide_allocation(
117 &self,
118 query: &mut gst::query::Allocation,
119 ) -> Result<(), gst::LoggableError> {
120 self.parent_decide_allocation(query)
121 }
122
123 fn copy_metadata(
124 &self,
125 inbuf: &gst::BufferRef,
126 outbuf: &mut gst::BufferRef,
127 ) -> Result<(), gst::LoggableError> {
128 self.parent_copy_metadata(inbuf, outbuf)
129 }
130
131 fn transform_meta<'a>(
132 &self,
133 outbuf: &mut gst::BufferRef,
134 meta: gst::MetaRef<'a, gst::Meta>,
135 inbuf: &'a gst::BufferRef,
136 ) -> bool {
137 self.parent_transform_meta(outbuf, meta, inbuf)
138 }
139
140 fn before_transform(&self, inbuf: &gst::BufferRef) {
141 self.parent_before_transform(inbuf);
142 }
143
144 fn submit_input_buffer(
145 &self,
146 is_discont: bool,
147 inbuf: gst::Buffer,
148 ) -> Result<gst::FlowSuccess, gst::FlowError> {
149 self.parent_submit_input_buffer(is_discont, inbuf)
150 }
151
152 fn generate_output(&self) -> Result<GenerateOutputSuccess, gst::FlowError> {
153 self.parent_generate_output()
154 }
155}
156
157mod sealed {
158 pub trait Sealed {}
159 impl<T: super::BaseTransformImplExt> Sealed for T {}
160}
161
162pub trait BaseTransformImplExt: sealed::Sealed + ObjectSubclass {
163 fn parent_start(&self) -> Result<(), gst::ErrorMessage> {
164 unsafe {
165 let data = Self::type_data();
166 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
167 (*parent_class)
168 .start
169 .map(|f| {
170 if from_glib(f(self
171 .obj()
172 .unsafe_cast_ref::<BaseTransform>()
173 .to_glib_none()
174 .0))
175 {
176 Ok(())
177 } else {
178 Err(gst::error_msg!(
179 gst::CoreError::StateChange,
180 ["Parent function `start` failed"]
181 ))
182 }
183 })
184 .unwrap_or(Ok(()))
185 }
186 }
187
188 fn parent_stop(&self) -> Result<(), gst::ErrorMessage> {
189 unsafe {
190 let data = Self::type_data();
191 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
192 (*parent_class)
193 .stop
194 .map(|f| {
195 if from_glib(f(self
196 .obj()
197 .unsafe_cast_ref::<BaseTransform>()
198 .to_glib_none()
199 .0))
200 {
201 Ok(())
202 } else {
203 Err(gst::error_msg!(
204 gst::CoreError::StateChange,
205 ["Parent function `stop` failed"]
206 ))
207 }
208 })
209 .unwrap_or(Ok(()))
210 }
211 }
212
213 fn parent_transform_caps(
214 &self,
215 direction: gst::PadDirection,
216 caps: &gst::Caps,
217 filter: Option<&gst::Caps>,
218 ) -> Option<gst::Caps> {
219 unsafe {
220 let data = Self::type_data();
221 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
222 (*parent_class)
223 .transform_caps
224 .map(|f| {
225 from_glib_full(f(
226 self.obj()
227 .unsafe_cast_ref::<BaseTransform>()
228 .to_glib_none()
229 .0,
230 direction.into_glib(),
231 caps.to_glib_none().0,
232 filter.to_glib_none().0,
233 ))
234 })
235 .unwrap_or(None)
236 }
237 }
238
239 fn parent_fixate_caps(
240 &self,
241 direction: gst::PadDirection,
242 caps: &gst::Caps,
243 othercaps: gst::Caps,
244 ) -> gst::Caps {
245 unsafe {
246 let data = Self::type_data();
247 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
248 match (*parent_class).fixate_caps {
249 Some(f) => from_glib_full(f(
250 self.obj()
251 .unsafe_cast_ref::<BaseTransform>()
252 .to_glib_none()
253 .0,
254 direction.into_glib(),
255 caps.to_glib_none().0,
256 othercaps.into_glib_ptr(),
257 )),
258 None => othercaps,
259 }
260 }
261 }
262
263 fn parent_set_caps(
264 &self,
265 incaps: &gst::Caps,
266 outcaps: &gst::Caps,
267 ) -> Result<(), gst::LoggableError> {
268 unsafe {
269 let data = Self::type_data();
270 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
271 (*parent_class)
272 .set_caps
273 .map(|f| {
274 gst::result_from_gboolean!(
275 f(
276 self.obj()
277 .unsafe_cast_ref::<BaseTransform>()
278 .to_glib_none()
279 .0,
280 incaps.to_glib_none().0,
281 outcaps.to_glib_none().0,
282 ),
283 gst::CAT_RUST,
284 "Parent function `set_caps` failed"
285 )
286 })
287 .unwrap_or(Ok(()))
288 }
289 }
290
291 fn parent_accept_caps(&self, direction: gst::PadDirection, caps: &gst::Caps) -> bool {
292 unsafe {
293 let data = Self::type_data();
294 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
295 (*parent_class)
296 .accept_caps
297 .map(|f| {
298 from_glib(f(
299 self.obj()
300 .unsafe_cast_ref::<BaseTransform>()
301 .to_glib_none()
302 .0,
303 direction.into_glib(),
304 caps.to_glib_none().0,
305 ))
306 })
307 .unwrap_or(false)
308 }
309 }
310
311 fn parent_query(&self, direction: gst::PadDirection, query: &mut gst::QueryRef) -> bool {
312 unsafe {
313 let data = Self::type_data();
314 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
315 (*parent_class)
316 .query
317 .map(|f| {
318 from_glib(f(
319 self.obj()
320 .unsafe_cast_ref::<BaseTransform>()
321 .to_glib_none()
322 .0,
323 direction.into_glib(),
324 query.as_mut_ptr(),
325 ))
326 })
327 .unwrap_or(false)
328 }
329 }
330
331 fn parent_transform_size(
332 &self,
333 direction: gst::PadDirection,
334 caps: &gst::Caps,
335 size: usize,
336 othercaps: &gst::Caps,
337 ) -> Option<usize> {
338 unsafe {
339 let data = Self::type_data();
340 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
341 (*parent_class)
342 .transform_size
343 .map(|f| {
344 let mut othersize = mem::MaybeUninit::uninit();
345 let res: bool = from_glib(f(
346 self.obj()
347 .unsafe_cast_ref::<BaseTransform>()
348 .to_glib_none()
349 .0,
350 direction.into_glib(),
351 caps.to_glib_none().0,
352 size,
353 othercaps.to_glib_none().0,
354 othersize.as_mut_ptr(),
355 ));
356 if res {
357 Some(othersize.assume_init())
358 } else {
359 None
360 }
361 })
362 .unwrap_or(None)
363 }
364 }
365
366 fn parent_unit_size(&self, caps: &gst::Caps) -> Option<usize> {
367 unsafe {
368 let data = Self::type_data();
369 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
370 let f = (*parent_class).get_unit_size.unwrap_or_else(|| {
371 if !self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
372 unimplemented!(concat!(
373 "Missing parent function `get_unit_size`. Required because ",
374 "transform doesn't operate in-place"
375 ))
376 } else {
377 unreachable!(concat!(
378 "parent `get_unit_size` called while transform operates in-place"
379 ))
380 }
381 });
382
383 let mut size = mem::MaybeUninit::uninit();
384 if from_glib(f(
385 self.obj()
386 .unsafe_cast_ref::<BaseTransform>()
387 .to_glib_none()
388 .0,
389 caps.to_glib_none().0,
390 size.as_mut_ptr(),
391 )) {
392 Some(size.assume_init())
393 } else {
394 None
395 }
396 }
397 }
398
399 fn parent_sink_event(&self, event: gst::Event) -> bool {
400 unsafe {
401 let data = Self::type_data();
402 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
403 (*parent_class)
404 .sink_event
405 .map(|f| {
406 from_glib(f(
407 self.obj()
408 .unsafe_cast_ref::<BaseTransform>()
409 .to_glib_none()
410 .0,
411 event.into_glib_ptr(),
412 ))
413 })
414 .unwrap_or(true)
415 }
416 }
417
418 fn parent_src_event(&self, event: gst::Event) -> bool {
419 unsafe {
420 let data = Self::type_data();
421 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
422 (*parent_class)
423 .src_event
424 .map(|f| {
425 from_glib(f(
426 self.obj()
427 .unsafe_cast_ref::<BaseTransform>()
428 .to_glib_none()
429 .0,
430 event.into_glib_ptr(),
431 ))
432 })
433 .unwrap_or(true)
434 }
435 }
436
437 fn parent_prepare_output_buffer(
438 &self,
439 inbuf: InputBuffer,
440 ) -> Result<PrepareOutputBufferSuccess, gst::FlowError> {
441 unsafe {
442 let data = Self::type_data();
443 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
444 let buf = match inbuf {
445 InputBuffer::Readable(inbuf_r) => inbuf_r.as_ptr(),
446 InputBuffer::Writable(inbuf_w) => inbuf_w.as_mut_ptr(),
447 };
448 (*parent_class)
449 .prepare_output_buffer
450 .map(|f| {
451 let mut outbuf: *mut gst::ffi::GstBuffer = ptr::null_mut();
452 // FIXME: Wrong signature in FFI
453 gst::FlowSuccess::try_from_glib(f(
454 self.obj()
455 .unsafe_cast_ref::<BaseTransform>()
456 .to_glib_none()
457 .0,
458 buf as *mut gst::ffi::GstBuffer,
459 (&mut outbuf) as *mut *mut gst::ffi::GstBuffer as *mut gst::ffi::GstBuffer,
460 ))
461 .map(|_| {
462 if outbuf == buf as *mut _ {
463 PrepareOutputBufferSuccess::InputBuffer
464 } else {
465 PrepareOutputBufferSuccess::Buffer(from_glib_full(outbuf))
466 }
467 })
468 .map_err(|err| {
469 if outbuf != buf as *mut _ {
470 drop(Option::<gst::Buffer>::from_glib_full(outbuf));
471 }
472
473 err
474 })
475 })
476 .unwrap_or(Err(gst::FlowError::NotSupported))
477 }
478 }
479
480 fn parent_transform(
481 &self,
482 inbuf: &gst::Buffer,
483 outbuf: &mut gst::BufferRef,
484 ) -> Result<gst::FlowSuccess, gst::FlowError> {
485 unsafe {
486 let data = Self::type_data();
487 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
488 (*parent_class)
489 .transform
490 .map(|f| {
491 try_from_glib(f(
492 self.obj()
493 .unsafe_cast_ref::<BaseTransform>()
494 .to_glib_none()
495 .0,
496 inbuf.to_glib_none().0,
497 outbuf.as_mut_ptr(),
498 ))
499 })
500 .unwrap_or_else(|| {
501 if !self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
502 Err(gst::FlowError::NotSupported)
503 } else {
504 unreachable!(concat!(
505 "parent `transform` called while transform operates in-place"
506 ));
507 }
508 })
509 }
510 }
511
512 fn parent_transform_ip(
513 &self,
514 buf: &mut gst::BufferRef,
515 ) -> Result<gst::FlowSuccess, gst::FlowError> {
516 unsafe {
517 let data = Self::type_data();
518 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
519 let f = (*parent_class).transform_ip.unwrap_or_else(|| {
520 if self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
521 panic!(concat!(
522 "Missing parent function `transform_ip`. Required because ",
523 "transform operates in-place"
524 ));
525 } else {
526 unreachable!(concat!(
527 "parent `transform` called while transform doesn't operate in-place"
528 ));
529 }
530 });
531
532 try_from_glib(f(
533 self.obj()
534 .unsafe_cast_ref::<BaseTransform>()
535 .to_glib_none()
536 .0,
537 buf.as_mut_ptr() as *mut _,
538 ))
539 }
540 }
541
542 fn parent_transform_ip_passthrough(
543 &self,
544 buf: &gst::Buffer,
545 ) -> Result<gst::FlowSuccess, gst::FlowError> {
546 unsafe {
547 let data = Self::type_data();
548 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
549 let f = (*parent_class).transform_ip.unwrap_or_else(|| {
550 if self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
551 panic!(concat!(
552 "Missing parent function `transform_ip`. Required because ",
553 "transform operates in-place (passthrough mode)"
554 ));
555 } else {
556 unreachable!(concat!(
557 "parent `transform_ip` called ",
558 "while transform doesn't operate in-place (passthrough mode)"
559 ));
560 }
561 });
562
563 // FIXME: Wrong signature in FFI
564 let buf: *mut gst::ffi::GstBuffer = buf.to_glib_none().0;
565 try_from_glib(f(
566 self.obj()
567 .unsafe_cast_ref::<BaseTransform>()
568 .to_glib_none()
569 .0,
570 buf as *mut _,
571 ))
572 }
573 }
574
575 fn parent_propose_allocation(
576 &self,
577 decide_query: Option<&gst::query::Allocation>,
578 query: &mut gst::query::Allocation,
579 ) -> Result<(), gst::LoggableError> {
580 unsafe {
581 let data = Self::type_data();
582 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
583 (*parent_class)
584 .propose_allocation
585 .map(|f| {
586 gst::result_from_gboolean!(
587 f(
588 self.obj()
589 .unsafe_cast_ref::<BaseTransform>()
590 .to_glib_none()
591 .0,
592 decide_query
593 .as_ref()
594 .map(|q| q.as_mut_ptr())
595 .unwrap_or(ptr::null_mut()),
596 query.as_mut_ptr(),
597 ),
598 gst::CAT_RUST,
599 "Parent function `propose_allocation` failed",
600 )
601 })
602 .unwrap_or(Ok(()))
603 }
604 }
605
606 fn parent_decide_allocation(
607 &self,
608 query: &mut gst::query::Allocation,
609 ) -> Result<(), gst::LoggableError> {
610 unsafe {
611 let data = Self::type_data();
612 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
613 (*parent_class)
614 .decide_allocation
615 .map(|f| {
616 gst::result_from_gboolean!(
617 f(
618 self.obj()
619 .unsafe_cast_ref::<BaseTransform>()
620 .to_glib_none()
621 .0,
622 query.as_mut_ptr(),
623 ),
624 gst::CAT_RUST,
625 "Parent function `decide_allocation` failed,"
626 )
627 })
628 .unwrap_or(Ok(()))
629 }
630 }
631
632 fn parent_copy_metadata(
633 &self,
634 inbuf: &gst::BufferRef,
635 outbuf: &mut gst::BufferRef,
636 ) -> Result<(), gst::LoggableError> {
637 unsafe {
638 let data = Self::type_data();
639 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
640 if let Some(ref f) = (*parent_class).copy_metadata {
641 gst::result_from_gboolean!(
642 f(
643 self.obj()
644 .unsafe_cast_ref::<BaseTransform>()
645 .to_glib_none()
646 .0,
647 inbuf.as_ptr() as *mut _,
648 outbuf.as_mut_ptr()
649 ),
650 gst::CAT_RUST,
651 "Parent function `copy_metadata` failed"
652 )
653 } else {
654 Ok(())
655 }
656 }
657 }
658
659 fn parent_transform_meta<'a>(
660 &self,
661 outbuf: &mut gst::BufferRef,
662 meta: gst::MetaRef<'a, gst::Meta>,
663 inbuf: &'a gst::BufferRef,
664 ) -> bool {
665 unsafe {
666 let data = Self::type_data();
667 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
668 (*parent_class)
669 .transform_meta
670 .map(|f| {
671 from_glib(f(
672 self.obj()
673 .unsafe_cast_ref::<BaseTransform>()
674 .to_glib_none()
675 .0,
676 outbuf.as_mut_ptr(),
677 meta.as_ptr() as *mut _,
678 inbuf.as_ptr() as *mut _,
679 ))
680 })
681 .unwrap_or(false)
682 }
683 }
684
685 fn parent_before_transform(&self, inbuf: &gst::BufferRef) {
686 unsafe {
687 let data = Self::type_data();
688 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
689 if let Some(ref f) = (*parent_class).before_transform {
690 f(
691 self.obj()
692 .unsafe_cast_ref::<BaseTransform>()
693 .to_glib_none()
694 .0,
695 inbuf.as_ptr() as *mut _,
696 );
697 }
698 }
699 }
700
701 fn parent_submit_input_buffer(
702 &self,
703 is_discont: bool,
704 inbuf: gst::Buffer,
705 ) -> Result<gst::FlowSuccess, gst::FlowError> {
706 unsafe {
707 let data = Self::type_data();
708 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
709 let f = (*parent_class)
710 .submit_input_buffer
711 .expect("Missing parent function `submit_input_buffer`");
712
713 try_from_glib(f(
714 self.obj()
715 .unsafe_cast_ref::<BaseTransform>()
716 .to_glib_none()
717 .0,
718 is_discont.into_glib(),
719 inbuf.into_glib_ptr(),
720 ))
721 }
722 }
723
724 fn parent_generate_output(&self) -> Result<GenerateOutputSuccess, gst::FlowError> {
725 unsafe {
726 let data = Self::type_data();
727 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
728 let f = (*parent_class)
729 .generate_output
730 .expect("Missing parent function `generate_output`");
731
732 let mut outbuf = ptr::null_mut();
733 let res = gst::FlowSuccess::try_from_glib(f(
734 self.obj()
735 .unsafe_cast_ref::<BaseTransform>()
736 .to_glib_none()
737 .0,
738 &mut outbuf,
739 ));
740
741 let outbuf = Option::<gst::Buffer>::from_glib_full(outbuf);
742
743 res.map(move |res| match (res, outbuf) {
744 (crate::BASE_TRANSFORM_FLOW_DROPPED, _) => GenerateOutputSuccess::Dropped,
745 (gst::FlowSuccess::Ok, Some(outbuf)) => GenerateOutputSuccess::Buffer(outbuf),
746 _ => GenerateOutputSuccess::NoOutput,
747 })
748 }
749 }
750
751 fn take_queued_buffer(&self) -> Option<gst::Buffer>
752 where
753 Self: ObjectSubclass,
754 <Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
755 {
756 unsafe {
757 let instance = self.obj();
758 let ptr: *mut ffi::GstBaseTransform =
759 instance.unsafe_cast_ref::<BaseTransform>().to_glib_none().0;
760 let sinkpad: Borrowed<gst::Pad> = from_glib_borrow((*ptr).sinkpad);
761 let _stream_lock = sinkpad.stream_lock();
762 let buffer = (*ptr).queued_buf;
763 (*ptr).queued_buf = ptr::null_mut();
764 from_glib_full(buffer)
765 }
766 }
767
768 fn queued_buffer(&self) -> Option<gst::Buffer>
769 where
770 Self: ObjectSubclass,
771 <Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
772 {
773 unsafe {
774 let instance = self.obj();
775 let ptr: *mut ffi::GstBaseTransform =
776 instance.unsafe_cast_ref::<BaseTransform>().to_glib_none().0;
777 let sinkpad: Borrowed<gst::Pad> = from_glib_borrow((*ptr).sinkpad);
778 let _stream_lock = sinkpad.stream_lock();
779 let buffer = (*ptr).queued_buf;
780 from_glib_none(buffer)
781 }
782 }
783}
784
785impl<T: BaseTransformImpl> BaseTransformImplExt for T {}
786
787unsafe impl<T: BaseTransformImpl> IsSubclassable<T> for BaseTransform {
788 fn class_init(klass: &mut glib::Class<Self>) {
789 Self::parent_class_init::<T>(klass);
790 let klass = klass.as_mut();
791 klass.start = Some(base_transform_start::<T>);
792 klass.stop = Some(base_transform_stop::<T>);
793 klass.transform_caps = Some(base_transform_transform_caps::<T>);
794 klass.fixate_caps = Some(base_transform_fixate_caps::<T>);
795 klass.set_caps = Some(base_transform_set_caps::<T>);
796 klass.accept_caps = Some(base_transform_accept_caps::<T>);
797 klass.query = Some(base_transform_query::<T>);
798 klass.transform_size = Some(base_transform_transform_size::<T>);
799 klass.get_unit_size = Some(base_transform_get_unit_size::<T>);
800 klass.prepare_output_buffer = Some(base_transform_prepare_output_buffer::<T>);
801 klass.sink_event = Some(base_transform_sink_event::<T>);
802 klass.src_event = Some(base_transform_src_event::<T>);
803 klass.transform_meta = Some(base_transform_transform_meta::<T>);
804 klass.propose_allocation = Some(base_transform_propose_allocation::<T>);
805 klass.decide_allocation = Some(base_transform_decide_allocation::<T>);
806 klass.copy_metadata = Some(base_transform_copy_metadata::<T>);
807 klass.before_transform = Some(base_transform_before_transform::<T>);
808 klass.submit_input_buffer = Some(base_transform_submit_input_buffer::<T>);
809 klass.generate_output = Some(base_transform_generate_output::<T>);
810
811 klass.passthrough_on_same_caps = T::PASSTHROUGH_ON_SAME_CAPS.into_glib();
812 klass.transform_ip_on_passthrough = T::TRANSFORM_IP_ON_PASSTHROUGH.into_glib();
813
814 match T::MODE {
815 BaseTransformMode::AlwaysInPlace => {
816 klass.transform = None;
817 klass.transform_ip = Some(base_transform_transform_ip::<T>);
818 }
819 BaseTransformMode::NeverInPlace => {
820 klass.transform = Some(base_transform_transform::<T>);
821 klass.transform_ip = None;
822 }
823 BaseTransformMode::Both => {
824 klass.transform = Some(base_transform_transform::<T>);
825 klass.transform_ip = Some(base_transform_transform_ip::<T>);
826 }
827 }
828 }
829}
830
831#[derive(Debug)]
832pub enum GenerateOutputSuccess {
833 Buffer(gst::Buffer),
834 NoOutput,
835 Dropped,
836}
837
838#[derive(Debug)]
839pub enum PrepareOutputBufferSuccess {
840 Buffer(gst::Buffer),
841 InputBuffer,
842}
843
844#[derive(Debug)]
845pub enum InputBuffer<'a> {
846 Writable(&'a mut gst::BufferRef),
847 Readable(&'a gst::BufferRef),
848}
849
850unsafe extern "C" fn base_transform_start<T: BaseTransformImpl>(
851 ptr: *mut ffi::GstBaseTransform,
852) -> glib::ffi::gboolean {
853 let instance: &::Instance = &*(ptr as *mut T::Instance);
854 let imp: &T = instance.imp();
855
856 gstbool::panic_to_error!(imp, false, {
857 match imp.start() {
858 Ok(()) => true,
859 Err(err) => {
860 imp.post_error_message(err);
861 false
862 }
863 }
864 })
865 .into_glib()
866}
867
868unsafe extern "C" fn base_transform_stop<T: BaseTransformImpl>(
869 ptr: *mut ffi::GstBaseTransform,
870) -> glib::ffi::gboolean {
871 let instance: &::Instance = &*(ptr as *mut T::Instance);
872 let imp: &T = instance.imp();
873
874 gstbool::panic_to_error!(imp, false, {
875 match imp.stop() {
876 Ok(()) => true,
877 Err(err) => {
878 imp.post_error_message(err);
879 false
880 }
881 }
882 })
883 .into_glib()
884}
885
886unsafe extern "C" fn base_transform_transform_caps<T: BaseTransformImpl>(
887 ptr: *mut ffi::GstBaseTransform,
888 direction: gst::ffi::GstPadDirection,
889 caps: *mut gst::ffi::GstCaps,
890 filter: *mut gst::ffi::GstCaps,
891) -> *mut gst::ffi::GstCaps {
892 let instance: &::Instance = &*(ptr as *mut T::Instance);
893 let imp: &T = instance.imp();
894
895 gst::panic_to_error!(imp, None, {
896 let filter: Borrowed<Option<gst::Caps>> = from_glib_borrow(filter);
897
898 imp.transform_caps(
899 from_glib(direction),
900 &from_glib_borrow(caps),
901 filter.as_ref().as_ref(),
902 )
903 })
904 .map(|caps| caps.into_glib_ptr())
905 .unwrap_or(default:std::ptr::null_mut())
906}
907
908unsafe extern "C" fn base_transform_fixate_caps<T: BaseTransformImpl>(
909 ptr: *mut ffi::GstBaseTransform,
910 direction: gst::ffi::GstPadDirection,
911 caps: *mut gst::ffi::GstCaps,
912 othercaps: *mut gst::ffi::GstCaps,
913) -> *mut gst::ffi::GstCaps {
914 let instance: &::Instance = &*(ptr as *mut T::Instance);
915 let imp: &T = instance.imp();
916
917 gstCaps::panic_to_error!(imp, gst::Caps::new_empty(), {
918 imp.fixate_caps(
919 from_glib(direction),
920 &from_glib_borrow(caps),
921 from_glib_full(othercaps),
922 )
923 })
924 .into_glib_ptr()
925}
926
927unsafe extern "C" fn base_transform_set_caps<T: BaseTransformImpl>(
928 ptr: *mut ffi::GstBaseTransform,
929 incaps: *mut gst::ffi::GstCaps,
930 outcaps: *mut gst::ffi::GstCaps,
931) -> glib::ffi::gboolean {
932 let instance: &::Instance = &*(ptr as *mut T::Instance);
933 let imp: &T = instance.imp();
934
935 gstbool::panic_to_error!(imp, false, {
936 match imp.set_caps(&from_glib_borrow(incaps), &from_glib_borrow(outcaps)) {
937 Ok(()) => true,
938 Err(err) => {
939 err.log_with_imp(imp);
940 false
941 }
942 }
943 })
944 .into_glib()
945}
946
947unsafe extern "C" fn base_transform_accept_caps<T: BaseTransformImpl>(
948 ptr: *mut ffi::GstBaseTransform,
949 direction: gst::ffi::GstPadDirection,
950 caps: *mut gst::ffi::GstCaps,
951) -> glib::ffi::gboolean {
952 let instance: &::Instance = &*(ptr as *mut T::Instance);
953 let imp: &T = instance.imp();
954
955 gstbool::panic_to_error!(imp, false, {
956 imp.accept_caps(from_glib(direction), &from_glib_borrow(caps))
957 })
958 .into_glib()
959}
960
961unsafe extern "C" fn base_transform_query<T: BaseTransformImpl>(
962 ptr: *mut ffi::GstBaseTransform,
963 direction: gst::ffi::GstPadDirection,
964 query: *mut gst::ffi::GstQuery,
965) -> glib::ffi::gboolean {
966 let instance: &::Instance = &*(ptr as *mut T::Instance);
967 let imp: &T = instance.imp();
968
969 gstbool::panic_to_error!(imp, false, {
970 BaseTransformImpl::query(
971 imp,
972 from_glib(direction),
973 gst::QueryRef::from_mut_ptr(query),
974 )
975 })
976 .into_glib()
977}
978
979unsafe extern "C" fn base_transform_transform_size<T: BaseTransformImpl>(
980 ptr: *mut ffi::GstBaseTransform,
981 direction: gst::ffi::GstPadDirection,
982 caps: *mut gst::ffi::GstCaps,
983 size: usize,
984 othercaps: *mut gst::ffi::GstCaps,
985 othersize: *mut usize,
986) -> glib::ffi::gboolean {
987 let instance: &::Instance = &*(ptr as *mut T::Instance);
988 let imp: &T = instance.imp();
989
990 gstbool::panic_to_error!(imp, false, {
991 match imp.transform_size(
992 from_glib(direction),
993 &from_glib_borrow(caps),
994 size,
995 &from_glib_borrow(othercaps),
996 ) {
997 Some(s) => {
998 *othersize = s;
999 true
1000 }
1001 None => false,
1002 }
1003 })
1004 .into_glib()
1005}
1006
1007unsafe extern "C" fn base_transform_get_unit_size<T: BaseTransformImpl>(
1008 ptr: *mut ffi::GstBaseTransform,
1009 caps: *mut gst::ffi::GstCaps,
1010 size: *mut usize,
1011) -> glib::ffi::gboolean {
1012 let instance: &::Instance = &*(ptr as *mut T::Instance);
1013 let imp: &T = instance.imp();
1014
1015 gstbool::panic_to_error!(imp, false, {
1016 match imp.unit_size(&from_glib_borrow(caps)) {
1017 Some(s) => {
1018 *size = s;
1019 true
1020 }
1021 None => false,
1022 }
1023 })
1024 .into_glib()
1025}
1026
1027unsafe extern "C" fn base_transform_prepare_output_buffer<T: BaseTransformImpl>(
1028 ptr: *mut ffi::GstBaseTransform,
1029 inbuf: *mut gst::ffi::GstBuffer,
1030 outbuf: *mut gst::ffi::GstBuffer,
1031) -> gst::ffi::GstFlowReturn {
1032 let instance = &*(ptr as *mut T::Instance);
1033 let imp = instance.imp();
1034
1035 // FIXME: Wrong signature in FFI
1036 let outbuf = outbuf as *mut *mut gst::ffi::GstBuffer;
1037 let is_passthrough: bool = from_glib(ffi::gst_base_transform_is_passthrough(ptr));
1038 let is_in_place: bool = from_glib(ffi::gst_base_transform_is_in_place(ptr));
1039 let writable = is_in_place
1040 && !is_passthrough
1041 && gst::ffi::gst_mini_object_is_writable(inbuf as *mut _) != glib::ffi::GFALSE;
1042 let buffer = match writable {
1043 false => InputBuffer::Readable(gst::BufferRef::from_ptr(inbuf)),
1044 true => InputBuffer::Writable(gst::BufferRef::from_mut_ptr(inbuf)),
1045 };
1046
1047 *outbuf = ptr::null_mut();
1048
1049 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1050 match imp.prepare_output_buffer(buffer) {
1051 Ok(PrepareOutputBufferSuccess::InputBuffer) => {
1052 assert!(
1053 is_passthrough || is_in_place,
1054 "Returning InputBuffer only allowed for passthrough or in-place mode"
1055 );
1056 *outbuf = inbuf;
1057 gst::FlowReturn::Ok
1058 }
1059 Ok(PrepareOutputBufferSuccess::Buffer(buf)) => {
1060 assert!(
1061 !is_passthrough,
1062 "Returning Buffer not allowed for passthrough mode"
1063 );
1064 *outbuf = buf.into_glib_ptr();
1065 gst::FlowReturn::Ok
1066 }
1067 Err(err) => err.into(),
1068 }
1069 })
1070 .into_glib()
1071}
1072
1073unsafe extern "C" fn base_transform_sink_event<T: BaseTransformImpl>(
1074 ptr: *mut ffi::GstBaseTransform,
1075 event: *mut gst::ffi::GstEvent,
1076) -> glib::ffi::gboolean {
1077 let instance: &::Instance = &*(ptr as *mut T::Instance);
1078 let imp: &T = instance.imp();
1079
1080 gst::panic_to_error!(imp, false, { imp.sink_event(from_glib_full(event)) }).into_glib()
1081}
1082
1083unsafe extern "C" fn base_transform_src_event<T: BaseTransformImpl>(
1084 ptr: *mut ffi::GstBaseTransform,
1085 event: *mut gst::ffi::GstEvent,
1086) -> glib::ffi::gboolean {
1087 let instance: &::Instance = &*(ptr as *mut T::Instance);
1088 let imp: &T = instance.imp();
1089
1090 gst::panic_to_error!(imp, false, { imp.src_event(from_glib_full(event)) }).into_glib()
1091}
1092
1093unsafe extern "C" fn base_transform_transform<T: BaseTransformImpl>(
1094 ptr: *mut ffi::GstBaseTransform,
1095 inbuf: *mut gst::ffi::GstBuffer,
1096 outbuf: *mut gst::ffi::GstBuffer,
1097) -> gst::ffi::GstFlowReturn {
1098 let instance: &::Instance = &*(ptr as *mut T::Instance);
1099 let imp: &T = instance.imp();
1100
1101 gstFlowReturn::panic_to_error!(imp, gst::FlowReturn::Error, {
1102 imp.transform(
1103 &from_glib_borrow(inbuf),
1104 gst::BufferRef::from_mut_ptr(outbuf),
1105 )
1106 .into()
1107 })
1108 .into_glib()
1109}
1110
1111unsafe extern "C" fn base_transform_transform_ip<T: BaseTransformImpl>(
1112 ptr: *mut ffi::GstBaseTransform,
1113 buf: *mut *mut gst::ffi::GstBuffer,
1114) -> gst::ffi::GstFlowReturn {
1115 let instance: &::Instance = &*(ptr as *mut T::Instance);
1116 let imp: &T = instance.imp();
1117
1118 // FIXME: Wrong signature in FFI
1119 let buf: *mut GstBuffer = buf as *mut gst::ffi::GstBuffer;
1120
1121 gstFlowReturn::panic_to_error!(imp, gst::FlowReturn::Error, {
1122 if from_glib(ffi::gst_base_transform_is_passthrough(ptr)) {
1123 imp.transform_ip_passthrough(&from_glib_borrow(buf)).into()
1124 } else {
1125 imp.transform_ip(gst::BufferRef::from_mut_ptr(buf)).into()
1126 }
1127 })
1128 .into_glib()
1129}
1130
1131unsafe extern "C" fn base_transform_transform_meta<T: BaseTransformImpl>(
1132 ptr: *mut ffi::GstBaseTransform,
1133 outbuf: *mut gst::ffi::GstBuffer,
1134 meta: *mut gst::ffi::GstMeta,
1135 inbuf: *mut gst::ffi::GstBuffer,
1136) -> glib::ffi::gboolean {
1137 let instance: &::Instance = &*(ptr as *mut T::Instance);
1138 let imp: &T = instance.imp();
1139
1140 let inbuf: &BufferRef = gst::BufferRef::from_ptr(inbuf);
1141
1142 gstbool::panic_to_error!(imp, false, {
1143 imp.transform_meta(
1144 gst::BufferRef::from_mut_ptr(outbuf),
1145 gst::Meta::from_ptr(inbuf, meta),
1146 inbuf,
1147 )
1148 })
1149 .into_glib()
1150}
1151
1152unsafe extern "C" fn base_transform_propose_allocation<T: BaseTransformImpl>(
1153 ptr: *mut ffi::GstBaseTransform,
1154 decide_query: *mut gst::ffi::GstQuery,
1155 query: *mut gst::ffi::GstQuery,
1156) -> glib::ffi::gboolean {
1157 let instance = &*(ptr as *mut T::Instance);
1158 let imp = instance.imp();
1159 let decide_query = if decide_query.is_null() {
1160 None
1161 } else {
1162 match gst::QueryRef::from_ptr(decide_query).view() {
1163 gst::QueryView::Allocation(allocation) => Some(allocation),
1164 _ => unreachable!(),
1165 }
1166 };
1167 let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1168 gst::QueryViewMut::Allocation(allocation) => allocation,
1169 _ => unreachable!(),
1170 };
1171
1172 gst::panic_to_error!(imp, false, {
1173 match imp.propose_allocation(decide_query, query) {
1174 Ok(()) => true,
1175 Err(err) => {
1176 err.log_with_imp(imp);
1177 false
1178 }
1179 }
1180 })
1181 .into_glib()
1182}
1183
1184unsafe extern "C" fn base_transform_decide_allocation<T: BaseTransformImpl>(
1185 ptr: *mut ffi::GstBaseTransform,
1186 query: *mut gst::ffi::GstQuery,
1187) -> glib::ffi::gboolean {
1188 let instance: &::Instance = &*(ptr as *mut T::Instance);
1189 let imp: &T = instance.imp();
1190 let query: &mut Allocation = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1191 gst::QueryViewMut::Allocation(allocation: &mut Allocation) => allocation,
1192 _ => unreachable!(),
1193 };
1194
1195 gstbool::panic_to_error!(imp, false, {
1196 match imp.decide_allocation(query) {
1197 Ok(()) => true,
1198 Err(err) => {
1199 err.log_with_imp(imp);
1200 false
1201 }
1202 }
1203 })
1204 .into_glib()
1205}
1206
1207unsafe extern "C" fn base_transform_copy_metadata<T: BaseTransformImpl>(
1208 ptr: *mut ffi::GstBaseTransform,
1209 inbuf: *mut gst::ffi::GstBuffer,
1210 outbuf: *mut gst::ffi::GstBuffer,
1211) -> glib::ffi::gboolean {
1212 let instance = &*(ptr as *mut T::Instance);
1213 let imp = instance.imp();
1214
1215 if gst::ffi::gst_mini_object_is_writable(outbuf as *mut _) == glib::ffi::GFALSE {
1216 let instance = imp.obj();
1217 let obj = instance.unsafe_cast_ref::<BaseTransform>();
1218 gst::warning!(gst::CAT_RUST, obj: obj, "buffer {:?} not writable", outbuf);
1219 return glib::ffi::GFALSE;
1220 }
1221
1222 gst::panic_to_error!(imp, true, {
1223 match imp.copy_metadata(
1224 gst::BufferRef::from_ptr(inbuf),
1225 gst::BufferRef::from_mut_ptr(outbuf),
1226 ) {
1227 Ok(_) => true,
1228 Err(err) => {
1229 err.log_with_imp(imp);
1230 false
1231 }
1232 }
1233 })
1234 .into_glib()
1235}
1236
1237unsafe extern "C" fn base_transform_before_transform<T: BaseTransformImpl>(
1238 ptr: *mut ffi::GstBaseTransform,
1239 inbuf: *mut gst::ffi::GstBuffer,
1240) {
1241 let instance: &::Instance = &*(ptr as *mut T::Instance);
1242 let imp: &T = instance.imp();
1243
1244 gst::panic_to_error!(imp, (), {
1245 imp.before_transform(gst::BufferRef::from_ptr(inbuf));
1246 })
1247}
1248
1249unsafe extern "C" fn base_transform_submit_input_buffer<T: BaseTransformImpl>(
1250 ptr: *mut ffi::GstBaseTransform,
1251 is_discont: glib::ffi::gboolean,
1252 buf: *mut gst::ffi::GstBuffer,
1253) -> gst::ffi::GstFlowReturn {
1254 let instance: &::Instance = &*(ptr as *mut T::Instance);
1255 let imp: &T = instance.imp();
1256
1257 gstFlowReturn::panic_to_error!(imp, gst::FlowReturn::Error, {
1258 imp.submit_input_buffer(from_glib(is_discont), from_glib_full(buf))
1259 .into()
1260 })
1261 .into_glib()
1262}
1263
1264unsafe extern "C" fn base_transform_generate_output<T: BaseTransformImpl>(
1265 ptr: *mut ffi::GstBaseTransform,
1266 buf: *mut *mut gst::ffi::GstBuffer,
1267) -> gst::ffi::GstFlowReturn {
1268 let instance: &::Instance = &*(ptr as *mut T::Instance);
1269 let imp: &T = instance.imp();
1270
1271 *buf = ptr::null_mut();
1272
1273 gstFlowReturn::panic_to_error!(imp, gst::FlowReturn::Error, {
1274 match imp.generate_output() {
1275 Ok(GenerateOutputSuccess::Dropped) => crate::BASE_TRANSFORM_FLOW_DROPPED.into(),
1276 Ok(GenerateOutputSuccess::NoOutput) => gst::FlowReturn::Ok,
1277 Ok(GenerateOutputSuccess::Buffer(outbuf)) => {
1278 *buf = outbuf.into_glib_ptr();
1279 gst::FlowReturn::Ok
1280 }
1281 Err(err) => err.into(),
1282 }
1283 })
1284 .into_glib()
1285}
1286
1287#[cfg(test)]
1288mod tests {
1289 use super::*;
1290
1291 pub mod imp {
1292 use super::*;
1293 use std::sync::atomic::{self, AtomicBool};
1294
1295 #[derive(Default)]
1296 pub struct TestTransform {
1297 drop_next: AtomicBool,
1298 }
1299
1300 #[glib::object_subclass]
1301 impl ObjectSubclass for TestTransform {
1302 const NAME: &'static str = "TestTransform";
1303 type Type = super::TestTransform;
1304 type ParentType = crate::BaseTransform;
1305 }
1306
1307 impl ObjectImpl for TestTransform {}
1308
1309 impl GstObjectImpl for TestTransform {}
1310
1311 impl ElementImpl for TestTransform {
1312 fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
1313 use glib::once_cell::sync::Lazy;
1314 static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
1315 gst::subclass::ElementMetadata::new(
1316 "Test Transform",
1317 "Generic",
1318 "Does nothing",
1319 "Sebastian Dröge <sebastian@centricular.com>",
1320 )
1321 });
1322
1323 Some(&*ELEMENT_METADATA)
1324 }
1325
1326 fn pad_templates() -> &'static [gst::PadTemplate] {
1327 use glib::once_cell::sync::Lazy;
1328 static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
1329 let caps = gst::Caps::new_any();
1330 vec![
1331 gst::PadTemplate::new(
1332 "src",
1333 gst::PadDirection::Src,
1334 gst::PadPresence::Always,
1335 &caps,
1336 )
1337 .unwrap(),
1338 gst::PadTemplate::new(
1339 "sink",
1340 gst::PadDirection::Sink,
1341 gst::PadPresence::Always,
1342 &caps,
1343 )
1344 .unwrap(),
1345 ]
1346 });
1347
1348 PAD_TEMPLATES.as_ref()
1349 }
1350 }
1351
1352 impl BaseTransformImpl for TestTransform {
1353 const MODE: BaseTransformMode = BaseTransformMode::AlwaysInPlace;
1354
1355 const PASSTHROUGH_ON_SAME_CAPS: bool = false;
1356
1357 const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
1358
1359 fn transform_ip(
1360 &self,
1361 _buf: &mut gst::BufferRef,
1362 ) -> Result<gst::FlowSuccess, gst::FlowError> {
1363 if self.drop_next.load(atomic::Ordering::SeqCst) {
1364 self.drop_next.store(false, atomic::Ordering::SeqCst);
1365 Ok(crate::BASE_TRANSFORM_FLOW_DROPPED)
1366 } else {
1367 self.drop_next.store(true, atomic::Ordering::SeqCst);
1368 Ok(gst::FlowSuccess::Ok)
1369 }
1370 }
1371 }
1372 }
1373
1374 glib::wrapper! {
1375 pub struct TestTransform(ObjectSubclass<imp::TestTransform>) @extends crate::BaseTransform, gst::Element, gst::Object;
1376 }
1377
1378 impl TestTransform {
1379 pub fn new(name: Option<&str>) -> Self {
1380 glib::Object::builder().property("name", name).build()
1381 }
1382 }
1383
1384 #[test]
1385 fn test_transform_subclass() {
1386 gst::init().unwrap();
1387
1388 let element = TestTransform::new(Some("test"));
1389
1390 assert_eq!(element.name(), "test");
1391
1392 let pipeline = gst::Pipeline::new();
1393 let src = gst::ElementFactory::make("audiotestsrc")
1394 .property("num-buffers", 100i32)
1395 .build()
1396 .unwrap();
1397 let sink = gst::ElementFactory::make("fakesink").build().unwrap();
1398
1399 pipeline
1400 .add_many([&src, element.upcast_ref(), &sink])
1401 .unwrap();
1402 gst::Element::link_many([&src, element.upcast_ref(), &sink]).unwrap();
1403
1404 pipeline.set_state(gst::State::Playing).unwrap();
1405 let bus = pipeline.bus().unwrap();
1406
1407 let eos = bus.timed_pop_filtered(gst::ClockTime::NONE, &[gst::MessageType::Eos]);
1408 assert!(eos.is_some());
1409
1410 let stats = sink.property::<gst::Structure>("stats");
1411 assert_eq!(stats.get::<u64>("rendered").unwrap(), 50);
1412
1413 pipeline.set_state(gst::State::Null).unwrap();
1414 }
1415}
1416