Skip to main content

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::{BaseTransform, ffi, prelude::*};
9
10#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11pub enum BaseTransformMode {
12    AlwaysInPlace,
13    NeverInPlace,
14    Both,
15}
16
17pub trait BaseTransformImpl: ElementImpl + ObjectSubclass<Type: IsA<BaseTransform>> {
18    const MODE: BaseTransformMode;
19    const PASSTHROUGH_ON_SAME_CAPS: bool;
20    const TRANSFORM_IP_ON_PASSTHROUGH: bool;
21
22    fn start(&self) -> Result<(), gst::ErrorMessage> {
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    unsafe {
843        let instance = &*(ptr as *mut T::Instance);
844        let imp = instance.imp();
845
846        gst::panic_to_error!(imp, false, {
847            match imp.start() {
848                Ok(()) => true,
849                Err(err) => {
850                    imp.post_error_message(err);
851                    false
852                }
853            }
854        })
855        .into_glib()
856    }
857}
858
859unsafe extern "C" fn base_transform_stop<T: BaseTransformImpl>(
860    ptr: *mut ffi::GstBaseTransform,
861) -> glib::ffi::gboolean {
862    unsafe {
863        let instance = &*(ptr as *mut T::Instance);
864        let imp = instance.imp();
865
866        gst::panic_to_error!(imp, false, {
867            match imp.stop() {
868                Ok(()) => true,
869                Err(err) => {
870                    imp.post_error_message(err);
871                    false
872                }
873            }
874        })
875        .into_glib()
876    }
877}
878
879unsafe extern "C" fn base_transform_transform_caps<T: BaseTransformImpl>(
880    ptr: *mut ffi::GstBaseTransform,
881    direction: gst::ffi::GstPadDirection,
882    caps: *mut gst::ffi::GstCaps,
883    filter: *mut gst::ffi::GstCaps,
884) -> *mut gst::ffi::GstCaps {
885    unsafe {
886        let instance = &*(ptr as *mut T::Instance);
887        let imp = instance.imp();
888
889        gst::panic_to_error!(imp, None, {
890            let filter: Borrowed<Option<gst::Caps>> = from_glib_borrow(filter);
891
892            imp.transform_caps(
893                from_glib(direction),
894                &from_glib_borrow(caps),
895                filter.as_ref().as_ref(),
896            )
897        })
898        .map(|caps| caps.into_glib_ptr())
899        .unwrap_or(std::ptr::null_mut())
900    }
901}
902
903unsafe extern "C" fn base_transform_fixate_caps<T: BaseTransformImpl>(
904    ptr: *mut ffi::GstBaseTransform,
905    direction: gst::ffi::GstPadDirection,
906    caps: *mut gst::ffi::GstCaps,
907    othercaps: *mut gst::ffi::GstCaps,
908) -> *mut gst::ffi::GstCaps {
909    unsafe {
910        let instance = &*(ptr as *mut T::Instance);
911        let imp = instance.imp();
912
913        gst::panic_to_error!(imp, gst::Caps::new_empty(), {
914            imp.fixate_caps(
915                from_glib(direction),
916                &from_glib_borrow(caps),
917                from_glib_full(othercaps),
918            )
919        })
920        .into_glib_ptr()
921    }
922}
923
924unsafe extern "C" fn base_transform_set_caps<T: BaseTransformImpl>(
925    ptr: *mut ffi::GstBaseTransform,
926    incaps: *mut gst::ffi::GstCaps,
927    outcaps: *mut gst::ffi::GstCaps,
928) -> glib::ffi::gboolean {
929    unsafe {
930        let instance = &*(ptr as *mut T::Instance);
931        let imp = instance.imp();
932
933        gst::panic_to_error!(imp, false, {
934            match imp.set_caps(&from_glib_borrow(incaps), &from_glib_borrow(outcaps)) {
935                Ok(()) => true,
936                Err(err) => {
937                    err.log_with_imp(imp);
938                    false
939                }
940            }
941        })
942        .into_glib()
943    }
944}
945
946unsafe extern "C" fn base_transform_accept_caps<T: BaseTransformImpl>(
947    ptr: *mut ffi::GstBaseTransform,
948    direction: gst::ffi::GstPadDirection,
949    caps: *mut gst::ffi::GstCaps,
950) -> glib::ffi::gboolean {
951    unsafe {
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}
961
962unsafe extern "C" fn base_transform_query<T: BaseTransformImpl>(
963    ptr: *mut ffi::GstBaseTransform,
964    direction: gst::ffi::GstPadDirection,
965    query: *mut gst::ffi::GstQuery,
966) -> glib::ffi::gboolean {
967    unsafe {
968        let instance = &*(ptr as *mut T::Instance);
969        let imp = instance.imp();
970
971        gst::panic_to_error!(imp, false, {
972            BaseTransformImpl::query(
973                imp,
974                from_glib(direction),
975                gst::QueryRef::from_mut_ptr(query),
976            )
977        })
978        .into_glib()
979    }
980}
981
982unsafe extern "C" fn base_transform_transform_size<T: BaseTransformImpl>(
983    ptr: *mut ffi::GstBaseTransform,
984    direction: gst::ffi::GstPadDirection,
985    caps: *mut gst::ffi::GstCaps,
986    size: usize,
987    othercaps: *mut gst::ffi::GstCaps,
988    othersize: *mut usize,
989) -> glib::ffi::gboolean {
990    unsafe {
991        let instance = &*(ptr as *mut T::Instance);
992        let imp = instance.imp();
993
994        gst::panic_to_error!(imp, false, {
995            match imp.transform_size(
996                from_glib(direction),
997                &from_glib_borrow(caps),
998                size,
999                &from_glib_borrow(othercaps),
1000            ) {
1001                Some(s) => {
1002                    *othersize = s;
1003                    true
1004                }
1005                None => false,
1006            }
1007        })
1008        .into_glib()
1009    }
1010}
1011
1012unsafe extern "C" fn base_transform_get_unit_size<T: BaseTransformImpl>(
1013    ptr: *mut ffi::GstBaseTransform,
1014    caps: *mut gst::ffi::GstCaps,
1015    size: *mut usize,
1016) -> glib::ffi::gboolean {
1017    unsafe {
1018        let instance = &*(ptr as *mut T::Instance);
1019        let imp = instance.imp();
1020
1021        gst::panic_to_error!(imp, false, {
1022            match imp.unit_size(&from_glib_borrow(caps)) {
1023                Some(s) => {
1024                    *size = s;
1025                    true
1026                }
1027                None => false,
1028            }
1029        })
1030        .into_glib()
1031    }
1032}
1033
1034unsafe extern "C" fn base_transform_prepare_output_buffer<T: BaseTransformImpl>(
1035    ptr: *mut ffi::GstBaseTransform,
1036    inbuf: *mut gst::ffi::GstBuffer,
1037    outbuf: *mut gst::ffi::GstBuffer,
1038) -> gst::ffi::GstFlowReturn {
1039    unsafe {
1040        let instance = &*(ptr as *mut T::Instance);
1041        let imp = instance.imp();
1042
1043        // FIXME: Wrong signature in FFI
1044        let outbuf = outbuf as *mut *mut gst::ffi::GstBuffer;
1045        let is_passthrough: bool = from_glib(ffi::gst_base_transform_is_passthrough(ptr));
1046        let is_in_place: bool = from_glib(ffi::gst_base_transform_is_in_place(ptr));
1047        let writable = is_in_place
1048            && !is_passthrough
1049            && gst::ffi::gst_mini_object_is_writable(inbuf as *mut _) != glib::ffi::GFALSE;
1050        let buffer = match writable {
1051            false => InputBuffer::Readable(gst::BufferRef::from_ptr(inbuf)),
1052            true => InputBuffer::Writable(gst::BufferRef::from_mut_ptr(inbuf)),
1053        };
1054
1055        *outbuf = ptr::null_mut();
1056
1057        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1058            match imp.prepare_output_buffer(buffer) {
1059                Ok(PrepareOutputBufferSuccess::InputBuffer) => {
1060                    assert!(
1061                        is_passthrough || is_in_place,
1062                        "Returning InputBuffer only allowed for passthrough or in-place mode"
1063                    );
1064                    *outbuf = inbuf;
1065                    gst::FlowReturn::Ok
1066                }
1067                Ok(PrepareOutputBufferSuccess::Buffer(buf)) => {
1068                    assert!(
1069                        !is_passthrough,
1070                        "Returning Buffer not allowed for passthrough mode"
1071                    );
1072                    *outbuf = buf.into_glib_ptr();
1073                    gst::FlowReturn::Ok
1074                }
1075                Err(err) => err.into(),
1076            }
1077        })
1078        .into_glib()
1079    }
1080}
1081
1082unsafe extern "C" fn base_transform_sink_event<T: BaseTransformImpl>(
1083    ptr: *mut ffi::GstBaseTransform,
1084    event: *mut gst::ffi::GstEvent,
1085) -> glib::ffi::gboolean {
1086    unsafe {
1087        let instance = &*(ptr as *mut T::Instance);
1088        let imp = instance.imp();
1089
1090        gst::panic_to_error!(imp, false, { imp.sink_event(from_glib_full(event)) }).into_glib()
1091    }
1092}
1093
1094unsafe extern "C" fn base_transform_src_event<T: BaseTransformImpl>(
1095    ptr: *mut ffi::GstBaseTransform,
1096    event: *mut gst::ffi::GstEvent,
1097) -> glib::ffi::gboolean {
1098    unsafe {
1099        let instance = &*(ptr as *mut T::Instance);
1100        let imp = instance.imp();
1101
1102        gst::panic_to_error!(imp, false, { imp.src_event(from_glib_full(event)) }).into_glib()
1103    }
1104}
1105
1106unsafe extern "C" fn base_transform_transform<T: BaseTransformImpl>(
1107    ptr: *mut ffi::GstBaseTransform,
1108    inbuf: *mut gst::ffi::GstBuffer,
1109    outbuf: *mut gst::ffi::GstBuffer,
1110) -> gst::ffi::GstFlowReturn {
1111    unsafe {
1112        let instance = &*(ptr as *mut T::Instance);
1113        let imp = instance.imp();
1114
1115        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1116            imp.transform(
1117                &from_glib_borrow(inbuf),
1118                gst::BufferRef::from_mut_ptr(outbuf),
1119            )
1120            .into()
1121        })
1122        .into_glib()
1123    }
1124}
1125
1126unsafe extern "C" fn base_transform_transform_ip<T: BaseTransformImpl>(
1127    ptr: *mut ffi::GstBaseTransform,
1128    buf: *mut *mut gst::ffi::GstBuffer,
1129) -> gst::ffi::GstFlowReturn {
1130    unsafe {
1131        let instance = &*(ptr as *mut T::Instance);
1132        let imp = instance.imp();
1133
1134        // FIXME: Wrong signature in FFI
1135        let buf = buf as *mut gst::ffi::GstBuffer;
1136
1137        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1138            if from_glib(ffi::gst_base_transform_is_passthrough(ptr)) {
1139                imp.transform_ip_passthrough(&from_glib_borrow(buf)).into()
1140            } else {
1141                imp.transform_ip(gst::BufferRef::from_mut_ptr(buf)).into()
1142            }
1143        })
1144        .into_glib()
1145    }
1146}
1147
1148unsafe extern "C" fn base_transform_transform_meta<T: BaseTransformImpl>(
1149    ptr: *mut ffi::GstBaseTransform,
1150    outbuf: *mut gst::ffi::GstBuffer,
1151    meta: *mut gst::ffi::GstMeta,
1152    inbuf: *mut gst::ffi::GstBuffer,
1153) -> glib::ffi::gboolean {
1154    unsafe {
1155        let instance = &*(ptr as *mut T::Instance);
1156        let imp = instance.imp();
1157
1158        let inbuf = gst::BufferRef::from_ptr(inbuf);
1159
1160        gst::panic_to_error!(imp, false, {
1161            imp.transform_meta(
1162                gst::BufferRef::from_mut_ptr(outbuf),
1163                gst::Meta::from_ptr(inbuf, meta),
1164                inbuf,
1165            )
1166        })
1167        .into_glib()
1168    }
1169}
1170
1171unsafe extern "C" fn base_transform_propose_allocation<T: BaseTransformImpl>(
1172    ptr: *mut ffi::GstBaseTransform,
1173    decide_query: *mut gst::ffi::GstQuery,
1174    query: *mut gst::ffi::GstQuery,
1175) -> glib::ffi::gboolean {
1176    unsafe {
1177        let instance = &*(ptr as *mut T::Instance);
1178        let imp = instance.imp();
1179        let decide_query = if decide_query.is_null() {
1180            None
1181        } else {
1182            match gst::QueryRef::from_ptr(decide_query).view() {
1183                gst::QueryView::Allocation(allocation) => Some(allocation),
1184                _ => unreachable!(),
1185            }
1186        };
1187        let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1188            gst::QueryViewMut::Allocation(allocation) => allocation,
1189            _ => unreachable!(),
1190        };
1191
1192        gst::panic_to_error!(imp, false, {
1193            match imp.propose_allocation(decide_query, query) {
1194                Ok(()) => true,
1195                Err(err) => {
1196                    err.log_with_imp_and_level(imp, gst::DebugLevel::Info);
1197                    false
1198                }
1199            }
1200        })
1201        .into_glib()
1202    }
1203}
1204
1205unsafe extern "C" fn base_transform_decide_allocation<T: BaseTransformImpl>(
1206    ptr: *mut ffi::GstBaseTransform,
1207    query: *mut gst::ffi::GstQuery,
1208) -> glib::ffi::gboolean {
1209    unsafe {
1210        let instance = &*(ptr as *mut T::Instance);
1211        let imp = instance.imp();
1212        let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1213            gst::QueryViewMut::Allocation(allocation) => allocation,
1214            _ => unreachable!(),
1215        };
1216
1217        gst::panic_to_error!(imp, false, {
1218            match imp.decide_allocation(query) {
1219                Ok(()) => true,
1220                Err(err) => {
1221                    err.log_with_imp(imp);
1222                    false
1223                }
1224            }
1225        })
1226        .into_glib()
1227    }
1228}
1229
1230unsafe extern "C" fn base_transform_copy_metadata<T: BaseTransformImpl>(
1231    ptr: *mut ffi::GstBaseTransform,
1232    inbuf: *mut gst::ffi::GstBuffer,
1233    outbuf: *mut gst::ffi::GstBuffer,
1234) -> glib::ffi::gboolean {
1235    unsafe {
1236        let instance = &*(ptr as *mut T::Instance);
1237        let imp = instance.imp();
1238
1239        if gst::ffi::gst_mini_object_is_writable(outbuf as *mut _) == glib::ffi::GFALSE {
1240            let instance = imp.obj();
1241            let obj = instance.unsafe_cast_ref::<BaseTransform>();
1242            gst::warning!(gst::CAT_RUST, obj = obj, "buffer {:?} not writable", outbuf);
1243            return glib::ffi::GFALSE;
1244        }
1245
1246        gst::panic_to_error!(imp, true, {
1247            match imp.copy_metadata(
1248                gst::BufferRef::from_ptr(inbuf),
1249                gst::BufferRef::from_mut_ptr(outbuf),
1250            ) {
1251                Ok(_) => true,
1252                Err(err) => {
1253                    err.log_with_imp(imp);
1254                    false
1255                }
1256            }
1257        })
1258        .into_glib()
1259    }
1260}
1261
1262unsafe extern "C" fn base_transform_before_transform<T: BaseTransformImpl>(
1263    ptr: *mut ffi::GstBaseTransform,
1264    inbuf: *mut gst::ffi::GstBuffer,
1265) {
1266    unsafe {
1267        let instance = &*(ptr as *mut T::Instance);
1268        let imp = instance.imp();
1269
1270        gst::panic_to_error!(imp, (), {
1271            imp.before_transform(gst::BufferRef::from_ptr(inbuf));
1272        })
1273    }
1274}
1275
1276unsafe extern "C" fn base_transform_submit_input_buffer<T: BaseTransformImpl>(
1277    ptr: *mut ffi::GstBaseTransform,
1278    is_discont: glib::ffi::gboolean,
1279    buf: *mut gst::ffi::GstBuffer,
1280) -> gst::ffi::GstFlowReturn {
1281    unsafe {
1282        let instance = &*(ptr as *mut T::Instance);
1283        let imp = instance.imp();
1284
1285        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1286            imp.submit_input_buffer(from_glib(is_discont), from_glib_full(buf))
1287                .into()
1288        })
1289        .into_glib()
1290    }
1291}
1292
1293unsafe extern "C" fn base_transform_generate_output<T: BaseTransformImpl>(
1294    ptr: *mut ffi::GstBaseTransform,
1295    buf: *mut *mut gst::ffi::GstBuffer,
1296) -> gst::ffi::GstFlowReturn {
1297    unsafe {
1298        let instance = &*(ptr as *mut T::Instance);
1299        let imp = instance.imp();
1300
1301        *buf = ptr::null_mut();
1302
1303        gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1304            match imp.generate_output() {
1305                Ok(GenerateOutputSuccess::Dropped) => crate::BASE_TRANSFORM_FLOW_DROPPED.into(),
1306                Ok(GenerateOutputSuccess::NoOutput) => gst::FlowReturn::Ok,
1307                Ok(GenerateOutputSuccess::Buffer(outbuf)) => {
1308                    *buf = outbuf.into_glib_ptr();
1309                    gst::FlowReturn::Ok
1310                }
1311                Err(err) => err.into(),
1312            }
1313        })
1314        .into_glib()
1315    }
1316}
1317
1318#[cfg(test)]
1319mod tests {
1320    use super::*;
1321
1322    pub mod imp {
1323        use super::*;
1324        use std::sync::atomic::{self, AtomicBool};
1325
1326        #[derive(Default)]
1327        pub struct TestTransform {
1328            drop_next: AtomicBool,
1329        }
1330
1331        #[glib::object_subclass]
1332        impl ObjectSubclass for TestTransform {
1333            const NAME: &'static str = "TestTransform";
1334            type Type = super::TestTransform;
1335            type ParentType = crate::BaseTransform;
1336        }
1337
1338        impl ObjectImpl for TestTransform {}
1339
1340        impl GstObjectImpl for TestTransform {}
1341
1342        impl ElementImpl for TestTransform {
1343            fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
1344                static ELEMENT_METADATA: std::sync::OnceLock<gst::subclass::ElementMetadata> =
1345                    std::sync::OnceLock::new();
1346
1347                Some(ELEMENT_METADATA.get_or_init(|| {
1348                    gst::subclass::ElementMetadata::new(
1349                        "Test Transform",
1350                        "Generic",
1351                        "Does nothing",
1352                        "Sebastian Dröge <sebastian@centricular.com>",
1353                    )
1354                }))
1355            }
1356
1357            fn pad_templates() -> &'static [gst::PadTemplate] {
1358                static PAD_TEMPLATES: std::sync::OnceLock<Vec<gst::PadTemplate>> =
1359                    std::sync::OnceLock::new();
1360
1361                PAD_TEMPLATES.get_or_init(|| {
1362                    let caps = gst::Caps::new_any();
1363                    vec![
1364                        gst::PadTemplate::new(
1365                            "src",
1366                            gst::PadDirection::Src,
1367                            gst::PadPresence::Always,
1368                            &caps,
1369                        )
1370                        .unwrap(),
1371                        gst::PadTemplate::new(
1372                            "sink",
1373                            gst::PadDirection::Sink,
1374                            gst::PadPresence::Always,
1375                            &caps,
1376                        )
1377                        .unwrap(),
1378                    ]
1379                })
1380            }
1381        }
1382
1383        impl BaseTransformImpl for TestTransform {
1384            const MODE: BaseTransformMode = BaseTransformMode::AlwaysInPlace;
1385
1386            const PASSTHROUGH_ON_SAME_CAPS: bool = false;
1387
1388            const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
1389
1390            fn transform_ip(
1391                &self,
1392                _buf: &mut gst::BufferRef,
1393            ) -> Result<gst::FlowSuccess, gst::FlowError> {
1394                if self.drop_next.load(atomic::Ordering::SeqCst) {
1395                    self.drop_next.store(false, atomic::Ordering::SeqCst);
1396                    Ok(crate::BASE_TRANSFORM_FLOW_DROPPED)
1397                } else {
1398                    self.drop_next.store(true, atomic::Ordering::SeqCst);
1399                    Ok(gst::FlowSuccess::Ok)
1400                }
1401            }
1402        }
1403    }
1404
1405    glib::wrapper! {
1406        pub struct TestTransform(ObjectSubclass<imp::TestTransform>) @extends crate::BaseTransform, gst::Element, gst::Object;
1407    }
1408
1409    impl TestTransform {
1410        pub fn new(name: Option<&str>) -> Self {
1411            glib::Object::builder().property("name", name).build()
1412        }
1413    }
1414
1415    #[test]
1416    fn test_transform_subclass() {
1417        gst::init().unwrap();
1418
1419        let element = TestTransform::new(Some("test"));
1420
1421        assert_eq!(element.name(), "test");
1422
1423        let pipeline = gst::Pipeline::new();
1424        let src = gst::ElementFactory::make("audiotestsrc")
1425            .property("num-buffers", 100i32)
1426            .build()
1427            .unwrap();
1428        let sink = gst::ElementFactory::make("fakesink").build().unwrap();
1429
1430        pipeline
1431            .add_many([&src, element.upcast_ref(), &sink])
1432            .unwrap();
1433        gst::Element::link_many([&src, element.upcast_ref(), &sink]).unwrap();
1434
1435        pipeline.set_state(gst::State::Playing).unwrap();
1436        let bus = pipeline.bus().unwrap();
1437
1438        let eos = bus.timed_pop_filtered(gst::ClockTime::NONE, &[gst::MessageType::Eos]);
1439        assert!(eos.is_some());
1440
1441        let stats = sink.property::<gst::Structure>("stats");
1442        assert_eq!(stats.get::<u64>("rendered").unwrap(), 50);
1443
1444        pipeline.set_state(gst::State::Null).unwrap();
1445    }
1446}