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