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