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