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> {
23 self.parent_start()
24 }
25
26 fn stop(&self) -> Result<(), gst::ErrorMessage> {
27 self.parent_stop()
28 }
29
30 fn transform_caps(
31 &self,
32 direction: gst::PadDirection,
33 caps: &gst::Caps,
34 filter: Option<&gst::Caps>,
35 ) -> Option<gst::Caps> {
36 self.parent_transform_caps(direction, caps, filter)
37 }
38
39 fn fixate_caps(
40 &self,
41 direction: gst::PadDirection,
42 caps: &gst::Caps,
43 othercaps: gst::Caps,
44 ) -> gst::Caps {
45 self.parent_fixate_caps(direction, caps, othercaps)
46 }
47
48 fn set_caps(&self, incaps: &gst::Caps, outcaps: &gst::Caps) -> Result<(), gst::LoggableError> {
49 self.parent_set_caps(incaps, outcaps)
50 }
51
52 fn accept_caps(&self, direction: gst::PadDirection, caps: &gst::Caps) -> bool {
53 self.parent_accept_caps(direction, caps)
54 }
55
56 fn query(&self, direction: gst::PadDirection, query: &mut gst::QueryRef) -> bool {
57 BaseTransformImplExt::parent_query(self, direction, query)
58 }
59
60 fn transform_size(
61 &self,
62 direction: gst::PadDirection,
63 caps: &gst::Caps,
64 size: usize,
65 othercaps: &gst::Caps,
66 ) -> Option<usize> {
67 self.parent_transform_size(direction, caps, size, othercaps)
68 }
69
70 fn unit_size(&self, caps: &gst::Caps) -> Option<usize> {
71 self.parent_unit_size(caps)
72 }
73
74 fn sink_event(&self, event: gst::Event) -> bool {
75 self.parent_sink_event(event)
76 }
77
78 fn src_event(&self, event: gst::Event) -> bool {
79 self.parent_src_event(event)
80 }
81
82 fn prepare_output_buffer(
83 &self,
84 inbuf: InputBuffer,
85 ) -> Result<PrepareOutputBufferSuccess, gst::FlowError> {
86 self.parent_prepare_output_buffer(inbuf)
87 }
88
89 fn transform(
90 &self,
91 inbuf: &gst::Buffer,
92 outbuf: &mut gst::BufferRef,
93 ) -> Result<gst::FlowSuccess, gst::FlowError> {
94 self.parent_transform(inbuf, outbuf)
95 }
96
97 fn transform_ip(&self, buf: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
98 self.parent_transform_ip(buf)
99 }
100
101 fn transform_ip_passthrough(
102 &self,
103 buf: &gst::Buffer,
104 ) -> Result<gst::FlowSuccess, gst::FlowError> {
105 self.parent_transform_ip_passthrough(buf)
106 }
107
108 fn propose_allocation(
109 &self,
110 decide_query: Option<&gst::query::Allocation>,
111 query: &mut gst::query::Allocation,
112 ) -> Result<(), gst::LoggableError> {
113 self.parent_propose_allocation(decide_query, query)
114 }
115
116 fn decide_allocation(
117 &self,
118 query: &mut gst::query::Allocation,
119 ) -> Result<(), gst::LoggableError> {
120 self.parent_decide_allocation(query)
121 }
122
123 #[cfg(feature = "v1_30")]
124 #[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
125 fn prepare_allocator(&self, caps: Option<&gst::Caps>) -> Result<(), gst::LoggableError> {
126 self.parent_prepare_allocator(caps)
127 }
128
129 fn copy_metadata(
130 &self,
131 inbuf: &gst::BufferRef,
132 outbuf: &mut gst::BufferRef,
133 ) -> Result<(), gst::LoggableError> {
134 self.parent_copy_metadata(inbuf, outbuf)
135 }
136
137 fn transform_meta<'a>(
138 &self,
139 outbuf: &mut gst::BufferRef,
140 meta: gst::MetaRef<'a, gst::Meta>,
141 inbuf: &'a gst::BufferRef,
142 ) -> bool {
143 self.parent_transform_meta(outbuf, meta, inbuf)
144 }
145
146 fn before_transform(&self, inbuf: &gst::BufferRef) {
147 self.parent_before_transform(inbuf);
148 }
149
150 fn submit_input_buffer(
151 &self,
152 is_discont: bool,
153 inbuf: gst::Buffer,
154 ) -> Result<gst::FlowSuccess, gst::FlowError> {
155 self.parent_submit_input_buffer(is_discont, inbuf)
156 }
157
158 fn generate_output(&self) -> Result<GenerateOutputSuccess, gst::FlowError> {
159 self.parent_generate_output()
160 }
161}
162
163pub trait BaseTransformImplExt: BaseTransformImpl {
164 fn parent_start(&self) -> Result<(), gst::ErrorMessage> {
165 unsafe {
166 let data = Self::type_data();
167 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
168 (*parent_class)
169 .start
170 .map(|f| {
171 if from_glib(f(self
172 .obj()
173 .unsafe_cast_ref::<BaseTransform>()
174 .to_glib_none()
175 .0))
176 {
177 Ok(())
178 } else {
179 Err(gst::error_msg!(
180 gst::CoreError::StateChange,
181 ["Parent function `start` failed"]
182 ))
183 }
184 })
185 .unwrap_or(Ok(()))
186 }
187 }
188
189 fn parent_stop(&self) -> Result<(), gst::ErrorMessage> {
190 unsafe {
191 let data = Self::type_data();
192 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
193 (*parent_class)
194 .stop
195 .map(|f| {
196 if from_glib(f(self
197 .obj()
198 .unsafe_cast_ref::<BaseTransform>()
199 .to_glib_none()
200 .0))
201 {
202 Ok(())
203 } else {
204 Err(gst::error_msg!(
205 gst::CoreError::StateChange,
206 ["Parent function `stop` failed"]
207 ))
208 }
209 })
210 .unwrap_or(Ok(()))
211 }
212 }
213
214 fn parent_transform_caps(
215 &self,
216 direction: gst::PadDirection,
217 caps: &gst::Caps,
218 filter: Option<&gst::Caps>,
219 ) -> Option<gst::Caps> {
220 unsafe {
221 let data = Self::type_data();
222 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
223 (*parent_class)
224 .transform_caps
225 .map(|f| {
226 from_glib_full(f(
227 self.obj()
228 .unsafe_cast_ref::<BaseTransform>()
229 .to_glib_none()
230 .0,
231 direction.into_glib(),
232 caps.to_glib_none().0,
233 filter.to_glib_none().0,
234 ))
235 })
236 .unwrap_or(None)
237 }
238 }
239
240 fn parent_fixate_caps(
241 &self,
242 direction: gst::PadDirection,
243 caps: &gst::Caps,
244 othercaps: gst::Caps,
245 ) -> gst::Caps {
246 unsafe {
247 let data = Self::type_data();
248 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
249 match (*parent_class).fixate_caps {
250 Some(f) => from_glib_full(f(
251 self.obj()
252 .unsafe_cast_ref::<BaseTransform>()
253 .to_glib_none()
254 .0,
255 direction.into_glib(),
256 caps.to_glib_none().0,
257 othercaps.into_glib_ptr(),
258 )),
259 None => othercaps,
260 }
261 }
262 }
263
264 fn parent_set_caps(
265 &self,
266 incaps: &gst::Caps,
267 outcaps: &gst::Caps,
268 ) -> Result<(), gst::LoggableError> {
269 unsafe {
270 let data = Self::type_data();
271 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
272 (*parent_class)
273 .set_caps
274 .map(|f| {
275 gst::result_from_gboolean!(
276 f(
277 self.obj()
278 .unsafe_cast_ref::<BaseTransform>()
279 .to_glib_none()
280 .0,
281 incaps.to_glib_none().0,
282 outcaps.to_glib_none().0,
283 ),
284 gst::CAT_RUST,
285 "Parent function `set_caps` failed"
286 )
287 })
288 .unwrap_or(Ok(()))
289 }
290 }
291
292 fn parent_accept_caps(&self, direction: gst::PadDirection, caps: &gst::Caps) -> bool {
293 unsafe {
294 let data = Self::type_data();
295 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
296 (*parent_class)
297 .accept_caps
298 .map(|f| {
299 from_glib(f(
300 self.obj()
301 .unsafe_cast_ref::<BaseTransform>()
302 .to_glib_none()
303 .0,
304 direction.into_glib(),
305 caps.to_glib_none().0,
306 ))
307 })
308 .unwrap_or(false)
309 }
310 }
311
312 fn parent_query(&self, direction: gst::PadDirection, query: &mut gst::QueryRef) -> bool {
313 unsafe {
314 let data = Self::type_data();
315 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
316 (*parent_class)
317 .query
318 .map(|f| {
319 from_glib(f(
320 self.obj()
321 .unsafe_cast_ref::<BaseTransform>()
322 .to_glib_none()
323 .0,
324 direction.into_glib(),
325 query.as_mut_ptr(),
326 ))
327 })
328 .unwrap_or(false)
329 }
330 }
331
332 fn parent_transform_size(
333 &self,
334 direction: gst::PadDirection,
335 caps: &gst::Caps,
336 size: usize,
337 othercaps: &gst::Caps,
338 ) -> Option<usize> {
339 unsafe {
340 let data = Self::type_data();
341 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
342 (*parent_class)
343 .transform_size
344 .map(|f| {
345 let mut othersize = mem::MaybeUninit::uninit();
346 let res: bool = from_glib(f(
347 self.obj()
348 .unsafe_cast_ref::<BaseTransform>()
349 .to_glib_none()
350 .0,
351 direction.into_glib(),
352 caps.to_glib_none().0,
353 size,
354 othercaps.to_glib_none().0,
355 othersize.as_mut_ptr(),
356 ));
357 if res {
358 Some(othersize.assume_init())
359 } else {
360 None
361 }
362 })
363 .unwrap_or(None)
364 }
365 }
366
367 fn parent_unit_size(&self, caps: &gst::Caps) -> Option<usize> {
368 unsafe {
369 let data = Self::type_data();
370 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
371 let f = (*parent_class).get_unit_size.unwrap_or_else(|| {
372 if !self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
373 unimplemented!(concat!(
374 "Missing parent function `get_unit_size`. Required because ",
375 "transform doesn't operate in-place"
376 ))
377 } else {
378 unreachable!("parent `get_unit_size` called while transform operates in-place")
379 }
380 });
381
382 let mut size = mem::MaybeUninit::uninit();
383 if from_glib(f(
384 self.obj()
385 .unsafe_cast_ref::<BaseTransform>()
386 .to_glib_none()
387 .0,
388 caps.to_glib_none().0,
389 size.as_mut_ptr(),
390 )) {
391 Some(size.assume_init())
392 } else {
393 None
394 }
395 }
396 }
397
398 fn parent_sink_event(&self, event: gst::Event) -> bool {
399 unsafe {
400 let data = Self::type_data();
401 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
402 (*parent_class)
403 .sink_event
404 .map(|f| {
405 from_glib(f(
406 self.obj()
407 .unsafe_cast_ref::<BaseTransform>()
408 .to_glib_none()
409 .0,
410 event.into_glib_ptr(),
411 ))
412 })
413 .unwrap_or(true)
414 }
415 }
416
417 fn parent_src_event(&self, event: gst::Event) -> bool {
418 unsafe {
419 let data = Self::type_data();
420 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
421 (*parent_class)
422 .src_event
423 .map(|f| {
424 from_glib(f(
425 self.obj()
426 .unsafe_cast_ref::<BaseTransform>()
427 .to_glib_none()
428 .0,
429 event.into_glib_ptr(),
430 ))
431 })
432 .unwrap_or(true)
433 }
434 }
435
436 fn parent_prepare_output_buffer(
437 &self,
438 inbuf: InputBuffer,
439 ) -> Result<PrepareOutputBufferSuccess, gst::FlowError> {
440 unsafe {
441 let data = Self::type_data();
442 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
443 let buf = match inbuf {
444 InputBuffer::Readable(inbuf_r) => inbuf_r.as_ptr(),
445 InputBuffer::Writable(inbuf_w) => inbuf_w.as_mut_ptr(),
446 };
447 (*parent_class)
448 .prepare_output_buffer
449 .map(|f| {
450 let mut outbuf: *mut gst::ffi::GstBuffer = ptr::null_mut();
451 gst::FlowSuccess::try_from_glib(f(
453 self.obj()
454 .unsafe_cast_ref::<BaseTransform>()
455 .to_glib_none()
456 .0,
457 buf as *mut gst::ffi::GstBuffer,
458 (&mut outbuf) as *mut *mut gst::ffi::GstBuffer as *mut gst::ffi::GstBuffer,
459 ))
460 .map(|_| {
461 if ptr::eq(outbuf, buf as *mut _) {
462 PrepareOutputBufferSuccess::InputBuffer
463 } else {
464 PrepareOutputBufferSuccess::Buffer(from_glib_full(outbuf))
465 }
466 })
467 .inspect_err(|_err| {
468 if !ptr::eq(outbuf, buf as *mut _) {
469 drop(Option::<gst::Buffer>::from_glib_full(outbuf));
470 }
471 })
472 })
473 .unwrap_or(Err(gst::FlowError::NotSupported))
474 }
475 }
476
477 fn parent_transform(
478 &self,
479 inbuf: &gst::Buffer,
480 outbuf: &mut gst::BufferRef,
481 ) -> Result<gst::FlowSuccess, gst::FlowError> {
482 unsafe {
483 let data = Self::type_data();
484 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
485 (*parent_class)
486 .transform
487 .map(|f| {
488 try_from_glib(f(
489 self.obj()
490 .unsafe_cast_ref::<BaseTransform>()
491 .to_glib_none()
492 .0,
493 inbuf.to_glib_none().0,
494 outbuf.as_mut_ptr(),
495 ))
496 })
497 .unwrap_or_else(|| {
498 if !self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
499 Err(gst::FlowError::NotSupported)
500 } else {
501 unreachable!("parent `transform` called while transform operates in-place");
502 }
503 })
504 }
505 }
506
507 fn parent_transform_ip(
508 &self,
509 buf: &mut gst::BufferRef,
510 ) -> Result<gst::FlowSuccess, gst::FlowError> {
511 unsafe {
512 let data = Self::type_data();
513 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
514 let f = (*parent_class).transform_ip.unwrap_or_else(|| {
515 if self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
516 panic!(concat!(
517 "Missing parent function `transform_ip`. Required because ",
518 "transform operates in-place"
519 ));
520 } else {
521 unreachable!(
522 "parent `transform` called while transform doesn't operate in-place"
523 );
524 }
525 });
526
527 try_from_glib(f(
528 self.obj()
529 .unsafe_cast_ref::<BaseTransform>()
530 .to_glib_none()
531 .0,
532 buf.as_mut_ptr() as *mut _,
533 ))
534 }
535 }
536
537 fn parent_transform_ip_passthrough(
538 &self,
539 buf: &gst::Buffer,
540 ) -> Result<gst::FlowSuccess, gst::FlowError> {
541 unsafe {
542 let data = Self::type_data();
543 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
544 let f = (*parent_class).transform_ip.unwrap_or_else(|| {
545 if self.obj().unsafe_cast_ref::<BaseTransform>().is_in_place() {
546 panic!(concat!(
547 "Missing parent function `transform_ip`. Required because ",
548 "transform operates in-place (passthrough mode)"
549 ));
550 } else {
551 unreachable!(concat!(
552 "parent `transform_ip` called ",
553 "while transform doesn't operate in-place (passthrough mode)"
554 ));
555 }
556 });
557
558 let buf: *mut gst::ffi::GstBuffer = buf.to_glib_none().0;
560 try_from_glib(f(
561 self.obj()
562 .unsafe_cast_ref::<BaseTransform>()
563 .to_glib_none()
564 .0,
565 buf as *mut _,
566 ))
567 }
568 }
569
570 fn parent_propose_allocation(
571 &self,
572 decide_query: Option<&gst::query::Allocation>,
573 query: &mut gst::query::Allocation,
574 ) -> Result<(), gst::LoggableError> {
575 unsafe {
576 let data = Self::type_data();
577 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
578 (*parent_class)
579 .propose_allocation
580 .map(|f| {
581 gst::result_from_gboolean!(
582 f(
583 self.obj()
584 .unsafe_cast_ref::<BaseTransform>()
585 .to_glib_none()
586 .0,
587 decide_query
588 .as_ref()
589 .map(|q| q.as_mut_ptr())
590 .unwrap_or(ptr::null_mut()),
591 query.as_mut_ptr(),
592 ),
593 gst::CAT_RUST,
594 "Parent function `propose_allocation` failed",
595 )
596 })
597 .unwrap_or(Ok(()))
598 }
599 }
600
601 fn parent_decide_allocation(
602 &self,
603 query: &mut gst::query::Allocation,
604 ) -> Result<(), gst::LoggableError> {
605 unsafe {
606 let data = Self::type_data();
607 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
608 (*parent_class)
609 .decide_allocation
610 .map(|f| {
611 gst::result_from_gboolean!(
612 f(
613 self.obj()
614 .unsafe_cast_ref::<BaseTransform>()
615 .to_glib_none()
616 .0,
617 query.as_mut_ptr(),
618 ),
619 gst::CAT_RUST,
620 "Parent function `decide_allocation` failed,"
621 )
622 })
623 .unwrap_or(Ok(()))
624 }
625 }
626
627 #[cfg(feature = "v1_30")]
628 #[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
629 fn parent_prepare_allocator(&self, caps: Option<&gst::Caps>) -> Result<(), gst::LoggableError> {
630 unsafe {
631 let data = Self::type_data();
632 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
633 (*parent_class)
634 .prepare_allocator
635 .map(|f| {
636 gst::result_from_gboolean!(
637 f(
638 self.obj()
639 .unsafe_cast_ref::<BaseTransform>()
640 .to_glib_none()
641 .0,
642 caps.to_glib_none().0
643 ),
644 gst::CAT_RUST,
645 "Parent function `prepare_allocator` failed",
646 )
647 })
648 .unwrap_or(Ok(()))
649 }
650 }
651
652 fn parent_copy_metadata(
653 &self,
654 inbuf: &gst::BufferRef,
655 outbuf: &mut gst::BufferRef,
656 ) -> Result<(), gst::LoggableError> {
657 unsafe {
658 let data = Self::type_data();
659 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
660 if let Some(ref f) = (*parent_class).copy_metadata {
661 gst::result_from_gboolean!(
662 f(
663 self.obj()
664 .unsafe_cast_ref::<BaseTransform>()
665 .to_glib_none()
666 .0,
667 inbuf.as_ptr() as *mut _,
668 outbuf.as_mut_ptr()
669 ),
670 gst::CAT_RUST,
671 "Parent function `copy_metadata` failed"
672 )
673 } else {
674 Ok(())
675 }
676 }
677 }
678
679 fn parent_transform_meta<'a>(
680 &self,
681 outbuf: &mut gst::BufferRef,
682 meta: gst::MetaRef<'a, gst::Meta>,
683 inbuf: &'a gst::BufferRef,
684 ) -> bool {
685 unsafe {
686 let data = Self::type_data();
687 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
688 (*parent_class)
689 .transform_meta
690 .map(|f| {
691 from_glib(f(
692 self.obj()
693 .unsafe_cast_ref::<BaseTransform>()
694 .to_glib_none()
695 .0,
696 outbuf.as_mut_ptr(),
697 meta.as_ptr() as *mut _,
698 inbuf.as_ptr() as *mut _,
699 ))
700 })
701 .unwrap_or(false)
702 }
703 }
704
705 fn parent_before_transform(&self, inbuf: &gst::BufferRef) {
706 unsafe {
707 let data = Self::type_data();
708 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
709 if let Some(ref f) = (*parent_class).before_transform {
710 f(
711 self.obj()
712 .unsafe_cast_ref::<BaseTransform>()
713 .to_glib_none()
714 .0,
715 inbuf.as_ptr() as *mut _,
716 );
717 }
718 }
719 }
720
721 fn parent_submit_input_buffer(
722 &self,
723 is_discont: bool,
724 inbuf: gst::Buffer,
725 ) -> Result<gst::FlowSuccess, gst::FlowError> {
726 unsafe {
727 let data = Self::type_data();
728 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
729 let f = (*parent_class)
730 .submit_input_buffer
731 .expect("Missing parent function `submit_input_buffer`");
732
733 try_from_glib(f(
734 self.obj()
735 .unsafe_cast_ref::<BaseTransform>()
736 .to_glib_none()
737 .0,
738 is_discont.into_glib(),
739 inbuf.into_glib_ptr(),
740 ))
741 }
742 }
743
744 fn parent_generate_output(&self) -> Result<GenerateOutputSuccess, gst::FlowError> {
745 unsafe {
746 let data = Self::type_data();
747 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseTransformClass;
748 let f = (*parent_class)
749 .generate_output
750 .expect("Missing parent function `generate_output`");
751
752 let mut outbuf = ptr::null_mut();
753 let res = gst::FlowSuccess::try_from_glib(f(
754 self.obj()
755 .unsafe_cast_ref::<BaseTransform>()
756 .to_glib_none()
757 .0,
758 &mut outbuf,
759 ));
760
761 let outbuf = Option::<gst::Buffer>::from_glib_full(outbuf);
762
763 res.map(move |res| match (res, outbuf) {
764 (crate::BASE_TRANSFORM_FLOW_DROPPED, _) => GenerateOutputSuccess::Dropped,
765 (gst::FlowSuccess::Ok, Some(outbuf)) => GenerateOutputSuccess::Buffer(outbuf),
766 _ => GenerateOutputSuccess::NoOutput,
767 })
768 }
769 }
770
771 fn take_queued_buffer(&self) -> Option<gst::Buffer>
772 where
773 Self: ObjectSubclass,
774 <Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
775 {
776 unsafe {
777 let instance = self.obj();
778 let ptr: *mut ffi::GstBaseTransform =
779 instance.unsafe_cast_ref::<BaseTransform>().to_glib_none().0;
780 let sinkpad: Borrowed<gst::Pad> = from_glib_borrow((*ptr).sinkpad);
781 let _stream_lock = sinkpad.stream_lock();
782 let buffer = (*ptr).queued_buf;
783 (*ptr).queued_buf = ptr::null_mut();
784 from_glib_full(buffer)
785 }
786 }
787
788 fn queued_buffer(&self) -> Option<gst::Buffer>
789 where
790 Self: ObjectSubclass,
791 <Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
792 {
793 unsafe {
794 let instance = self.obj();
795 let ptr: *mut ffi::GstBaseTransform =
796 instance.unsafe_cast_ref::<BaseTransform>().to_glib_none().0;
797 let sinkpad: Borrowed<gst::Pad> = from_glib_borrow((*ptr).sinkpad);
798 let _stream_lock = sinkpad.stream_lock();
799 let buffer = (*ptr).queued_buf;
800 from_glib_none(buffer)
801 }
802 }
803}
804
805impl<T: BaseTransformImpl> BaseTransformImplExt for T {}
806
807unsafe impl<T: BaseTransformImpl> IsSubclassable<T> for BaseTransform {
808 fn class_init(klass: &mut glib::Class<Self>) {
809 Self::parent_class_init::<T>(klass);
810 let klass = klass.as_mut();
811 klass.start = Some(base_transform_start::<T>);
812 klass.stop = Some(base_transform_stop::<T>);
813 klass.transform_caps = Some(base_transform_transform_caps::<T>);
814 klass.fixate_caps = Some(base_transform_fixate_caps::<T>);
815 klass.set_caps = Some(base_transform_set_caps::<T>);
816 klass.accept_caps = Some(base_transform_accept_caps::<T>);
817 klass.query = Some(base_transform_query::<T>);
818 klass.transform_size = Some(base_transform_transform_size::<T>);
819 klass.get_unit_size = Some(base_transform_get_unit_size::<T>);
820 klass.prepare_output_buffer = Some(base_transform_prepare_output_buffer::<T>);
821 klass.sink_event = Some(base_transform_sink_event::<T>);
822 klass.src_event = Some(base_transform_src_event::<T>);
823 klass.transform_meta = Some(base_transform_transform_meta::<T>);
824 klass.propose_allocation = Some(base_transform_propose_allocation::<T>);
825 klass.decide_allocation = Some(base_transform_decide_allocation::<T>);
826 klass.copy_metadata = Some(base_transform_copy_metadata::<T>);
827 klass.before_transform = Some(base_transform_before_transform::<T>);
828 klass.submit_input_buffer = Some(base_transform_submit_input_buffer::<T>);
829 klass.generate_output = Some(base_transform_generate_output::<T>);
830
831 klass.passthrough_on_same_caps = T::PASSTHROUGH_ON_SAME_CAPS.into_glib();
832 klass.transform_ip_on_passthrough = T::TRANSFORM_IP_ON_PASSTHROUGH.into_glib();
833
834 match T::MODE {
835 BaseTransformMode::AlwaysInPlace => {
836 klass.transform = None;
837 klass.transform_ip = Some(base_transform_transform_ip::<T>);
838 }
839 BaseTransformMode::NeverInPlace => {
840 klass.transform = Some(base_transform_transform::<T>);
841 klass.transform_ip = None;
842 }
843 BaseTransformMode::Both => {
844 klass.transform = Some(base_transform_transform::<T>);
845 klass.transform_ip = Some(base_transform_transform_ip::<T>);
846 }
847 }
848
849 #[cfg(feature = "v1_30")]
850 {
851 klass.prepare_allocator = Some(base_transform_prepare_allocator::<T>);
852 }
853 }
854}
855
856#[derive(Debug)]
857pub enum GenerateOutputSuccess {
858 Buffer(gst::Buffer),
859 NoOutput,
860 Dropped,
861}
862
863#[derive(Debug)]
864pub enum PrepareOutputBufferSuccess {
865 Buffer(gst::Buffer),
866 InputBuffer,
867}
868
869#[derive(Debug)]
870pub enum InputBuffer<'a> {
871 Writable(&'a mut gst::BufferRef),
872 Readable(&'a gst::BufferRef),
873}
874
875unsafe extern "C" fn base_transform_start<T: BaseTransformImpl>(
876 ptr: *mut ffi::GstBaseTransform,
877) -> glib::ffi::gboolean {
878 unsafe {
879 let instance = &*(ptr as *mut T::Instance);
880 let imp = instance.imp();
881
882 gst::panic_to_error!(imp, false, {
883 match imp.start() {
884 Ok(()) => true,
885 Err(err) => {
886 imp.post_error_message(err);
887 false
888 }
889 }
890 })
891 .into_glib()
892 }
893}
894
895unsafe extern "C" fn base_transform_stop<T: BaseTransformImpl>(
896 ptr: *mut ffi::GstBaseTransform,
897) -> glib::ffi::gboolean {
898 unsafe {
899 let instance = &*(ptr as *mut T::Instance);
900 let imp = instance.imp();
901
902 gst::panic_to_error!(imp, false, {
903 match imp.stop() {
904 Ok(()) => true,
905 Err(err) => {
906 imp.post_error_message(err);
907 false
908 }
909 }
910 })
911 .into_glib()
912 }
913}
914
915unsafe extern "C" fn base_transform_transform_caps<T: BaseTransformImpl>(
916 ptr: *mut ffi::GstBaseTransform,
917 direction: gst::ffi::GstPadDirection,
918 caps: *mut gst::ffi::GstCaps,
919 filter: *mut gst::ffi::GstCaps,
920) -> *mut gst::ffi::GstCaps {
921 unsafe {
922 let instance = &*(ptr as *mut T::Instance);
923 let imp = instance.imp();
924
925 gst::panic_to_error!(imp, None, {
926 let filter: Borrowed<Option<gst::Caps>> = from_glib_borrow(filter);
927
928 imp.transform_caps(
929 from_glib(direction),
930 &from_glib_borrow(caps),
931 filter.as_ref().as_ref(),
932 )
933 })
934 .map(|caps| caps.into_glib_ptr())
935 .unwrap_or(std::ptr::null_mut())
936 }
937}
938
939unsafe extern "C" fn base_transform_fixate_caps<T: BaseTransformImpl>(
940 ptr: *mut ffi::GstBaseTransform,
941 direction: gst::ffi::GstPadDirection,
942 caps: *mut gst::ffi::GstCaps,
943 othercaps: *mut gst::ffi::GstCaps,
944) -> *mut gst::ffi::GstCaps {
945 unsafe {
946 let instance = &*(ptr as *mut T::Instance);
947 let imp = instance.imp();
948
949 gst::panic_to_error!(imp, gst::Caps::new_empty(), {
950 imp.fixate_caps(
951 from_glib(direction),
952 &from_glib_borrow(caps),
953 from_glib_full(othercaps),
954 )
955 })
956 .into_glib_ptr()
957 }
958}
959
960unsafe extern "C" fn base_transform_set_caps<T: BaseTransformImpl>(
961 ptr: *mut ffi::GstBaseTransform,
962 incaps: *mut gst::ffi::GstCaps,
963 outcaps: *mut gst::ffi::GstCaps,
964) -> glib::ffi::gboolean {
965 unsafe {
966 let instance = &*(ptr as *mut T::Instance);
967 let imp = instance.imp();
968
969 gst::panic_to_error!(imp, false, {
970 match imp.set_caps(&from_glib_borrow(incaps), &from_glib_borrow(outcaps)) {
971 Ok(()) => true,
972 Err(err) => {
973 err.log_with_imp(imp);
974 false
975 }
976 }
977 })
978 .into_glib()
979 }
980}
981
982unsafe extern "C" fn base_transform_accept_caps<T: BaseTransformImpl>(
983 ptr: *mut ffi::GstBaseTransform,
984 direction: gst::ffi::GstPadDirection,
985 caps: *mut gst::ffi::GstCaps,
986) -> glib::ffi::gboolean {
987 unsafe {
988 let instance = &*(ptr as *mut T::Instance);
989 let imp = instance.imp();
990
991 gst::panic_to_error!(imp, false, {
992 imp.accept_caps(from_glib(direction), &from_glib_borrow(caps))
993 })
994 .into_glib()
995 }
996}
997
998unsafe extern "C" fn base_transform_query<T: BaseTransformImpl>(
999 ptr: *mut ffi::GstBaseTransform,
1000 direction: gst::ffi::GstPadDirection,
1001 query: *mut gst::ffi::GstQuery,
1002) -> glib::ffi::gboolean {
1003 unsafe {
1004 let instance = &*(ptr as *mut T::Instance);
1005 let imp = instance.imp();
1006
1007 gst::panic_to_error!(imp, false, {
1008 BaseTransformImpl::query(
1009 imp,
1010 from_glib(direction),
1011 gst::QueryRef::from_mut_ptr(query),
1012 )
1013 })
1014 .into_glib()
1015 }
1016}
1017
1018unsafe extern "C" fn base_transform_transform_size<T: BaseTransformImpl>(
1019 ptr: *mut ffi::GstBaseTransform,
1020 direction: gst::ffi::GstPadDirection,
1021 caps: *mut gst::ffi::GstCaps,
1022 size: usize,
1023 othercaps: *mut gst::ffi::GstCaps,
1024 othersize: *mut usize,
1025) -> glib::ffi::gboolean {
1026 unsafe {
1027 let instance = &*(ptr as *mut T::Instance);
1028 let imp = instance.imp();
1029
1030 gst::panic_to_error!(imp, false, {
1031 match imp.transform_size(
1032 from_glib(direction),
1033 &from_glib_borrow(caps),
1034 size,
1035 &from_glib_borrow(othercaps),
1036 ) {
1037 Some(s) => {
1038 *othersize = s;
1039 true
1040 }
1041 None => false,
1042 }
1043 })
1044 .into_glib()
1045 }
1046}
1047
1048unsafe extern "C" fn base_transform_get_unit_size<T: BaseTransformImpl>(
1049 ptr: *mut ffi::GstBaseTransform,
1050 caps: *mut gst::ffi::GstCaps,
1051 size: *mut usize,
1052) -> glib::ffi::gboolean {
1053 unsafe {
1054 let instance = &*(ptr as *mut T::Instance);
1055 let imp = instance.imp();
1056
1057 gst::panic_to_error!(imp, false, {
1058 match imp.unit_size(&from_glib_borrow(caps)) {
1059 Some(s) => {
1060 *size = s;
1061 true
1062 }
1063 None => false,
1064 }
1065 })
1066 .into_glib()
1067 }
1068}
1069
1070unsafe extern "C" fn base_transform_prepare_output_buffer<T: BaseTransformImpl>(
1071 ptr: *mut ffi::GstBaseTransform,
1072 inbuf: *mut gst::ffi::GstBuffer,
1073 outbuf: *mut gst::ffi::GstBuffer,
1074) -> gst::ffi::GstFlowReturn {
1075 unsafe {
1076 let instance = &*(ptr as *mut T::Instance);
1077 let imp = instance.imp();
1078
1079 let outbuf = outbuf as *mut *mut gst::ffi::GstBuffer;
1081 let is_passthrough: bool = from_glib(ffi::gst_base_transform_is_passthrough(ptr));
1082 let is_in_place: bool = from_glib(ffi::gst_base_transform_is_in_place(ptr));
1083 let writable = is_in_place
1084 && !is_passthrough
1085 && gst::ffi::gst_mini_object_is_writable(inbuf as *mut _) != glib::ffi::GFALSE;
1086 let buffer = match writable {
1087 false => InputBuffer::Readable(gst::BufferRef::from_ptr(inbuf)),
1088 true => InputBuffer::Writable(gst::BufferRef::from_mut_ptr(inbuf)),
1089 };
1090
1091 *outbuf = ptr::null_mut();
1092
1093 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1094 match imp.prepare_output_buffer(buffer) {
1095 Ok(PrepareOutputBufferSuccess::InputBuffer) => {
1096 assert!(
1097 is_passthrough || is_in_place,
1098 "Returning InputBuffer only allowed for passthrough or in-place mode"
1099 );
1100 *outbuf = inbuf;
1101 gst::FlowReturn::Ok
1102 }
1103 Ok(PrepareOutputBufferSuccess::Buffer(buf)) => {
1104 assert!(
1105 !is_passthrough,
1106 "Returning Buffer not allowed for passthrough mode"
1107 );
1108 *outbuf = buf.into_glib_ptr();
1109 gst::FlowReturn::Ok
1110 }
1111 Err(err) => err.into(),
1112 }
1113 })
1114 .into_glib()
1115 }
1116}
1117
1118unsafe extern "C" fn base_transform_sink_event<T: BaseTransformImpl>(
1119 ptr: *mut ffi::GstBaseTransform,
1120 event: *mut gst::ffi::GstEvent,
1121) -> glib::ffi::gboolean {
1122 unsafe {
1123 let instance = &*(ptr as *mut T::Instance);
1124 let imp = instance.imp();
1125
1126 gst::panic_to_error!(imp, false, { imp.sink_event(from_glib_full(event)) }).into_glib()
1127 }
1128}
1129
1130unsafe extern "C" fn base_transform_src_event<T: BaseTransformImpl>(
1131 ptr: *mut ffi::GstBaseTransform,
1132 event: *mut gst::ffi::GstEvent,
1133) -> glib::ffi::gboolean {
1134 unsafe {
1135 let instance = &*(ptr as *mut T::Instance);
1136 let imp = instance.imp();
1137
1138 gst::panic_to_error!(imp, false, { imp.src_event(from_glib_full(event)) }).into_glib()
1139 }
1140}
1141
1142unsafe extern "C" fn base_transform_transform<T: BaseTransformImpl>(
1143 ptr: *mut ffi::GstBaseTransform,
1144 inbuf: *mut gst::ffi::GstBuffer,
1145 outbuf: *mut gst::ffi::GstBuffer,
1146) -> gst::ffi::GstFlowReturn {
1147 unsafe {
1148 let instance = &*(ptr as *mut T::Instance);
1149 let imp = instance.imp();
1150
1151 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1152 imp.transform(
1153 &from_glib_borrow(inbuf),
1154 gst::BufferRef::from_mut_ptr(outbuf),
1155 )
1156 .into()
1157 })
1158 .into_glib()
1159 }
1160}
1161
1162unsafe extern "C" fn base_transform_transform_ip<T: BaseTransformImpl>(
1163 ptr: *mut ffi::GstBaseTransform,
1164 buf: *mut *mut gst::ffi::GstBuffer,
1165) -> gst::ffi::GstFlowReturn {
1166 unsafe {
1167 let instance = &*(ptr as *mut T::Instance);
1168 let imp = instance.imp();
1169
1170 let buf = buf as *mut gst::ffi::GstBuffer;
1172
1173 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1174 if from_glib(ffi::gst_base_transform_is_passthrough(ptr)) {
1175 imp.transform_ip_passthrough(&from_glib_borrow(buf)).into()
1176 } else {
1177 imp.transform_ip(gst::BufferRef::from_mut_ptr(buf)).into()
1178 }
1179 })
1180 .into_glib()
1181 }
1182}
1183
1184unsafe extern "C" fn base_transform_transform_meta<T: BaseTransformImpl>(
1185 ptr: *mut ffi::GstBaseTransform,
1186 outbuf: *mut gst::ffi::GstBuffer,
1187 meta: *mut gst::ffi::GstMeta,
1188 inbuf: *mut gst::ffi::GstBuffer,
1189) -> glib::ffi::gboolean {
1190 unsafe {
1191 let instance = &*(ptr as *mut T::Instance);
1192 let imp = instance.imp();
1193
1194 let inbuf = gst::BufferRef::from_ptr(inbuf);
1195
1196 gst::panic_to_error!(imp, false, {
1197 imp.transform_meta(
1198 gst::BufferRef::from_mut_ptr(outbuf),
1199 gst::Meta::from_ptr(inbuf, meta),
1200 inbuf,
1201 )
1202 })
1203 .into_glib()
1204 }
1205}
1206
1207unsafe extern "C" fn base_transform_propose_allocation<T: BaseTransformImpl>(
1208 ptr: *mut ffi::GstBaseTransform,
1209 decide_query: *mut gst::ffi::GstQuery,
1210 query: *mut gst::ffi::GstQuery,
1211) -> glib::ffi::gboolean {
1212 unsafe {
1213 let instance = &*(ptr as *mut T::Instance);
1214 let imp = instance.imp();
1215 let decide_query = if decide_query.is_null() {
1216 None
1217 } else {
1218 match gst::QueryRef::from_ptr(decide_query).view() {
1219 gst::QueryView::Allocation(allocation) => Some(allocation),
1220 _ => unreachable!(),
1221 }
1222 };
1223 let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1224 gst::QueryViewMut::Allocation(allocation) => allocation,
1225 _ => unreachable!(),
1226 };
1227
1228 gst::panic_to_error!(imp, false, {
1229 match imp.propose_allocation(decide_query, query) {
1230 Ok(()) => true,
1231 Err(err) => {
1232 err.log_with_imp_and_level(imp, gst::DebugLevel::Info);
1233 false
1234 }
1235 }
1236 })
1237 .into_glib()
1238 }
1239}
1240
1241unsafe extern "C" fn base_transform_decide_allocation<T: BaseTransformImpl>(
1242 ptr: *mut ffi::GstBaseTransform,
1243 query: *mut gst::ffi::GstQuery,
1244) -> glib::ffi::gboolean {
1245 unsafe {
1246 let instance = &*(ptr as *mut T::Instance);
1247 let imp = instance.imp();
1248 let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1249 gst::QueryViewMut::Allocation(allocation) => allocation,
1250 _ => unreachable!(),
1251 };
1252
1253 gst::panic_to_error!(imp, false, {
1254 match imp.decide_allocation(query) {
1255 Ok(()) => true,
1256 Err(err) => {
1257 err.log_with_imp(imp);
1258 false
1259 }
1260 }
1261 })
1262 .into_glib()
1263 }
1264}
1265
1266unsafe extern "C" fn base_transform_copy_metadata<T: BaseTransformImpl>(
1267 ptr: *mut ffi::GstBaseTransform,
1268 inbuf: *mut gst::ffi::GstBuffer,
1269 outbuf: *mut gst::ffi::GstBuffer,
1270) -> glib::ffi::gboolean {
1271 unsafe {
1272 let instance = &*(ptr as *mut T::Instance);
1273 let imp = instance.imp();
1274
1275 if gst::ffi::gst_mini_object_is_writable(outbuf as *mut _) == glib::ffi::GFALSE {
1276 let instance = imp.obj();
1277 let obj = instance.unsafe_cast_ref::<BaseTransform>();
1278 gst::warning!(gst::CAT_RUST, obj = obj, "buffer {:?} not writable", outbuf);
1279 return glib::ffi::GFALSE;
1280 }
1281
1282 gst::panic_to_error!(imp, true, {
1283 match imp.copy_metadata(
1284 gst::BufferRef::from_ptr(inbuf),
1285 gst::BufferRef::from_mut_ptr(outbuf),
1286 ) {
1287 Ok(_) => true,
1288 Err(err) => {
1289 err.log_with_imp(imp);
1290 false
1291 }
1292 }
1293 })
1294 .into_glib()
1295 }
1296}
1297
1298unsafe extern "C" fn base_transform_before_transform<T: BaseTransformImpl>(
1299 ptr: *mut ffi::GstBaseTransform,
1300 inbuf: *mut gst::ffi::GstBuffer,
1301) {
1302 unsafe {
1303 let instance = &*(ptr as *mut T::Instance);
1304 let imp = instance.imp();
1305
1306 gst::panic_to_error!(imp, (), {
1307 imp.before_transform(gst::BufferRef::from_ptr(inbuf));
1308 })
1309 }
1310}
1311
1312unsafe extern "C" fn base_transform_submit_input_buffer<T: BaseTransformImpl>(
1313 ptr: *mut ffi::GstBaseTransform,
1314 is_discont: glib::ffi::gboolean,
1315 buf: *mut gst::ffi::GstBuffer,
1316) -> gst::ffi::GstFlowReturn {
1317 unsafe {
1318 let instance = &*(ptr as *mut T::Instance);
1319 let imp = instance.imp();
1320
1321 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1322 imp.submit_input_buffer(from_glib(is_discont), from_glib_full(buf))
1323 .into()
1324 })
1325 .into_glib()
1326 }
1327}
1328
1329unsafe extern "C" fn base_transform_generate_output<T: BaseTransformImpl>(
1330 ptr: *mut ffi::GstBaseTransform,
1331 buf: *mut *mut gst::ffi::GstBuffer,
1332) -> gst::ffi::GstFlowReturn {
1333 unsafe {
1334 let instance = &*(ptr as *mut T::Instance);
1335 let imp = instance.imp();
1336
1337 *buf = ptr::null_mut();
1338
1339 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1340 match imp.generate_output() {
1341 Ok(GenerateOutputSuccess::Dropped) => crate::BASE_TRANSFORM_FLOW_DROPPED.into(),
1342 Ok(GenerateOutputSuccess::NoOutput) => gst::FlowReturn::Ok,
1343 Ok(GenerateOutputSuccess::Buffer(outbuf)) => {
1344 *buf = outbuf.into_glib_ptr();
1345 gst::FlowReturn::Ok
1346 }
1347 Err(err) => err.into(),
1348 }
1349 })
1350 .into_glib()
1351 }
1352}
1353
1354#[cfg(feature = "v1_30")]
1355#[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
1356unsafe extern "C" fn base_transform_prepare_allocator<T: BaseTransformImpl>(
1357 ptr: *mut ffi::GstBaseTransform,
1358 caps: *mut gst::ffi::GstCaps,
1359) -> glib::ffi::gboolean {
1360 unsafe {
1361 let instance = &*(ptr as *mut T::Instance);
1362 let imp = instance.imp();
1363 let caps = Option::<gst::Caps>::from_glib_none(caps);
1364
1365 gst::panic_to_error!(imp, false, {
1366 match imp.prepare_allocator(caps.as_ref()) {
1367 Ok(()) => true,
1368 Err(err) => {
1369 err.log_with_imp(imp);
1370 false
1371 }
1372 }
1373 })
1374 .into_glib()
1375 }
1376}
1377
1378#[cfg(test)]
1379mod tests {
1380 use super::*;
1381
1382 pub mod imp {
1383 use super::*;
1384 use std::sync::atomic::{self, AtomicBool};
1385
1386 #[derive(Default)]
1387 pub struct TestTransform {
1388 drop_next: AtomicBool,
1389 }
1390
1391 #[glib::object_subclass]
1392 impl ObjectSubclass for TestTransform {
1393 const NAME: &'static str = "TestTransform";
1394 type Type = super::TestTransform;
1395 type ParentType = crate::BaseTransform;
1396 }
1397
1398 impl ObjectImpl for TestTransform {}
1399
1400 impl GstObjectImpl for TestTransform {}
1401
1402 impl ElementImpl for TestTransform {
1403 fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
1404 static ELEMENT_METADATA: std::sync::OnceLock<gst::subclass::ElementMetadata> =
1405 std::sync::OnceLock::new();
1406
1407 Some(ELEMENT_METADATA.get_or_init(|| {
1408 gst::subclass::ElementMetadata::new(
1409 "Test Transform",
1410 "Generic",
1411 "Does nothing",
1412 "Sebastian Dröge <sebastian@centricular.com>",
1413 )
1414 }))
1415 }
1416
1417 fn pad_templates() -> &'static [gst::PadTemplate] {
1418 static PAD_TEMPLATES: std::sync::OnceLock<Vec<gst::PadTemplate>> =
1419 std::sync::OnceLock::new();
1420
1421 PAD_TEMPLATES.get_or_init(|| {
1422 let caps = gst::Caps::new_any();
1423 vec![
1424 gst::PadTemplate::new(
1425 "src",
1426 gst::PadDirection::Src,
1427 gst::PadPresence::Always,
1428 &caps,
1429 )
1430 .unwrap(),
1431 gst::PadTemplate::new(
1432 "sink",
1433 gst::PadDirection::Sink,
1434 gst::PadPresence::Always,
1435 &caps,
1436 )
1437 .unwrap(),
1438 ]
1439 })
1440 }
1441 }
1442
1443 impl BaseTransformImpl for TestTransform {
1444 const MODE: BaseTransformMode = BaseTransformMode::AlwaysInPlace;
1445
1446 const PASSTHROUGH_ON_SAME_CAPS: bool = false;
1447
1448 const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
1449
1450 fn transform_ip(
1451 &self,
1452 _buf: &mut gst::BufferRef,
1453 ) -> Result<gst::FlowSuccess, gst::FlowError> {
1454 if self.drop_next.load(atomic::Ordering::SeqCst) {
1455 self.drop_next.store(false, atomic::Ordering::SeqCst);
1456 Ok(crate::BASE_TRANSFORM_FLOW_DROPPED)
1457 } else {
1458 self.drop_next.store(true, atomic::Ordering::SeqCst);
1459 Ok(gst::FlowSuccess::Ok)
1460 }
1461 }
1462 }
1463 }
1464
1465 glib::wrapper! {
1466 pub struct TestTransform(ObjectSubclass<imp::TestTransform>) @extends crate::BaseTransform, gst::Element, gst::Object;
1467 }
1468
1469 impl TestTransform {
1470 pub fn new(name: Option<&str>) -> Self {
1471 glib::Object::builder().property("name", name).build()
1472 }
1473 }
1474
1475 #[test]
1476 fn test_transform_subclass() {
1477 gst::init().unwrap();
1478
1479 let element = TestTransform::new(Some("test"));
1480
1481 assert_eq!(element.name(), "test");
1482
1483 let pipeline = gst::Pipeline::new();
1484 let src = gst::ElementFactory::make("audiotestsrc")
1485 .property("num-buffers", 100i32)
1486 .build()
1487 .unwrap();
1488 let sink = gst::ElementFactory::make("fakesink").build().unwrap();
1489
1490 pipeline
1491 .add_many([&src, element.upcast_ref(), &sink])
1492 .unwrap();
1493 gst::Element::link_many([&src, element.upcast_ref(), &sink]).unwrap();
1494
1495 pipeline.set_state(gst::State::Playing).unwrap();
1496 let bus = pipeline.bus().unwrap();
1497
1498 let eos = bus.timed_pop_filtered(gst::ClockTime::NONE, &[gst::MessageType::Eos]);
1499 assert!(eos.is_some());
1500
1501 let stats = sink.property::<gst::Structure>("stats");
1502 assert_eq!(stats.get::<u64>("rendered").unwrap(), 50);
1503
1504 pipeline.set_state(gst::State::Null).unwrap();
1505 }
1506}