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