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