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::element_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::element_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::element_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::element_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::element_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::element_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::element_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::element_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::element_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::element_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::element_panic_to_error!(imp, false, { imp.sink_event(from_glib_full(event)) })
1195            .into_glib()
1196    }
1197}
1198
1199unsafe extern "C" fn base_transform_src_event<T: BaseTransformImpl>(
1200    ptr: *mut ffi::GstBaseTransform,
1201    event: *mut gst::ffi::GstEvent,
1202) -> glib::ffi::gboolean {
1203    unsafe {
1204        let instance = &*(ptr as *mut T::Instance);
1205        let imp = instance.imp();
1206
1207        gst::element_panic_to_error!(imp, false, { imp.src_event(from_glib_full(event)) })
1208            .into_glib()
1209    }
1210}
1211
1212unsafe extern "C" fn base_transform_transform<T: BaseTransformImpl>(
1213    ptr: *mut ffi::GstBaseTransform,
1214    inbuf: *mut gst::ffi::GstBuffer,
1215    outbuf: *mut gst::ffi::GstBuffer,
1216) -> gst::ffi::GstFlowReturn {
1217    unsafe {
1218        let instance = &*(ptr as *mut T::Instance);
1219        let imp = instance.imp();
1220
1221        gst::element_panic_to_error!(imp, gst::FlowReturn::Error, {
1222            imp.transform(
1223                &from_glib_borrow(inbuf),
1224                gst::BufferRef::from_mut_ptr(outbuf),
1225            )
1226            .into()
1227        })
1228        .into_glib()
1229    }
1230}
1231
1232unsafe extern "C" fn base_transform_transform_ip<T: BaseTransformImpl>(
1233    ptr: *mut ffi::GstBaseTransform,
1234    buf: *mut *mut gst::ffi::GstBuffer,
1235) -> gst::ffi::GstFlowReturn {
1236    unsafe {
1237        let instance = &*(ptr as *mut T::Instance);
1238        let imp = instance.imp();
1239
1240        // FIXME: Wrong signature in FFI
1241        let buf = buf as *mut gst::ffi::GstBuffer;
1242
1243        gst::element_panic_to_error!(imp, gst::FlowReturn::Error, {
1244            if from_glib(ffi::gst_base_transform_is_passthrough(ptr)) {
1245                imp.transform_ip_passthrough(&from_glib_borrow(buf)).into()
1246            } else {
1247                imp.transform_ip(gst::BufferRef::from_mut_ptr(buf)).into()
1248            }
1249        })
1250        .into_glib()
1251    }
1252}
1253
1254unsafe extern "C" fn base_transform_transform_meta<T: BaseTransformImpl>(
1255    ptr: *mut ffi::GstBaseTransform,
1256    outbuf: *mut gst::ffi::GstBuffer,
1257    meta: *mut gst::ffi::GstMeta,
1258    inbuf: *mut gst::ffi::GstBuffer,
1259) -> glib::ffi::gboolean {
1260    unsafe {
1261        let instance = &*(ptr as *mut T::Instance);
1262        let imp = instance.imp();
1263
1264        let inbuf = gst::BufferRef::from_ptr(inbuf);
1265
1266        gst::element_panic_to_error!(imp, false, {
1267            imp.transform_meta(
1268                gst::BufferRef::from_mut_ptr(outbuf),
1269                gst::Meta::from_ptr(inbuf, meta),
1270                inbuf,
1271            )
1272        })
1273        .into_glib()
1274    }
1275}
1276
1277unsafe extern "C" fn base_transform_propose_allocation<T: BaseTransformImpl>(
1278    ptr: *mut ffi::GstBaseTransform,
1279    decide_query: *mut gst::ffi::GstQuery,
1280    query: *mut gst::ffi::GstQuery,
1281) -> glib::ffi::gboolean {
1282    unsafe {
1283        let instance = &*(ptr as *mut T::Instance);
1284        let imp = instance.imp();
1285        let decide_query = if decide_query.is_null() {
1286            None
1287        } else {
1288            match gst::QueryRef::from_ptr(decide_query).view() {
1289                gst::QueryView::Allocation(allocation) => Some(allocation),
1290                _ => unreachable!(),
1291            }
1292        };
1293        let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1294            gst::QueryViewMut::Allocation(allocation) => allocation,
1295            _ => unreachable!(),
1296        };
1297
1298        gst::element_panic_to_error!(imp, false, {
1299            match imp.propose_allocation(decide_query, query) {
1300                Ok(()) => true,
1301                Err(err) => {
1302                    err.log_with_imp_and_level(imp, gst::DebugLevel::Info);
1303                    false
1304                }
1305            }
1306        })
1307        .into_glib()
1308    }
1309}
1310
1311unsafe extern "C" fn base_transform_decide_allocation<T: BaseTransformImpl>(
1312    ptr: *mut ffi::GstBaseTransform,
1313    query: *mut gst::ffi::GstQuery,
1314) -> glib::ffi::gboolean {
1315    unsafe {
1316        let instance = &*(ptr as *mut T::Instance);
1317        let imp = instance.imp();
1318        let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1319            gst::QueryViewMut::Allocation(allocation) => allocation,
1320            _ => unreachable!(),
1321        };
1322
1323        gst::element_panic_to_error!(imp, false, {
1324            match imp.decide_allocation(query) {
1325                Ok(()) => true,
1326                Err(err) => {
1327                    err.log_with_imp(imp);
1328                    false
1329                }
1330            }
1331        })
1332        .into_glib()
1333    }
1334}
1335
1336unsafe extern "C" fn base_transform_copy_metadata<T: BaseTransformImpl>(
1337    ptr: *mut ffi::GstBaseTransform,
1338    inbuf: *mut gst::ffi::GstBuffer,
1339    outbuf: *mut gst::ffi::GstBuffer,
1340) -> glib::ffi::gboolean {
1341    unsafe {
1342        let instance = &*(ptr as *mut T::Instance);
1343        let imp = instance.imp();
1344
1345        if gst::ffi::gst_mini_object_is_writable(outbuf as *mut _) == glib::ffi::GFALSE {
1346            let instance = imp.obj();
1347            let obj = instance.unsafe_cast_ref::<BaseTransform>();
1348            gst::warning!(gst::CAT_RUST, obj = obj, "buffer {:?} not writable", outbuf);
1349            return glib::ffi::GFALSE;
1350        }
1351
1352        gst::element_panic_to_error!(imp, true, {
1353            match imp.copy_metadata(
1354                gst::BufferRef::from_ptr(inbuf),
1355                gst::BufferRef::from_mut_ptr(outbuf),
1356            ) {
1357                Ok(_) => true,
1358                Err(err) => {
1359                    err.log_with_imp(imp);
1360                    false
1361                }
1362            }
1363        })
1364        .into_glib()
1365    }
1366}
1367
1368unsafe extern "C" fn base_transform_before_transform<T: BaseTransformImpl>(
1369    ptr: *mut ffi::GstBaseTransform,
1370    inbuf: *mut gst::ffi::GstBuffer,
1371) {
1372    unsafe {
1373        let instance = &*(ptr as *mut T::Instance);
1374        let imp = instance.imp();
1375
1376        gst::element_panic_to_error!(imp, (), {
1377            imp.before_transform(gst::BufferRef::from_ptr(inbuf));
1378        })
1379    }
1380}
1381
1382unsafe extern "C" fn base_transform_submit_input_buffer<T: BaseTransformImpl>(
1383    ptr: *mut ffi::GstBaseTransform,
1384    is_discont: glib::ffi::gboolean,
1385    buf: *mut gst::ffi::GstBuffer,
1386) -> gst::ffi::GstFlowReturn {
1387    unsafe {
1388        let instance = &*(ptr as *mut T::Instance);
1389        let imp = instance.imp();
1390
1391        gst::element_panic_to_error!(imp, gst::FlowReturn::Error, {
1392            imp.submit_input_buffer(from_glib(is_discont), from_glib_full(buf))
1393                .into()
1394        })
1395        .into_glib()
1396    }
1397}
1398
1399unsafe extern "C" fn base_transform_generate_output<T: BaseTransformImpl>(
1400    ptr: *mut ffi::GstBaseTransform,
1401    buf: *mut *mut gst::ffi::GstBuffer,
1402) -> gst::ffi::GstFlowReturn {
1403    unsafe {
1404        let instance = &*(ptr as *mut T::Instance);
1405        let imp = instance.imp();
1406
1407        *buf = ptr::null_mut();
1408
1409        gst::element_panic_to_error!(imp, gst::FlowReturn::Error, {
1410            match imp.generate_output() {
1411                Ok(GenerateOutputSuccess::Dropped) => crate::BASE_TRANSFORM_FLOW_DROPPED.into(),
1412                Ok(GenerateOutputSuccess::NoOutput) => gst::FlowReturn::Ok,
1413                Ok(GenerateOutputSuccess::Buffer(outbuf)) => {
1414                    *buf = outbuf.into_glib_ptr();
1415                    gst::FlowReturn::Ok
1416                }
1417                Err(err) => err.into(),
1418            }
1419        })
1420        .into_glib()
1421    }
1422}
1423
1424#[cfg(feature = "v1_30")]
1425#[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
1426unsafe extern "C" fn base_transform_prepare_allocator<T: BaseTransformImpl>(
1427    ptr: *mut ffi::GstBaseTransform,
1428    caps: *mut gst::ffi::GstCaps,
1429) -> glib::ffi::gboolean {
1430    unsafe {
1431        let instance = &*(ptr as *mut T::Instance);
1432        let imp = instance.imp();
1433        let caps = Option::<gst::Caps>::from_glib_none(caps);
1434
1435        gst::element_panic_to_error!(imp, false, {
1436            match imp.prepare_allocator(caps.as_ref()) {
1437                Ok(()) => true,
1438                Err(err) => {
1439                    err.log_with_imp(imp);
1440                    false
1441                }
1442            }
1443        })
1444        .into_glib()
1445    }
1446}
1447
1448#[cfg(test)]
1449mod tests {
1450    use super::*;
1451
1452    pub mod imp {
1453        use super::*;
1454        use std::sync::atomic::{self, AtomicBool};
1455
1456        #[derive(Default)]
1457        pub struct TestTransform {
1458            drop_next: AtomicBool,
1459        }
1460
1461        #[glib::object_subclass]
1462        impl ObjectSubclass for TestTransform {
1463            const NAME: &'static str = "TestTransform";
1464            type Type = super::TestTransform;
1465            type ParentType = crate::BaseTransform;
1466        }
1467
1468        impl ObjectImpl for TestTransform {}
1469
1470        impl GstObjectImpl for TestTransform {}
1471
1472        impl ElementImpl for TestTransform {
1473            fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
1474                static ELEMENT_METADATA: std::sync::OnceLock<gst::subclass::ElementMetadata> =
1475                    std::sync::OnceLock::new();
1476
1477                Some(ELEMENT_METADATA.get_or_init(|| {
1478                    gst::subclass::ElementMetadata::new(
1479                        "Test Transform",
1480                        "Generic",
1481                        "Does nothing",
1482                        "Sebastian Dröge <sebastian@centricular.com>",
1483                    )
1484                }))
1485            }
1486
1487            fn pad_templates() -> &'static [gst::PadTemplate] {
1488                static PAD_TEMPLATES: std::sync::OnceLock<Vec<gst::PadTemplate>> =
1489                    std::sync::OnceLock::new();
1490
1491                PAD_TEMPLATES.get_or_init(|| {
1492                    let caps = gst::Caps::new_any();
1493                    vec![
1494                        gst::PadTemplate::new(
1495                            "src",
1496                            gst::PadDirection::Src,
1497                            gst::PadPresence::Always,
1498                            &caps,
1499                        )
1500                        .unwrap(),
1501                        gst::PadTemplate::new(
1502                            "sink",
1503                            gst::PadDirection::Sink,
1504                            gst::PadPresence::Always,
1505                            &caps,
1506                        )
1507                        .unwrap(),
1508                    ]
1509                })
1510            }
1511        }
1512
1513        impl BaseTransformImpl for TestTransform {
1514            const MODE: BaseTransformMode = BaseTransformMode::AlwaysInPlace;
1515
1516            const PASSTHROUGH_ON_SAME_CAPS: bool = false;
1517
1518            const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
1519
1520            fn transform_ip(
1521                &self,
1522                _buf: &mut gst::BufferRef,
1523            ) -> Result<gst::FlowSuccess, gst::FlowError> {
1524                if self.drop_next.load(atomic::Ordering::SeqCst) {
1525                    self.drop_next.store(false, atomic::Ordering::SeqCst);
1526                    Ok(crate::BASE_TRANSFORM_FLOW_DROPPED)
1527                } else {
1528                    self.drop_next.store(true, atomic::Ordering::SeqCst);
1529                    Ok(gst::FlowSuccess::Ok)
1530                }
1531            }
1532        }
1533    }
1534
1535    glib::wrapper! {
1536        pub struct TestTransform(ObjectSubclass<imp::TestTransform>) @extends crate::BaseTransform, gst::Element, gst::Object;
1537    }
1538
1539    impl TestTransform {
1540        pub fn new(name: Option<&str>) -> Self {
1541            glib::Object::builder().property("name", name).build()
1542        }
1543    }
1544
1545    #[test]
1546    fn test_transform_subclass() {
1547        gst::init().unwrap();
1548
1549        let element = TestTransform::new(Some("test"));
1550
1551        assert_eq!(element.name(), "test");
1552
1553        let pipeline = gst::Pipeline::new();
1554        let src = gst::ElementFactory::make("audiotestsrc")
1555            .property("num-buffers", 100i32)
1556            .build()
1557            .unwrap();
1558        let sink = gst::ElementFactory::make("fakesink").build().unwrap();
1559
1560        pipeline
1561            .add_many([&src, element.upcast_ref(), &sink])
1562            .unwrap();
1563        gst::Element::link_many([&src, element.upcast_ref(), &sink]).unwrap();
1564
1565        pipeline.set_state(gst::State::Playing).unwrap();
1566        let bus = pipeline.bus().unwrap();
1567
1568        let eos = bus.timed_pop_filtered(gst::ClockTime::NONE, &[gst::MessageType::Eos]);
1569        assert!(eos.is_some());
1570
1571        let stats = sink.property::<gst::Structure>("stats");
1572        assert_eq!(stats.get::<u64>("rendered").unwrap(), 50);
1573
1574        pipeline.set_state(gst::State::Null).unwrap();
1575    }
1576}