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