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