gstreamer_base/subclass/
base_transform.rs

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