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