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")]
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 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 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 fn before_transform(&self, inbuf: &gst::BufferRef) {
206 self.parent_before_transform(inbuf);
207 }
208
209 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 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 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 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 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}