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    ///  calls ``decide_allocation()``.
164    /// ## `caps`
165    /// the negotiated [`gst::Caps`][crate::gst::Caps]
166    ///
167    /// # Returns
168    ///
169    ///  [`gst::Allocator`][crate::gst::Allocator] could be configured.
170    #[cfg(feature = "v1_30")]
171    #[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
172    fn prepare_allocator(&self, caps: Option<&gst::Caps>) -> Result<(), gst::LoggableError> {
173        self.parent_prepare_allocator(caps)
174    }
175
176    /// Optional.
177    ///  Copy the metadata from the input buffer to the output buffer.
178    ///  The default implementation will copy the flags, timestamps and
179    ///  offsets of the buffer.
180    fn copy_metadata(
181        &self,
182        inbuf: &gst::BufferRef,
183        outbuf: &mut gst::BufferRef,
184    ) -> Result<(), gst::LoggableError> {
185        self.parent_copy_metadata(inbuf, outbuf)
186    }
187
188    /// Optional. Transform the metadata on the input buffer to the
189    ///  output buffer. By default this method copies all meta without
190    ///  tags. Subclasses can implement this method and return [`true`] if
191    ///  the metadata is to be copied.
192    fn transform_meta<'a>(
193        &self,
194        outbuf: &mut gst::BufferRef,
195        meta: gst::MetaRef<'a, gst::Meta>,
196        inbuf: &'a gst::BufferRef,
197    ) -> bool {
198        self.parent_transform_meta(outbuf, meta, inbuf)
199    }
200
201    /// Optional.
202    ///  This method is called right before the base class will
203    ///  start processing. Dynamic properties or other delayed
204    ///  configuration could be performed in this method.
205    fn before_transform(&self, inbuf: &gst::BufferRef) {
206        self.parent_before_transform(inbuf);
207    }
208
209    /// Function which accepts a new input buffer and pre-processes it.
210    ///  The default implementation performs caps (re)negotiation, then
211    ///  QoS if needed, and places the input buffer into the `queued_buf`
212    ///  member variable. If the buffer is dropped due to QoS, it returns
213    ///  GST_BASE_TRANSFORM_FLOW_DROPPED. If this input buffer is not
214    ///  contiguous with any previous input buffer, then `is_discont`
215    ///  is set to [`true`]. (Since: 1.6)
216    fn submit_input_buffer(
217        &self,
218        is_discont: bool,
219        inbuf: gst::Buffer,
220    ) -> Result<gst::FlowSuccess, gst::FlowError> {
221        self.parent_submit_input_buffer(is_discont, inbuf)
222    }
223
224    fn generate_output(&self) -> Result<GenerateOutputSuccess, gst::FlowError> {
225        self.parent_generate_output()
226    }
227}
228
229pub trait BaseTransformImplExt: BaseTransformImpl {
230    fn parent_start(&self) -> Result<(), gst::ErrorMessage> {
231        unsafe {
232            let data = Self::type_data();
233            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
234            (*parent_class)
235                .start
236                .map(|f| {
237                    if from_glib(f(self
238                        .obj()
239                        .unsafe_cast_ref::<BaseTransform>()
240                        .to_glib_none()
241                        .0))
242                    {
243                        Ok(())
244                    } else {
245                        Err(gst::error_msg!(
246                            gst::CoreError::StateChange,
247                            ["Parent function `start` failed"]
248                        ))
249                    }
250                })
251                .unwrap_or(Ok(()))
252        }
253    }
254
255    fn parent_stop(&self) -> Result<(), gst::ErrorMessage> {
256        unsafe {
257            let data = Self::type_data();
258            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
259            (*parent_class)
260                .stop
261                .map(|f| {
262                    if from_glib(f(self
263                        .obj()
264                        .unsafe_cast_ref::<BaseTransform>()
265                        .to_glib_none()
266                        .0))
267                    {
268                        Ok(())
269                    } else {
270                        Err(gst::error_msg!(
271                            gst::CoreError::StateChange,
272                            ["Parent function `stop` failed"]
273                        ))
274                    }
275                })
276                .unwrap_or(Ok(()))
277        }
278    }
279
280    fn parent_transform_caps(
281        &self,
282        direction: gst::PadDirection,
283        caps: &gst::Caps,
284        filter: Option<&gst::Caps>,
285    ) -> Option<gst::Caps> {
286        unsafe {
287            let data = Self::type_data();
288            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
289            (*parent_class)
290                .transform_caps
291                .map(|f| {
292                    from_glib_full(f(
293                        self.obj()
294                            .unsafe_cast_ref::<BaseTransform>()
295                            .to_glib_none()
296                            .0,
297                        direction.into_glib(),
298                        caps.to_glib_none().0,
299                        filter.to_glib_none().0,
300                    ))
301                })
302                .unwrap_or(None)
303        }
304    }
305
306    fn parent_fixate_caps(
307        &self,
308        direction: gst::PadDirection,
309        caps: &gst::Caps,
310        othercaps: gst::Caps,
311    ) -> gst::Caps {
312        unsafe {
313            let data = Self::type_data();
314            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
315            match (*parent_class).fixate_caps {
316                Some(f) => from_glib_full(f(
317                    self.obj()
318                        .unsafe_cast_ref::<BaseTransform>()
319                        .to_glib_none()
320                        .0,
321                    direction.into_glib(),
322                    caps.to_glib_none().0,
323                    othercaps.into_glib_ptr(),
324                )),
325                None => othercaps,
326            }
327        }
328    }
329
330    fn parent_set_caps(
331        &self,
332        incaps: &gst::Caps,
333        outcaps: &gst::Caps,
334    ) -> Result<(), gst::LoggableError> {
335        unsafe {
336            let data = Self::type_data();
337            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
338            (*parent_class)
339                .set_caps
340                .map(|f| {
341                    gst::result_from_gboolean!(
342                        f(
343                            self.obj()
344                                .unsafe_cast_ref::<BaseTransform>()
345                                .to_glib_none()
346                                .0,
347                            incaps.to_glib_none().0,
348                            outcaps.to_glib_none().0,
349                        ),
350                        gst::CAT_RUST,
351                        "Parent function `set_caps` failed"
352                    )
353                })
354                .unwrap_or(Ok(()))
355        }
356    }
357
358    fn parent_accept_caps(&self, direction: gst::PadDirection, caps: &gst::Caps) -> bool {
359        unsafe {
360            let data = Self::type_data();
361            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
362            (*parent_class)
363                .accept_caps
364                .map(|f| {
365                    from_glib(f(
366                        self.obj()
367                            .unsafe_cast_ref::<BaseTransform>()
368                            .to_glib_none()
369                            .0,
370                        direction.into_glib(),
371                        caps.to_glib_none().0,
372                    ))
373                })
374                .unwrap_or(false)
375        }
376    }
377
378    fn parent_query(&self, direction: gst::PadDirection, query: &mut gst::QueryRef) -> bool {
379        unsafe {
380            let data = Self::type_data();
381            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
382            (*parent_class)
383                .query
384                .map(|f| {
385                    from_glib(f(
386                        self.obj()
387                            .unsafe_cast_ref::<BaseTransform>()
388                            .to_glib_none()
389                            .0,
390                        direction.into_glib(),
391                        query.as_mut_ptr(),
392                    ))
393                })
394                .unwrap_or(false)
395        }
396    }
397
398    fn parent_transform_size(
399        &self,
400        direction: gst::PadDirection,
401        caps: &gst::Caps,
402        size: usize,
403        othercaps: &gst::Caps,
404    ) -> Option<usize> {
405        unsafe {
406            let data = Self::type_data();
407            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
408            (*parent_class)
409                .transform_size
410                .map(|f| {
411                    let mut othersize = mem::MaybeUninit::uninit();
412                    let res: bool = from_glib(f(
413                        self.obj()
414                            .unsafe_cast_ref::<BaseTransform>()
415                            .to_glib_none()
416                            .0,
417                        direction.into_glib(),
418                        caps.to_glib_none().0,
419                        size,
420                        othercaps.to_glib_none().0,
421                        othersize.as_mut_ptr(),
422                    ));
423                    if res {
424                        Some(othersize.assume_init())
425                    } else {
426                        None
427                    }
428                })
429                .unwrap_or(None)
430        }
431    }
432
433    fn parent_unit_size(&self, caps: &gst::Caps) -> Option<usize> {
434        unsafe {
435            let data = Self::type_data();
436            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
437            let f = (*parent_class).get_unit_size.unwrap_or_else(|| {
438                if !self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
439                    unimplemented!(concat!(
440                        "Missing parent function `get_unit_size`. Required because ",
441                        "transform doesn't operate in-place"
442                    ))
443                } else {
444                    unreachable!("parent `get_unit_size` called while transform operates in-place")
445                }
446            });
447
448            let mut size = mem::MaybeUninit::uninit();
449            if from_glib(f(
450                self.obj()
451                    .unsafe_cast_ref::<BaseTransform>()
452                    .to_glib_none()
453                    .0,
454                caps.to_glib_none().0,
455                size.as_mut_ptr(),
456            )) {
457                Some(size.assume_init())
458            } else {
459                None
460            }
461        }
462    }
463
464    fn parent_sink_event(&self, event: gst::Event) -> bool {
465        unsafe {
466            let data = Self::type_data();
467            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
468            (*parent_class)
469                .sink_event
470                .map(|f| {
471                    from_glib(f(
472                        self.obj()
473                            .unsafe_cast_ref::<BaseTransform>()
474                            .to_glib_none()
475                            .0,
476                        event.into_glib_ptr(),
477                    ))
478                })
479                .unwrap_or(true)
480        }
481    }
482
483    fn parent_src_event(&self, event: gst::Event) -> bool {
484        unsafe {
485            let data = Self::type_data();
486            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
487            (*parent_class)
488                .src_event
489                .map(|f| {
490                    from_glib(f(
491                        self.obj()
492                            .unsafe_cast_ref::<BaseTransform>()
493                            .to_glib_none()
494                            .0,
495                        event.into_glib_ptr(),
496                    ))
497                })
498                .unwrap_or(true)
499        }
500    }
501
502    fn parent_prepare_output_buffer(
503        &self,
504        inbuf: InputBuffer,
505    ) -> Result<PrepareOutputBufferSuccess, gst::FlowError> {
506        unsafe {
507            let data = Self::type_data();
508            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
509            let buf = match inbuf {
510                InputBuffer::Readable(inbuf_r) => inbuf_r.as_ptr(),
511                InputBuffer::Writable(inbuf_w) => inbuf_w.as_mut_ptr(),
512            };
513            (*parent_class)
514                .prepare_output_buffer
515                .map(|f| {
516                    let mut outbuf: *mut gst::ffi::GstBuffer = ptr::null_mut();
517                    // FIXME: Wrong signature in FFI
518                    gst::FlowSuccess::try_from_glib(f(
519                        self.obj()
520                            .unsafe_cast_ref::<BaseTransform>()
521                            .to_glib_none()
522                            .0,
523                        buf as *mut gst::ffi::GstBuffer,
524                        (&mut outbuf) as *mut *mut gst::ffi::GstBuffer as *mut gst::ffi::GstBuffer,
525                    ))
526                    .map(|_| {
527                        if ptr::eq(outbuf, buf as *mut _) {
528                            PrepareOutputBufferSuccess::InputBuffer
529                        } else {
530                            PrepareOutputBufferSuccess::Buffer(from_glib_full(outbuf))
531                        }
532                    })
533                    .inspect_err(|_err| {
534                        if !ptr::eq(outbuf, buf as *mut _) {
535                            drop(Option::<gst::Buffer>::from_glib_full(outbuf));
536                        }
537                    })
538                })
539                .unwrap_or(Err(gst::FlowError::NotSupported))
540        }
541    }
542
543    fn parent_transform(
544        &self,
545        inbuf: &gst::Buffer,
546        outbuf: &mut gst::BufferRef,
547    ) -> Result<gst::FlowSuccess, gst::FlowError> {
548        unsafe {
549            let data = Self::type_data();
550            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
551            (*parent_class)
552                .transform
553                .map(|f| {
554                    try_from_glib(f(
555                        self.obj()
556                            .unsafe_cast_ref::<BaseTransform>()
557                            .to_glib_none()
558                            .0,
559                        inbuf.to_glib_none().0,
560                        outbuf.as_mut_ptr(),
561                    ))
562                })
563                .unwrap_or_else(|| {
564                    if !self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
565                        Err(gst::FlowError::NotSupported)
566                    } else {
567                        unreachable!("parent `transform` called while transform operates in-place");
568                    }
569                })
570        }
571    }
572
573    fn parent_transform_ip(
574        &self,
575        buf: &mut gst::BufferRef,
576    ) -> Result<gst::FlowSuccess, gst::FlowError> {
577        unsafe {
578            let data = Self::type_data();
579            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
580            let f = (*parent_class).transform_ip.unwrap_or_else(|| {
581                if self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
582                    panic!(concat!(
583                        "Missing parent function `transform_ip`. Required because ",
584                        "transform operates in-place"
585                    ));
586                } else {
587                    unreachable!(
588                        "parent `transform` called while transform doesn't operate in-place"
589                    );
590                }
591            });
592
593            try_from_glib(f(
594                self.obj()
595                    .unsafe_cast_ref::<BaseTransform>()
596                    .to_glib_none()
597                    .0,
598                buf.as_mut_ptr() as *mut _,
599            ))
600        }
601    }
602
603    fn parent_transform_ip_passthrough(
604        &self,
605        buf: &gst::Buffer,
606    ) -> Result<gst::FlowSuccess, gst::FlowError> {
607        unsafe {
608            let data = Self::type_data();
609            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
610            let f = (*parent_class).transform_ip.unwrap_or_else(|| {
611                if self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
612                    panic!(concat!(
613                        "Missing parent function `transform_ip`. Required because ",
614                        "transform operates in-place (passthrough mode)"
615                    ));
616                } else {
617                    unreachable!(concat!(
618                        "parent `transform_ip` called ",
619                        "while transform doesn't operate in-place (passthrough mode)"
620                    ));
621                }
622            });
623
624            // FIXME: Wrong signature in FFI
625            let buf: *mut gst::ffi::GstBuffer = buf.to_glib_none().0;
626            try_from_glib(f(
627                self.obj()
628                    .unsafe_cast_ref::<BaseTransform>()
629                    .to_glib_none()
630                    .0,
631                buf as *mut _,
632            ))
633        }
634    }
635
636    fn parent_propose_allocation(
637        &self,
638        decide_query: Option<&gst::query::Allocation>,
639        query: &mut gst::query::Allocation,
640    ) -> Result<(), gst::LoggableError> {
641        unsafe {
642            let data = Self::type_data();
643            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
644            (*parent_class)
645                .propose_allocation
646                .map(|f| {
647                    gst::result_from_gboolean!(
648                        f(
649                            self.obj()
650                                .unsafe_cast_ref::<BaseTransform>()
651                                .to_glib_none()
652                                .0,
653                            decide_query
654                                .as_ref()
655                                .map(|q| q.as_mut_ptr())
656                                .unwrap_or(ptr::null_mut()),
657                            query.as_mut_ptr(),
658                        ),
659                        gst::CAT_RUST,
660                        "Parent function `propose_allocation` failed",
661                    )
662                })
663                .unwrap_or(Ok(()))
664        }
665    }
666
667    fn parent_decide_allocation(
668        &self,
669        query: &mut gst::query::Allocation,
670    ) -> Result<(), gst::LoggableError> {
671        unsafe {
672            let data = Self::type_data();
673            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
674            (*parent_class)
675                .decide_allocation
676                .map(|f| {
677                    gst::result_from_gboolean!(
678                        f(
679                            self.obj()
680                                .unsafe_cast_ref::<BaseTransform>()
681                                .to_glib_none()
682                                .0,
683                            query.as_mut_ptr(),
684                        ),
685                        gst::CAT_RUST,
686                        "Parent function `decide_allocation` failed,"
687                    )
688                })
689                .unwrap_or(Ok(()))
690        }
691    }
692
693    #[cfg(feature = "v1_30")]
694    #[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
695    fn parent_prepare_allocator(&self, caps: Option<&gst::Caps>) -> Result<(), gst::LoggableError> {
696        unsafe {
697            let data = Self::type_data();
698            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
699            (*parent_class)
700                .prepare_allocator
701                .map(|f| {
702                    gst::result_from_gboolean!(
703                        f(
704                            self.obj()
705                                .unsafe_cast_ref::<BaseTransform>()
706                                .to_glib_none()
707                                .0,
708                            caps.to_glib_none().0
709                        ),
710                        gst::CAT_RUST,
711                        "Parent function `prepare_allocator` failed",
712                    )
713                })
714                .unwrap_or(Ok(()))
715        }
716    }
717
718    fn parent_copy_metadata(
719        &self,
720        inbuf: &gst::BufferRef,
721        outbuf: &mut gst::BufferRef,
722    ) -> Result<(), gst::LoggableError> {
723        unsafe {
724            let data = Self::type_data();
725            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
726            if let Some(ref f) = (*parent_class).copy_metadata {
727                gst::result_from_gboolean!(
728                    f(
729                        self.obj()
730                            .unsafe_cast_ref::<BaseTransform>()
731                            .to_glib_none()
732                            .0,
733                        inbuf.as_ptr() as *mut _,
734                        outbuf.as_mut_ptr()
735                    ),
736                    gst::CAT_RUST,
737                    "Parent function `copy_metadata` failed"
738                )
739            } else {
740                Ok(())
741            }
742        }
743    }
744
745    fn parent_transform_meta<'a>(
746        &self,
747        outbuf: &mut gst::BufferRef,
748        meta: gst::MetaRef<'a, gst::Meta>,
749        inbuf: &'a gst::BufferRef,
750    ) -> bool {
751        unsafe {
752            let data = Self::type_data();
753            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
754            (*parent_class)
755                .transform_meta
756                .map(|f| {
757                    from_glib(f(
758                        self.obj()
759                            .unsafe_cast_ref::<BaseTransform>()
760                            .to_glib_none()
761                            .0,
762                        outbuf.as_mut_ptr(),
763                        meta.as_ptr() as *mut _,
764                        inbuf.as_ptr() as *mut _,
765                    ))
766                })
767                .unwrap_or(false)
768        }
769    }
770
771    fn parent_before_transform(&self, inbuf: &gst::BufferRef) {
772        unsafe {
773            let data = Self::type_data();
774            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
775            if let Some(ref f) = (*parent_class).before_transform {
776                f(
777                    self.obj()
778                        .unsafe_cast_ref::<BaseTransform>()
779                        .to_glib_none()
780                        .0,
781                    inbuf.as_ptr() as *mut _,
782                );
783            }
784        }
785    }
786
787    fn parent_submit_input_buffer(
788        &self,
789        is_discont: bool,
790        inbuf: gst::Buffer,
791    ) -> Result<gst::FlowSuccess, gst::FlowError> {
792        unsafe {
793            let data = Self::type_data();
794            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
795            let f = (*parent_class)
796                .submit_input_buffer
797                .expect("Missing parent function `submit_input_buffer`");
798
799            try_from_glib(f(
800                self.obj()
801                    .unsafe_cast_ref::<BaseTransform>()
802                    .to_glib_none()
803                    .0,
804                is_discont.into_glib(),
805                inbuf.into_glib_ptr(),
806            ))
807        }
808    }
809
810    fn parent_generate_output(&self) -> Result<GenerateOutputSuccess, gst::FlowError> {
811        unsafe {
812            let data = Self::type_data();
813            let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
814            let f = (*parent_class)
815                .generate_output
816                .expect("Missing parent function `generate_output`");
817
818            let mut outbuf = ptr::null_mut();
819            let res = gst::FlowSuccess::try_from_glib(f(
820                self.obj()
821                    .unsafe_cast_ref::<BaseTransform>()
822                    .to_glib_none()
823                    .0,
824                &mut outbuf,
825            ));
826
827            let outbuf = Option::<gst::Buffer>::from_glib_full(outbuf);
828
829            res.map(move |res| match (res, outbuf) {
830                (crate::BASE_TRANSFORM_FLOW_DROPPED, _) => GenerateOutputSuccess::Dropped,
831                (gst::FlowSuccess::Ok, Some(outbuf)) => GenerateOutputSuccess::Buffer(outbuf),
832                _ => GenerateOutputSuccess::NoOutput,
833            })
834        }
835    }
836
837    fn take_queued_buffer(&self) -> Option<gst::Buffer>
838    where
839        Self: ObjectSubclass,
840        <Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
841    {
842        unsafe {
843            let instance = self.obj();
844            let ptr: *mut ffi::GstBaseTransform =
845                instance.unsafe_cast_ref::<BaseTransform>().to_glib_none().0;
846            let sinkpad: Borrowed<gst::Pad> = from_glib_borrow((*ptr).sinkpad);
847            let _stream_lock = sinkpad.stream_lock();
848            let buffer = (*ptr).queued_buf;
849            (*ptr).queued_buf = ptr::null_mut();
850            from_glib_full(buffer)
851        }
852    }
853
854    fn queued_buffer(&self) -> Option<gst::Buffer>
855    where
856        Self: ObjectSubclass,
857        <Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
858    {
859        unsafe {
860            let instance = self.obj();
861            let ptr: *mut ffi::GstBaseTransform =
862                instance.unsafe_cast_ref::<BaseTransform>().to_glib_none().0;
863            let sinkpad: Borrowed<gst::Pad> = from_glib_borrow((*ptr).sinkpad);
864            let _stream_lock = sinkpad.stream_lock();
865            let buffer = (*ptr).queued_buf;
866            from_glib_none(buffer)
867        }
868    }
869}
870
871impl<T: BaseTransformImpl> BaseTransformImplExt for T {}
872
873unsafe impl<T: BaseTransformImpl> IsSubclassable<T> for BaseTransform {
874    fn class_init(klass: &mut glib::Class<Self>) {
875        Self::parent_class_init::<T>(klass);
876        let klass = klass.as_mut();
877        klass.start = Some(base_transform_start::<T>);
878        klass.stop = Some(base_transform_stop::<T>);
879        klass.transform_caps = Some(base_transform_transform_caps::<T>);
880        klass.fixate_caps = Some(base_transform_fixate_caps::<T>);
881        klass.set_caps = Some(base_transform_set_caps::<T>);
882        klass.accept_caps = Some(base_transform_accept_caps::<T>);
883        klass.query = Some(base_transform_query::<T>);
884        klass.transform_size = Some(base_transform_transform_size::<T>);
885        klass.get_unit_size = Some(base_transform_get_unit_size::<T>);
886        klass.prepare_output_buffer = Some(base_transform_prepare_output_buffer::<T>);
887        klass.sink_event = Some(base_transform_sink_event::<T>);
888        klass.src_event = Some(base_transform_src_event::<T>);
889        klass.transform_meta = Some(base_transform_transform_meta::<T>);
890        klass.propose_allocation = Some(base_transform_propose_allocation::<T>);
891        klass.decide_allocation = Some(base_transform_decide_allocation::<T>);
892        klass.copy_metadata = Some(base_transform_copy_metadata::<T>);
893        klass.before_transform = Some(base_transform_before_transform::<T>);
894        klass.submit_input_buffer = Some(base_transform_submit_input_buffer::<T>);
895        klass.generate_output = Some(base_transform_generate_output::<T>);
896
897        klass.passthrough_on_same_caps = T::PASSTHROUGH_ON_SAME_CAPS.into_glib();
898        klass.transform_ip_on_passthrough = T::TRANSFORM_IP_ON_PASSTHROUGH.into_glib();
899
900        match T::MODE {
901            BaseTransformMode::AlwaysInPlace => {
902                klass.transform = None;
903                klass.transform_ip = Some(base_transform_transform_ip::<T>);
904            }
905            BaseTransformMode::NeverInPlace => {
906                klass.transform = Some(base_transform_transform::<T>);
907                klass.transform_ip = None;
908            }
909            BaseTransformMode::Both => {
910                klass.transform = Some(base_transform_transform::<T>);
911                klass.transform_ip = Some(base_transform_transform_ip::<T>);
912            }
913        }
914
915        #[cfg(feature = "v1_30")]
916        {
917            klass.prepare_allocator = Some(base_transform_prepare_allocator::<T>);
918        }
919    }
920}
921
922#[derive(Debug)]
923pub enum GenerateOutputSuccess {
924    Buffer(gst::Buffer),
925    NoOutput,
926    Dropped,
927}
928
929#[derive(Debug)]
930pub enum PrepareOutputBufferSuccess {
931    Buffer(gst::Buffer),
932    InputBuffer,
933}
934
935#[derive(Debug)]
936pub enum InputBuffer<'a> {
937    Writable(&'a mut gst::BufferRef),
938    Readable(&'a gst::BufferRef),
939}
940
941unsafe extern "C" fn base_transform_start<T: BaseTransformImpl>(
942    ptr: *mut ffi::GstBaseTransform,
943) -> glib::ffi::gboolean {
944    unsafe {
945        let instance = &*(ptr as *mut T::Instance);
946        let imp = instance.imp();
947
948        gst::element_panic_to_error!(imp, false, {
949            match imp.start() {
950                Ok(()) => true,
951                Err(err) => {
952                    imp.post_error_message(err);
953                    false
954                }
955            }
956        })
957        .into_glib()
958    }
959}
960
961unsafe extern "C" fn base_transform_stop<T: BaseTransformImpl>(
962    ptr: *mut ffi::GstBaseTransform,
963) -> glib::ffi::gboolean {
964    unsafe {
965        let instance = &*(ptr as *mut T::Instance);
966        let imp = instance.imp();
967
968        gst::element_panic_to_error!(imp, false, {
969            match imp.stop() {
970                Ok(()) => true,
971                Err(err) => {
972                    imp.post_error_message(err);
973                    false
974                }
975            }
976        })
977        .into_glib()
978    }
979}
980
981unsafe extern "C" fn base_transform_transform_caps<T: BaseTransformImpl>(
982    ptr: *mut ffi::GstBaseTransform,
983    direction: gst::ffi::GstPadDirection,
984    caps: *mut gst::ffi::GstCaps,
985    filter: *mut gst::ffi::GstCaps,
986) -> *mut gst::ffi::GstCaps {
987    unsafe {
988        let instance = &*(ptr as *mut T::Instance);
989        let imp = instance.imp();
990
991        gst::element_panic_to_error!(imp, None, {
992            let filter: Borrowed<Option<gst::Caps>> = from_glib_borrow(filter);
993
994            imp.transform_caps(
995                from_glib(direction),
996                &from_glib_borrow(caps),
997                filter.as_ref().as_ref(),
998            )
999        })
1000        .map(|caps| caps.into_glib_ptr())
1001        .unwrap_or(std::ptr::null_mut())
1002    }
1003}
1004
1005unsafe extern "C" fn base_transform_fixate_caps<T: BaseTransformImpl>(
1006    ptr: *mut ffi::GstBaseTransform,
1007    direction: gst::ffi::GstPadDirection,
1008    caps: *mut gst::ffi::GstCaps,
1009    othercaps: *mut gst::ffi::GstCaps,
1010) -> *mut gst::ffi::GstCaps {
1011    unsafe {
1012        let instance = &*(ptr as *mut T::Instance);
1013        let imp = instance.imp();
1014
1015        gst::element_panic_to_error!(imp, gst::Caps::new_empty(), {
1016            imp.fixate_caps(
1017                from_glib(direction),
1018                &from_glib_borrow(caps),
1019                from_glib_full(othercaps),
1020            )
1021        })
1022        .into_glib_ptr()
1023    }
1024}
1025
1026unsafe extern "C" fn base_transform_set_caps<T: BaseTransformImpl>(
1027    ptr: *mut ffi::GstBaseTransform,
1028    incaps: *mut gst::ffi::GstCaps,
1029    outcaps: *mut gst::ffi::GstCaps,
1030) -> glib::ffi::gboolean {
1031    unsafe {
1032        let instance = &*(ptr as *mut T::Instance);
1033        let imp = instance.imp();
1034
1035        gst::element_panic_to_error!(imp, false, {
1036            match imp.set_caps(&from_glib_borrow(incaps), &from_glib_borrow(outcaps)) {
1037                Ok(()) => true,
1038                Err(err) => {
1039                    err.log_with_imp(imp);
1040                    false
1041                }
1042            }
1043        })
1044        .into_glib()
1045    }
1046}
1047
1048unsafe extern "C" fn base_transform_accept_caps<T: BaseTransformImpl>(
1049    ptr: *mut ffi::GstBaseTransform,
1050    direction: gst::ffi::GstPadDirection,
1051    caps: *mut gst::ffi::GstCaps,
1052) -> glib::ffi::gboolean {
1053    unsafe {
1054        let instance = &*(ptr as *mut T::Instance);
1055        let imp = instance.imp();
1056
1057        gst::element_panic_to_error!(imp, false, {
1058            imp.accept_caps(from_glib(direction), &from_glib_borrow(caps))
1059        })
1060        .into_glib()
1061    }
1062}
1063
1064unsafe extern "C" fn base_transform_query<T: BaseTransformImpl>(
1065    ptr: *mut ffi::GstBaseTransform,
1066    direction: gst::ffi::GstPadDirection,
1067    query: *mut gst::ffi::GstQuery,
1068) -> glib::ffi::gboolean {
1069    unsafe {
1070        let instance = &*(ptr as *mut T::Instance);
1071        let imp = instance.imp();
1072
1073        gst::element_panic_to_error!(imp, false, {
1074            BaseTransformImpl::query(
1075                imp,
1076                from_glib(direction),
1077                gst::QueryRef::from_mut_ptr(query),
1078            )
1079        })
1080        .into_glib()
1081    }
1082}
1083
1084unsafe extern "C" fn base_transform_transform_size<T: BaseTransformImpl>(
1085    ptr: *mut ffi::GstBaseTransform,
1086    direction: gst::ffi::GstPadDirection,
1087    caps: *mut gst::ffi::GstCaps,
1088    size: usize,
1089    othercaps: *mut gst::ffi::GstCaps,
1090    othersize: *mut usize,
1091) -> glib::ffi::gboolean {
1092    unsafe {
1093        let instance = &*(ptr as *mut T::Instance);
1094        let imp = instance.imp();
1095
1096        gst::element_panic_to_error!(imp, false, {
1097            match imp.transform_size(
1098                from_glib(direction),
1099                &from_glib_borrow(caps),
1100                size,
1101                &from_glib_borrow(othercaps),
1102            ) {
1103                Some(s) => {
1104                    *othersize = s;
1105                    true
1106                }
1107                None => false,
1108            }
1109        })
1110        .into_glib()
1111    }
1112}
1113
1114unsafe extern "C" fn base_transform_get_unit_size<T: BaseTransformImpl>(
1115    ptr: *mut ffi::GstBaseTransform,
1116    caps: *mut gst::ffi::GstCaps,
1117    size: *mut usize,
1118) -> glib::ffi::gboolean {
1119    unsafe {
1120        let instance = &*(ptr as *mut T::Instance);
1121        let imp = instance.imp();
1122
1123        gst::element_panic_to_error!(imp, false, {
1124            match imp.unit_size(&from_glib_borrow(caps)) {
1125                Some(s) => {
1126                    *size = s;
1127                    true
1128                }
1129                None => false,
1130            }
1131        })
1132        .into_glib()
1133    }
1134}
1135
1136unsafe extern "C" fn base_transform_prepare_output_buffer<T: BaseTransformImpl>(
1137    ptr: *mut ffi::GstBaseTransform,
1138    inbuf: *mut gst::ffi::GstBuffer,
1139    outbuf: *mut gst::ffi::GstBuffer,
1140) -> gst::ffi::GstFlowReturn {
1141    unsafe {
1142        let instance = &*(ptr as *mut T::Instance);
1143        let imp = instance.imp();
1144
1145        // FIXME: Wrong signature in FFI
1146        let outbuf = outbuf as *mut *mut gst::ffi::GstBuffer;
1147        let is_passthrough: bool = from_glib(ffi::gst_base_transform_is_passthrough(ptr));
1148        let is_in_place: bool = from_glib(ffi::gst_base_transform_is_in_place(ptr));
1149        let writable = is_in_place
1150            && !is_passthrough
1151            && gst::ffi::gst_mini_object_is_writable(inbuf as *mut _) != glib::ffi::GFALSE;
1152        let buffer = match writable {
1153            false => InputBuffer::Readable(gst::BufferRef::from_ptr(inbuf)),
1154            true => InputBuffer::Writable(gst::BufferRef::from_mut_ptr(inbuf)),
1155        };
1156
1157        *outbuf = ptr::null_mut();
1158
1159        gst::element_panic_to_error!(imp, gst::FlowReturn::Error, {
1160            match imp.prepare_output_buffer(buffer) {
1161                Ok(PrepareOutputBufferSuccess::InputBuffer) => {
1162                    assert!(
1163                        is_passthrough || is_in_place,
1164                        "Returning InputBuffer only allowed for passthrough or in-place mode"
1165                    );
1166                    *outbuf = inbuf;
1167                    gst::FlowReturn::Ok
1168                }
1169                Ok(PrepareOutputBufferSuccess::Buffer(buf)) => {
1170                    assert!(
1171                        !is_passthrough,
1172                        "Returning Buffer not allowed for passthrough mode"
1173                    );
1174                    *outbuf = buf.into_glib_ptr();
1175                    gst::FlowReturn::Ok
1176                }
1177                Err(err) => err.into(),
1178            }
1179        })
1180        .into_glib()
1181    }
1182}
1183
1184unsafe extern "C" fn base_transform_sink_event<T: BaseTransformImpl>(
1185    ptr: *mut ffi::GstBaseTransform,
1186    event: *mut gst::ffi::GstEvent,
1187) -> glib::ffi::gboolean {
1188    unsafe {
1189        let instance = &*(ptr as *mut T::Instance);
1190        let imp = instance.imp();
1191
1192        gst::element_panic_to_error!(imp, false, { imp.sink_event(from_glib_full(event)) })
1193            .into_glib()
1194    }
1195}
1196
1197unsafe extern "C" fn base_transform_src_event<T: BaseTransformImpl>(
1198    ptr: *mut ffi::GstBaseTransform,
1199    event: *mut gst::ffi::GstEvent,
1200) -> glib::ffi::gboolean {
1201    unsafe {
1202        let instance = &*(ptr as *mut T::Instance);
1203        let imp = instance.imp();
1204
1205        gst::element_panic_to_error!(imp, false, { imp.src_event(from_glib_full(event)) })
1206            .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::element_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::element_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::element_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::element_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::element_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::element_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::element_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::element_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::element_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::element_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}