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: 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 = &*(ptr as *mut T::Instance);
854    let imp = instance.imp();
855
856    gst::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 = &*(ptr as *mut T::Instance);
872    let imp = instance.imp();
873
874    gst::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 = &*(ptr as *mut T::Instance);
893    let imp = 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(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 = &*(ptr as *mut T::Instance);
915    let imp = instance.imp();
916
917    gst::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 = &*(ptr as *mut T::Instance);
933    let imp = instance.imp();
934
935    gst::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 = &*(ptr as *mut T::Instance);
953    let imp = instance.imp();
954
955    gst::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 = &*(ptr as *mut T::Instance);
967    let imp = instance.imp();
968
969    gst::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 = &*(ptr as *mut T::Instance);
988    let imp = instance.imp();
989
990    gst::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 = &*(ptr as *mut T::Instance);
1013    let imp = instance.imp();
1014
1015    gst::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 = &*(ptr as *mut T::Instance);
1078    let imp = 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 = &*(ptr as *mut T::Instance);
1088    let imp = 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 = &*(ptr as *mut T::Instance);
1099    let imp = instance.imp();
1100
1101    gst::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 = &*(ptr as *mut T::Instance);
1116    let imp = instance.imp();
1117
1118    // FIXME: Wrong signature in FFI
1119    let buf = buf as *mut gst::ffi::GstBuffer;
1120
1121    gst::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 = &*(ptr as *mut T::Instance);
1138    let imp = instance.imp();
1139
1140    let inbuf = gst::BufferRef::from_ptr(inbuf);
1141
1142    gst::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_and_level(imp, gst::DebugLevel::Info);
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 = &*(ptr as *mut T::Instance);
1189    let imp = instance.imp();
1190    let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1191        gst::QueryViewMut::Allocation(allocation) => allocation,
1192        _ => unreachable!(),
1193    };
1194
1195    gst::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 = &*(ptr as *mut T::Instance);
1242    let imp = 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 = &*(ptr as *mut T::Instance);
1255    let imp = instance.imp();
1256
1257    gst::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 = &*(ptr as *mut T::Instance);
1269    let imp = instance.imp();
1270
1271    *buf = ptr::null_mut();
1272
1273    gst::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                static ELEMENT_METADATA: std::sync::OnceLock<gst::subclass::ElementMetadata> =
1314                    std::sync::OnceLock::new();
1315
1316                Some(ELEMENT_METADATA.get_or_init(|| {
1317                    gst::subclass::ElementMetadata::new(
1318                        "Test Transform",
1319                        "Generic",
1320                        "Does nothing",
1321                        "Sebastian Dröge <[email protected]>",
1322                    )
1323                }))
1324            }
1325
1326            fn pad_templates() -> &'static [gst::PadTemplate] {
1327                static PAD_TEMPLATES: std::sync::OnceLock<Vec<gst::PadTemplate>> =
1328                    std::sync::OnceLock::new();
1329
1330                PAD_TEMPLATES.get_or_init(|| {
1331                    let caps = gst::Caps::new_any();
1332                    vec![
1333                        gst::PadTemplate::new(
1334                            "src",
1335                            gst::PadDirection::Src,
1336                            gst::PadPresence::Always,
1337                            &caps,
1338                        )
1339                        .unwrap(),
1340                        gst::PadTemplate::new(
1341                            "sink",
1342                            gst::PadDirection::Sink,
1343                            gst::PadPresence::Always,
1344                            &caps,
1345                        )
1346                        .unwrap(),
1347                    ]
1348                })
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}