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