Skip to main content

gstreamer_base/subclass/
base_transform.rs

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