1use 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 fn start(&self) -> Result<(), gst::ErrorMessage> {
26 self.parent_start()
27 }
28
29 fn stop(&self) -> Result<(), gst::ErrorMessage> {
33 self.parent_stop()
34 }
35
36 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 fn set_caps(&self, incaps: &gst::Caps, outcaps: &gst::Caps) -> Result<(), gst::LoggableError> {
59 self.parent_set_caps(incaps, outcaps)
60 }
61
62 fn accept_caps(&self, direction: gst::PadDirection, caps: &gst::Caps) -> bool {
67 self.parent_accept_caps(direction, caps)
68 }
69
70 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 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 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 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 fn decide_allocation(
157 &self,
158 query: &mut gst::query::Allocation,
159 ) -> Result<(), gst::LoggableError> {
160 self.parent_decide_allocation(query)
161 }
162
163 #[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 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 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 fn before_transform(&self, inbuf: &gst::BufferRef) {
208 self.parent_before_transform(inbuf);
209 }
210
211 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 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 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 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 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}