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