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