1use std::{mem, ptr};
4
5use glib::translate::*;
6use gst::subclass::prelude::*;
7
8use crate::{BaseTransform, ffi, prelude::*};
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 unsafe {
843 let instance = &*(ptr as *mut T::Instance);
844 let imp = instance.imp();
845
846 gst::panic_to_error!(imp, false, {
847 match imp.start() {
848 Ok(()) => true,
849 Err(err) => {
850 imp.post_error_message(err);
851 false
852 }
853 }
854 })
855 .into_glib()
856 }
857}
858
859unsafe extern "C" fn base_transform_stop<T: BaseTransformImpl>(
860 ptr: *mut ffi::GstBaseTransform,
861) -> glib::ffi::gboolean {
862 unsafe {
863 let instance = &*(ptr as *mut T::Instance);
864 let imp = instance.imp();
865
866 gst::panic_to_error!(imp, false, {
867 match imp.stop() {
868 Ok(()) => true,
869 Err(err) => {
870 imp.post_error_message(err);
871 false
872 }
873 }
874 })
875 .into_glib()
876 }
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 unsafe {
886 let instance = &*(ptr as *mut T::Instance);
887 let imp = instance.imp();
888
889 gst::panic_to_error!(imp, None, {
890 let filter: Borrowed<Option<gst::Caps>> = from_glib_borrow(filter);
891
892 imp.transform_caps(
893 from_glib(direction),
894 &from_glib_borrow(caps),
895 filter.as_ref().as_ref(),
896 )
897 })
898 .map(|caps| caps.into_glib_ptr())
899 .unwrap_or(std::ptr::null_mut())
900 }
901}
902
903unsafe extern "C" fn base_transform_fixate_caps<T: BaseTransformImpl>(
904 ptr: *mut ffi::GstBaseTransform,
905 direction: gst::ffi::GstPadDirection,
906 caps: *mut gst::ffi::GstCaps,
907 othercaps: *mut gst::ffi::GstCaps,
908) -> *mut gst::ffi::GstCaps {
909 unsafe {
910 let instance = &*(ptr as *mut T::Instance);
911 let imp = instance.imp();
912
913 gst::panic_to_error!(imp, gst::Caps::new_empty(), {
914 imp.fixate_caps(
915 from_glib(direction),
916 &from_glib_borrow(caps),
917 from_glib_full(othercaps),
918 )
919 })
920 .into_glib_ptr()
921 }
922}
923
924unsafe extern "C" fn base_transform_set_caps<T: BaseTransformImpl>(
925 ptr: *mut ffi::GstBaseTransform,
926 incaps: *mut gst::ffi::GstCaps,
927 outcaps: *mut gst::ffi::GstCaps,
928) -> glib::ffi::gboolean {
929 unsafe {
930 let instance = &*(ptr as *mut T::Instance);
931 let imp = instance.imp();
932
933 gst::panic_to_error!(imp, false, {
934 match imp.set_caps(&from_glib_borrow(incaps), &from_glib_borrow(outcaps)) {
935 Ok(()) => true,
936 Err(err) => {
937 err.log_with_imp(imp);
938 false
939 }
940 }
941 })
942 .into_glib()
943 }
944}
945
946unsafe extern "C" fn base_transform_accept_caps<T: BaseTransformImpl>(
947 ptr: *mut ffi::GstBaseTransform,
948 direction: gst::ffi::GstPadDirection,
949 caps: *mut gst::ffi::GstCaps,
950) -> glib::ffi::gboolean {
951 unsafe {
952 let instance = &*(ptr as *mut T::Instance);
953 let imp = instance.imp();
954
955 gst::panic_to_error!(imp, false, {
956 imp.accept_caps(from_glib(direction), &from_glib_borrow(caps))
957 })
958 .into_glib()
959 }
960}
961
962unsafe extern "C" fn base_transform_query<T: BaseTransformImpl>(
963 ptr: *mut ffi::GstBaseTransform,
964 direction: gst::ffi::GstPadDirection,
965 query: *mut gst::ffi::GstQuery,
966) -> glib::ffi::gboolean {
967 unsafe {
968 let instance = &*(ptr as *mut T::Instance);
969 let imp = instance.imp();
970
971 gst::panic_to_error!(imp, false, {
972 BaseTransformImpl::query(
973 imp,
974 from_glib(direction),
975 gst::QueryRef::from_mut_ptr(query),
976 )
977 })
978 .into_glib()
979 }
980}
981
982unsafe extern "C" fn base_transform_transform_size<T: BaseTransformImpl>(
983 ptr: *mut ffi::GstBaseTransform,
984 direction: gst::ffi::GstPadDirection,
985 caps: *mut gst::ffi::GstCaps,
986 size: usize,
987 othercaps: *mut gst::ffi::GstCaps,
988 othersize: *mut usize,
989) -> glib::ffi::gboolean {
990 unsafe {
991 let instance = &*(ptr as *mut T::Instance);
992 let imp = instance.imp();
993
994 gst::panic_to_error!(imp, false, {
995 match imp.transform_size(
996 from_glib(direction),
997 &from_glib_borrow(caps),
998 size,
999 &from_glib_borrow(othercaps),
1000 ) {
1001 Some(s) => {
1002 *othersize = s;
1003 true
1004 }
1005 None => false,
1006 }
1007 })
1008 .into_glib()
1009 }
1010}
1011
1012unsafe extern "C" fn base_transform_get_unit_size<T: BaseTransformImpl>(
1013 ptr: *mut ffi::GstBaseTransform,
1014 caps: *mut gst::ffi::GstCaps,
1015 size: *mut usize,
1016) -> glib::ffi::gboolean {
1017 unsafe {
1018 let instance = &*(ptr as *mut T::Instance);
1019 let imp = instance.imp();
1020
1021 gst::panic_to_error!(imp, false, {
1022 match imp.unit_size(&from_glib_borrow(caps)) {
1023 Some(s) => {
1024 *size = s;
1025 true
1026 }
1027 None => false,
1028 }
1029 })
1030 .into_glib()
1031 }
1032}
1033
1034unsafe extern "C" fn base_transform_prepare_output_buffer<T: BaseTransformImpl>(
1035 ptr: *mut ffi::GstBaseTransform,
1036 inbuf: *mut gst::ffi::GstBuffer,
1037 outbuf: *mut gst::ffi::GstBuffer,
1038) -> gst::ffi::GstFlowReturn {
1039 unsafe {
1040 let instance = &*(ptr as *mut T::Instance);
1041 let imp = instance.imp();
1042
1043 let outbuf = outbuf as *mut *mut gst::ffi::GstBuffer;
1045 let is_passthrough: bool = from_glib(ffi::gst_base_transform_is_passthrough(ptr));
1046 let is_in_place: bool = from_glib(ffi::gst_base_transform_is_in_place(ptr));
1047 let writable = is_in_place
1048 && !is_passthrough
1049 && gst::ffi::gst_mini_object_is_writable(inbuf as *mut _) != glib::ffi::GFALSE;
1050 let buffer = match writable {
1051 false => InputBuffer::Readable(gst::BufferRef::from_ptr(inbuf)),
1052 true => InputBuffer::Writable(gst::BufferRef::from_mut_ptr(inbuf)),
1053 };
1054
1055 *outbuf = ptr::null_mut();
1056
1057 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1058 match imp.prepare_output_buffer(buffer) {
1059 Ok(PrepareOutputBufferSuccess::InputBuffer) => {
1060 assert!(
1061 is_passthrough || is_in_place,
1062 "Returning InputBuffer only allowed for passthrough or in-place mode"
1063 );
1064 *outbuf = inbuf;
1065 gst::FlowReturn::Ok
1066 }
1067 Ok(PrepareOutputBufferSuccess::Buffer(buf)) => {
1068 assert!(
1069 !is_passthrough,
1070 "Returning Buffer not allowed for passthrough mode"
1071 );
1072 *outbuf = buf.into_glib_ptr();
1073 gst::FlowReturn::Ok
1074 }
1075 Err(err) => err.into(),
1076 }
1077 })
1078 .into_glib()
1079 }
1080}
1081
1082unsafe extern "C" fn base_transform_sink_event<T: BaseTransformImpl>(
1083 ptr: *mut ffi::GstBaseTransform,
1084 event: *mut gst::ffi::GstEvent,
1085) -> glib::ffi::gboolean {
1086 unsafe {
1087 let instance = &*(ptr as *mut T::Instance);
1088 let imp = instance.imp();
1089
1090 gst::panic_to_error!(imp, false, { imp.sink_event(from_glib_full(event)) }).into_glib()
1091 }
1092}
1093
1094unsafe extern "C" fn base_transform_src_event<T: BaseTransformImpl>(
1095 ptr: *mut ffi::GstBaseTransform,
1096 event: *mut gst::ffi::GstEvent,
1097) -> glib::ffi::gboolean {
1098 unsafe {
1099 let instance = &*(ptr as *mut T::Instance);
1100 let imp = instance.imp();
1101
1102 gst::panic_to_error!(imp, false, { imp.src_event(from_glib_full(event)) }).into_glib()
1103 }
1104}
1105
1106unsafe extern "C" fn base_transform_transform<T: BaseTransformImpl>(
1107 ptr: *mut ffi::GstBaseTransform,
1108 inbuf: *mut gst::ffi::GstBuffer,
1109 outbuf: *mut gst::ffi::GstBuffer,
1110) -> gst::ffi::GstFlowReturn {
1111 unsafe {
1112 let instance = &*(ptr as *mut T::Instance);
1113 let imp = instance.imp();
1114
1115 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1116 imp.transform(
1117 &from_glib_borrow(inbuf),
1118 gst::BufferRef::from_mut_ptr(outbuf),
1119 )
1120 .into()
1121 })
1122 .into_glib()
1123 }
1124}
1125
1126unsafe extern "C" fn base_transform_transform_ip<T: BaseTransformImpl>(
1127 ptr: *mut ffi::GstBaseTransform,
1128 buf: *mut *mut gst::ffi::GstBuffer,
1129) -> gst::ffi::GstFlowReturn {
1130 unsafe {
1131 let instance = &*(ptr as *mut T::Instance);
1132 let imp = instance.imp();
1133
1134 let buf = buf as *mut gst::ffi::GstBuffer;
1136
1137 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1138 if from_glib(ffi::gst_base_transform_is_passthrough(ptr)) {
1139 imp.transform_ip_passthrough(&from_glib_borrow(buf)).into()
1140 } else {
1141 imp.transform_ip(gst::BufferRef::from_mut_ptr(buf)).into()
1142 }
1143 })
1144 .into_glib()
1145 }
1146}
1147
1148unsafe extern "C" fn base_transform_transform_meta<T: BaseTransformImpl>(
1149 ptr: *mut ffi::GstBaseTransform,
1150 outbuf: *mut gst::ffi::GstBuffer,
1151 meta: *mut gst::ffi::GstMeta,
1152 inbuf: *mut gst::ffi::GstBuffer,
1153) -> glib::ffi::gboolean {
1154 unsafe {
1155 let instance = &*(ptr as *mut T::Instance);
1156 let imp = instance.imp();
1157
1158 let inbuf = gst::BufferRef::from_ptr(inbuf);
1159
1160 gst::panic_to_error!(imp, false, {
1161 imp.transform_meta(
1162 gst::BufferRef::from_mut_ptr(outbuf),
1163 gst::Meta::from_ptr(inbuf, meta),
1164 inbuf,
1165 )
1166 })
1167 .into_glib()
1168 }
1169}
1170
1171unsafe extern "C" fn base_transform_propose_allocation<T: BaseTransformImpl>(
1172 ptr: *mut ffi::GstBaseTransform,
1173 decide_query: *mut gst::ffi::GstQuery,
1174 query: *mut gst::ffi::GstQuery,
1175) -> glib::ffi::gboolean {
1176 unsafe {
1177 let instance = &*(ptr as *mut T::Instance);
1178 let imp = instance.imp();
1179 let decide_query = if decide_query.is_null() {
1180 None
1181 } else {
1182 match gst::QueryRef::from_ptr(decide_query).view() {
1183 gst::QueryView::Allocation(allocation) => Some(allocation),
1184 _ => unreachable!(),
1185 }
1186 };
1187 let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1188 gst::QueryViewMut::Allocation(allocation) => allocation,
1189 _ => unreachable!(),
1190 };
1191
1192 gst::panic_to_error!(imp, false, {
1193 match imp.propose_allocation(decide_query, query) {
1194 Ok(()) => true,
1195 Err(err) => {
1196 err.log_with_imp_and_level(imp, gst::DebugLevel::Info);
1197 false
1198 }
1199 }
1200 })
1201 .into_glib()
1202 }
1203}
1204
1205unsafe extern "C" fn base_transform_decide_allocation<T: BaseTransformImpl>(
1206 ptr: *mut ffi::GstBaseTransform,
1207 query: *mut gst::ffi::GstQuery,
1208) -> glib::ffi::gboolean {
1209 unsafe {
1210 let instance = &*(ptr as *mut T::Instance);
1211 let imp = instance.imp();
1212 let query = match gst::QueryRef::from_mut_ptr(query).view_mut() {
1213 gst::QueryViewMut::Allocation(allocation) => allocation,
1214 _ => unreachable!(),
1215 };
1216
1217 gst::panic_to_error!(imp, false, {
1218 match imp.decide_allocation(query) {
1219 Ok(()) => true,
1220 Err(err) => {
1221 err.log_with_imp(imp);
1222 false
1223 }
1224 }
1225 })
1226 .into_glib()
1227 }
1228}
1229
1230unsafe extern "C" fn base_transform_copy_metadata<T: BaseTransformImpl>(
1231 ptr: *mut ffi::GstBaseTransform,
1232 inbuf: *mut gst::ffi::GstBuffer,
1233 outbuf: *mut gst::ffi::GstBuffer,
1234) -> glib::ffi::gboolean {
1235 unsafe {
1236 let instance = &*(ptr as *mut T::Instance);
1237 let imp = instance.imp();
1238
1239 if gst::ffi::gst_mini_object_is_writable(outbuf as *mut _) == glib::ffi::GFALSE {
1240 let instance = imp.obj();
1241 let obj = instance.unsafe_cast_ref::<BaseTransform>();
1242 gst::warning!(gst::CAT_RUST, obj = obj, "buffer {:?} not writable", outbuf);
1243 return glib::ffi::GFALSE;
1244 }
1245
1246 gst::panic_to_error!(imp, true, {
1247 match imp.copy_metadata(
1248 gst::BufferRef::from_ptr(inbuf),
1249 gst::BufferRef::from_mut_ptr(outbuf),
1250 ) {
1251 Ok(_) => true,
1252 Err(err) => {
1253 err.log_with_imp(imp);
1254 false
1255 }
1256 }
1257 })
1258 .into_glib()
1259 }
1260}
1261
1262unsafe extern "C" fn base_transform_before_transform<T: BaseTransformImpl>(
1263 ptr: *mut ffi::GstBaseTransform,
1264 inbuf: *mut gst::ffi::GstBuffer,
1265) {
1266 unsafe {
1267 let instance = &*(ptr as *mut T::Instance);
1268 let imp = instance.imp();
1269
1270 gst::panic_to_error!(imp, (), {
1271 imp.before_transform(gst::BufferRef::from_ptr(inbuf));
1272 })
1273 }
1274}
1275
1276unsafe extern "C" fn base_transform_submit_input_buffer<T: BaseTransformImpl>(
1277 ptr: *mut ffi::GstBaseTransform,
1278 is_discont: glib::ffi::gboolean,
1279 buf: *mut gst::ffi::GstBuffer,
1280) -> gst::ffi::GstFlowReturn {
1281 unsafe {
1282 let instance = &*(ptr as *mut T::Instance);
1283 let imp = instance.imp();
1284
1285 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1286 imp.submit_input_buffer(from_glib(is_discont), from_glib_full(buf))
1287 .into()
1288 })
1289 .into_glib()
1290 }
1291}
1292
1293unsafe extern "C" fn base_transform_generate_output<T: BaseTransformImpl>(
1294 ptr: *mut ffi::GstBaseTransform,
1295 buf: *mut *mut gst::ffi::GstBuffer,
1296) -> gst::ffi::GstFlowReturn {
1297 unsafe {
1298 let instance = &*(ptr as *mut T::Instance);
1299 let imp = instance.imp();
1300
1301 *buf = ptr::null_mut();
1302
1303 gst::panic_to_error!(imp, gst::FlowReturn::Error, {
1304 match imp.generate_output() {
1305 Ok(GenerateOutputSuccess::Dropped) => crate::BASE_TRANSFORM_FLOW_DROPPED.into(),
1306 Ok(GenerateOutputSuccess::NoOutput) => gst::FlowReturn::Ok,
1307 Ok(GenerateOutputSuccess::Buffer(outbuf)) => {
1308 *buf = outbuf.into_glib_ptr();
1309 gst::FlowReturn::Ok
1310 }
1311 Err(err) => err.into(),
1312 }
1313 })
1314 .into_glib()
1315 }
1316}
1317
1318#[cfg(test)]
1319mod tests {
1320 use super::*;
1321
1322 pub mod imp {
1323 use super::*;
1324 use std::sync::atomic::{self, AtomicBool};
1325
1326 #[derive(Default)]
1327 pub struct TestTransform {
1328 drop_next: AtomicBool,
1329 }
1330
1331 #[glib::object_subclass]
1332 impl ObjectSubclass for TestTransform {
1333 const NAME: &'static str = "TestTransform";
1334 type Type = super::TestTransform;
1335 type ParentType = crate::BaseTransform;
1336 }
1337
1338 impl ObjectImpl for TestTransform {}
1339
1340 impl GstObjectImpl for TestTransform {}
1341
1342 impl ElementImpl for TestTransform {
1343 fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
1344 static ELEMENT_METADATA: std::sync::OnceLock<gst::subclass::ElementMetadata> =
1345 std::sync::OnceLock::new();
1346
1347 Some(ELEMENT_METADATA.get_or_init(|| {
1348 gst::subclass::ElementMetadata::new(
1349 "Test Transform",
1350 "Generic",
1351 "Does nothing",
1352 "Sebastian Dröge <sebastian@centricular.com>",
1353 )
1354 }))
1355 }
1356
1357 fn pad_templates() -> &'static [gst::PadTemplate] {
1358 static PAD_TEMPLATES: std::sync::OnceLock<Vec<gst::PadTemplate>> =
1359 std::sync::OnceLock::new();
1360
1361 PAD_TEMPLATES.get_or_init(|| {
1362 let caps = gst::Caps::new_any();
1363 vec![
1364 gst::PadTemplate::new(
1365 "src",
1366 gst::PadDirection::Src,
1367 gst::PadPresence::Always,
1368 &caps,
1369 )
1370 .unwrap(),
1371 gst::PadTemplate::new(
1372 "sink",
1373 gst::PadDirection::Sink,
1374 gst::PadPresence::Always,
1375 &caps,
1376 )
1377 .unwrap(),
1378 ]
1379 })
1380 }
1381 }
1382
1383 impl BaseTransformImpl for TestTransform {
1384 const MODE: BaseTransformMode = BaseTransformMode::AlwaysInPlace;
1385
1386 const PASSTHROUGH_ON_SAME_CAPS: bool = false;
1387
1388 const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
1389
1390 fn transform_ip(
1391 &self,
1392 _buf: &mut gst::BufferRef,
1393 ) -> Result<gst::FlowSuccess, gst::FlowError> {
1394 if self.drop_next.load(atomic::Ordering::SeqCst) {
1395 self.drop_next.store(false, atomic::Ordering::SeqCst);
1396 Ok(crate::BASE_TRANSFORM_FLOW_DROPPED)
1397 } else {
1398 self.drop_next.store(true, atomic::Ordering::SeqCst);
1399 Ok(gst::FlowSuccess::Ok)
1400 }
1401 }
1402 }
1403 }
1404
1405 glib::wrapper! {
1406 pub struct TestTransform(ObjectSubclass<imp::TestTransform>) @extends crate::BaseTransform, gst::Element, gst::Object;
1407 }
1408
1409 impl TestTransform {
1410 pub fn new(name: Option<&str>) -> Self {
1411 glib::Object::builder().property("name", name).build()
1412 }
1413 }
1414
1415 #[test]
1416 fn test_transform_subclass() {
1417 gst::init().unwrap();
1418
1419 let element = TestTransform::new(Some("test"));
1420
1421 assert_eq!(element.name(), "test");
1422
1423 let pipeline = gst::Pipeline::new();
1424 let src = gst::ElementFactory::make("audiotestsrc")
1425 .property("num-buffers", 100i32)
1426 .build()
1427 .unwrap();
1428 let sink = gst::ElementFactory::make("fakesink").build().unwrap();
1429
1430 pipeline
1431 .add_many([&src, element.upcast_ref(), &sink])
1432 .unwrap();
1433 gst::Element::link_many([&src, element.upcast_ref(), &sink]).unwrap();
1434
1435 pipeline.set_state(gst::State::Playing).unwrap();
1436 let bus = pipeline.bus().unwrap();
1437
1438 let eos = bus.timed_pop_filtered(gst::ClockTime::NONE, &[gst::MessageType::Eos]);
1439 assert!(eos.is_some());
1440
1441 let stats = sink.property::<gst::Structure>("stats");
1442 assert_eq!(stats.get::<u64>("rendered").unwrap(), 50);
1443
1444 pipeline.set_state(gst::State::Null).unwrap();
1445 }
1446}