gstreamer_base/subclass/
base_transform.rs

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