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