1use std::mem;
4
5use glib::translate::*;
6use gst::subclass::prelude::*;
7
8use crate::{BaseParse, BaseParseFrame, BaseParseFrameFlags, ffi, prelude::*};
9
10pub trait BaseParseImpl: ElementImpl + ObjectSubclass<Type: IsA<BaseParse>> {
11 const USE_DETECT: bool = false;
14
15 fn start(&self) -> Result<(), gst::ErrorMessage> {
16 self.parent_start()
17 }
18
19 fn stop(&self) -> Result<(), gst::ErrorMessage> {
20 self.parent_stop()
21 }
22
23 fn sink_caps(&self, filter: Option<&gst::Caps>) -> gst::Caps {
24 self.parent_sink_caps(filter)
25 }
26
27 fn set_sink_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
28 self.parent_set_sink_caps(caps)
29 }
30
31 fn handle_frame(
32 &self,
33 frame: BaseParseFrame,
34 ) -> Result<(gst::FlowSuccess, u32), gst::FlowError> {
35 self.parent_handle_frame(frame)
36 }
37
38 fn convert(
39 &self,
40 src_val: impl gst::format::FormattedValue,
41 dest_format: gst::Format,
42 ) -> Option<gst::GenericFormattedValue> {
43 self.parent_convert(src_val, dest_format)
44 }
45
46 fn sink_event(&self, event: gst::Event) -> bool {
47 self.parent_sink_event(event)
48 }
49
50 fn src_event(&self, event: gst::Event) -> bool {
51 self.parent_src_event(event)
52 }
53
54 fn sink_query(&self, query: &mut gst::QueryRef) -> bool {
55 self.parent_sink_query(query)
56 }
57
58 fn src_query(&self, query: &mut gst::QueryRef) -> bool {
59 self.parent_src_query(query)
60 }
61
62 fn pre_push_frame(
63 &self,
64 frame: &mut BaseParseFrame,
65 ) -> Result<gst::FlowSuccess, gst::FlowError> {
66 self.parent_pre_push_frame(frame)
67 }
68
69 fn detect(&self, buffer: &gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
74 self.parent_detect(buffer)
75 }
76}
77
78pub trait BaseParseImplExt: BaseParseImpl {
79 fn parent_start(&self) -> Result<(), gst::ErrorMessage> {
80 unsafe {
81 let data = Self::type_data();
82 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
83 (*parent_class)
84 .start
85 .map(|f| {
86 if from_glib(f(self
87 .obj()
88 .unsafe_cast_ref::<BaseParse>()
89 .to_glib_none()
90 .0))
91 {
92 Ok(())
93 } else {
94 Err(gst::error_msg!(
95 gst::CoreError::StateChange,
96 ["Parent function `start` failed"]
97 ))
98 }
99 })
100 .unwrap_or(Ok(()))
101 }
102 }
103
104 fn parent_stop(&self) -> Result<(), gst::ErrorMessage> {
105 unsafe {
106 let data = Self::type_data();
107 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
108 (*parent_class)
109 .stop
110 .map(|f| {
111 if from_glib(f(self
112 .obj()
113 .unsafe_cast_ref::<BaseParse>()
114 .to_glib_none()
115 .0))
116 {
117 Ok(())
118 } else {
119 Err(gst::error_msg!(
120 gst::CoreError::StateChange,
121 ["Parent function `stop` failed"]
122 ))
123 }
124 })
125 .unwrap_or(Ok(()))
126 }
127 }
128
129 fn parent_sink_caps(&self, filter: Option<&gst::Caps>) -> gst::Caps {
130 unsafe {
131 let data = Self::type_data();
132 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
133 if let Some(f) = (*parent_class).get_sink_caps {
134 from_glib_full(f(
135 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
136 filter.to_glib_none().0,
137 ))
138 } else {
139 let templ_caps = self.obj().sink_pad().pad_template_caps();
140 if let Some(filter) = filter {
141 filter.intersect_with_mode(&templ_caps, gst::CapsIntersectMode::First)
142 } else {
143 templ_caps
144 }
145 }
146 }
147 }
148
149 fn parent_set_sink_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
150 unsafe {
151 let data = Self::type_data();
152 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
153 (*parent_class)
154 .set_sink_caps
155 .map(|f| {
156 gst::result_from_gboolean!(
157 f(
158 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
159 caps.to_glib_none().0,
160 ),
161 gst::CAT_RUST,
162 "Parent function `set_sink_caps` failed",
163 )
164 })
165 .unwrap_or(Ok(()))
166 }
167 }
168
169 fn parent_handle_frame(
170 &self,
171 frame: BaseParseFrame,
172 ) -> Result<(gst::FlowSuccess, u32), gst::FlowError> {
173 unsafe {
174 let data = Self::type_data();
175 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
176 let mut skipsize = 0;
177 (*parent_class)
178 .handle_frame
179 .map(|f| {
180 let res = try_from_glib(f(
181 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
182 frame.to_glib_none().0,
183 &mut skipsize,
184 ));
185 (res.unwrap(), skipsize as u32)
186 })
187 .ok_or(gst::FlowError::Error)
188 }
189 }
190
191 fn parent_convert(
192 &self,
193 src_val: impl gst::format::FormattedValue,
194 dest_format: gst::Format,
195 ) -> Option<gst::GenericFormattedValue> {
196 unsafe {
197 let data = Self::type_data();
198 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
199 let res = (*parent_class).convert.map(|f| {
200 let mut dest_val = mem::MaybeUninit::uninit();
201
202 let res = from_glib(f(
203 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
204 src_val.format().into_glib(),
205 src_val.into_raw_value(),
206 dest_format.into_glib(),
207 dest_val.as_mut_ptr(),
208 ));
209 (res, dest_val)
210 });
211
212 match res {
213 Some((true, dest_val)) => Some(gst::GenericFormattedValue::new(
214 dest_format,
215 dest_val.assume_init(),
216 )),
217 _ => None,
218 }
219 }
220 }
221
222 fn parent_sink_event(&self, event: gst::Event) -> bool {
223 unsafe {
224 let data = Self::type_data();
225 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
226 (*parent_class)
227 .sink_event
228 .map(|f| {
229 from_glib(f(
230 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
231 event.into_glib_ptr(),
232 ))
233 })
234 .unwrap_or(true)
235 }
236 }
237
238 fn parent_src_event(&self, event: gst::Event) -> bool {
239 unsafe {
240 let data = Self::type_data();
241 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
242 (*parent_class)
243 .src_event
244 .map(|f| {
245 from_glib(f(
246 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
247 event.into_glib_ptr(),
248 ))
249 })
250 .unwrap_or(true)
251 }
252 }
253
254 fn parent_sink_query(&self, query: &mut gst::QueryRef) -> bool {
255 unsafe {
256 let data = Self::type_data();
257 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
258 (*parent_class)
259 .sink_query
260 .map(|f| {
261 from_glib(f(
262 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
263 query.as_mut_ptr(),
264 ))
265 })
266 .unwrap_or(false)
267 }
268 }
269
270 fn parent_src_query(&self, query: &mut gst::QueryRef) -> bool {
271 unsafe {
272 let data = Self::type_data();
273 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
274 (*parent_class)
275 .src_query
276 .map(|f| {
277 from_glib(f(
278 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
279 query.as_mut_ptr(),
280 ))
281 })
282 .unwrap_or(false)
283 }
284 }
285
286 fn parent_pre_push_frame(
287 &self,
288 frame: &mut BaseParseFrame,
289 ) -> Result<gst::FlowSuccess, gst::FlowError> {
290 unsafe {
291 let data = Self::type_data();
292 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
293 match (*parent_class).pre_push_frame {
294 Some(f) => gst::FlowSuccess::try_from_glib(f(
295 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
296 frame.to_glib_none().0,
297 )),
298 None => {
301 frame.set_flags(BaseParseFrameFlags::CLIP);
302 Ok(gst::FlowSuccess::Ok)
303 }
304 }
305 }
306 }
307
308 fn parent_detect(&self, buffer: &gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
309 unsafe {
310 let data = Self::type_data();
311 let parent_class = data.as_ref().parent_class() as *mut ffi::GstBaseParseClass;
312 (*parent_class)
313 .detect
314 .map(|f| {
315 gst::FlowSuccess::try_from_glib(f(
316 self.obj().unsafe_cast_ref::<BaseParse>().to_glib_none().0,
317 buffer.as_ptr() as *mut _,
318 ))
319 })
320 .unwrap_or(Ok(gst::FlowSuccess::Ok))
321 }
322 }
323}
324
325impl<T: BaseParseImpl> BaseParseImplExt for T {}
326
327unsafe impl<T: BaseParseImpl> IsSubclassable<T> for BaseParse {
328 fn class_init(klass: &mut glib::Class<Self>) {
329 Self::parent_class_init::<T>(klass);
330 let klass = klass.as_mut();
331 klass.start = Some(base_parse_start::<T>);
332 klass.stop = Some(base_parse_stop::<T>);
333 klass.set_sink_caps = Some(base_parse_set_sink_caps::<T>);
334 klass.get_sink_caps = Some(base_parse_get_sink_caps::<T>);
335 klass.handle_frame = Some(base_parse_handle_frame::<T>);
336 klass.convert = Some(base_parse_convert::<T>);
337 klass.sink_event = Some(base_parse_sink_event::<T>);
338 klass.src_event = Some(base_parse_src_event::<T>);
339 klass.sink_query = Some(base_parse_sink_query::<T>);
340 klass.src_query = Some(base_parse_src_query::<T>);
341 klass.pre_push_frame = Some(base_parse_pre_push_frame::<T>);
342 if T::USE_DETECT {
343 klass.detect = Some(base_parse_detect::<T>);
344 }
345 }
346}
347
348unsafe extern "C" fn base_parse_start<T: BaseParseImpl>(
349 ptr: *mut ffi::GstBaseParse,
350) -> glib::ffi::gboolean {
351 unsafe {
352 let instance = &*(ptr as *mut T::Instance);
353 let imp = instance.imp();
354
355 gst::panic_to_error!(imp, false, {
356 match imp.start() {
357 Ok(()) => true,
358 Err(err) => {
359 imp.post_error_message(err);
360 false
361 }
362 }
363 })
364 .into_glib()
365 }
366}
367
368unsafe extern "C" fn base_parse_stop<T: BaseParseImpl>(
369 ptr: *mut ffi::GstBaseParse,
370) -> glib::ffi::gboolean {
371 unsafe {
372 let instance = &*(ptr as *mut T::Instance);
373 let imp = instance.imp();
374
375 gst::panic_to_error!(imp, false, {
376 match imp.stop() {
377 Ok(()) => true,
378 Err(err) => {
379 imp.post_error_message(err);
380 false
381 }
382 }
383 })
384 .into_glib()
385 }
386}
387
388unsafe extern "C" fn base_parse_set_sink_caps<T: BaseParseImpl>(
389 ptr: *mut ffi::GstBaseParse,
390 caps: *mut gst::ffi::GstCaps,
391) -> glib::ffi::gboolean {
392 unsafe {
393 let instance = &*(ptr as *mut T::Instance);
394 let imp = instance.imp();
395 let caps: Borrowed<gst::Caps> = from_glib_borrow(caps);
396
397 gst::panic_to_error!(imp, false, {
398 match imp.set_sink_caps(&caps) {
399 Ok(()) => true,
400 Err(err) => {
401 err.log_with_imp(imp);
402 false
403 }
404 }
405 })
406 .into_glib()
407 }
408}
409
410unsafe extern "C" fn base_parse_get_sink_caps<T: BaseParseImpl>(
411 ptr: *mut ffi::GstBaseParse,
412 filter: *mut gst::ffi::GstCaps,
413) -> *mut gst::ffi::GstCaps {
414 unsafe {
415 let instance = &*(ptr as *mut T::Instance);
416 let imp = instance.imp();
417 let filter: Borrowed<Option<gst::Caps>> = from_glib_borrow(filter);
418
419 gst::panic_to_error!(imp, gst::Caps::new_empty(), {
420 imp.sink_caps(filter.as_ref().as_ref())
421 })
422 .into_glib_ptr()
423 }
424}
425
426unsafe extern "C" fn base_parse_handle_frame<T: BaseParseImpl>(
427 ptr: *mut ffi::GstBaseParse,
428 frame: *mut ffi::GstBaseParseFrame,
429 skipsize: *mut i32,
430) -> gst::ffi::GstFlowReturn {
431 unsafe {
432 let instance = &*(ptr as *mut T::Instance);
433 let imp = instance.imp();
434 let instance = imp.obj();
435 let instance = instance.unsafe_cast_ref::<BaseParse>();
436 let wrap_frame = BaseParseFrame::new(frame, instance);
437
438 let res = gst::panic_to_error!(imp, Err(gst::FlowError::Error), {
439 imp.handle_frame(wrap_frame)
440 });
441
442 match res {
443 Ok((flow, skip)) => {
444 *skipsize = i32::try_from(skip).expect("skip is higher than i32::MAX");
445 gst::FlowReturn::from_ok(flow)
446 }
447 Err(flow) => gst::FlowReturn::from_error(flow),
448 }
449 .into_glib()
450 }
451}
452
453unsafe extern "C" fn base_parse_convert<T: BaseParseImpl>(
454 ptr: *mut ffi::GstBaseParse,
455 source_format: gst::ffi::GstFormat,
456 source_value: i64,
457 dest_format: gst::ffi::GstFormat,
458 dest_value: *mut i64,
459) -> glib::ffi::gboolean {
460 unsafe {
461 let instance = &*(ptr as *mut T::Instance);
462 let imp = instance.imp();
463 let source = gst::GenericFormattedValue::new(from_glib(source_format), source_value);
464
465 let res = gst::panic_to_error!(imp, None, { imp.convert(source, from_glib(dest_format)) });
466
467 match res {
468 Some(dest) => {
469 *dest_value = dest.into_raw_value();
470 true
471 }
472 _ => false,
473 }
474 .into_glib()
475 }
476}
477
478unsafe extern "C" fn base_parse_sink_event<T: BaseParseImpl>(
479 ptr: *mut ffi::GstBaseParse,
480 event: *mut gst::ffi::GstEvent,
481) -> glib::ffi::gboolean {
482 unsafe {
483 let instance = &*(ptr as *mut T::Instance);
484 let imp = instance.imp();
485
486 gst::panic_to_error!(imp, false, { imp.sink_event(from_glib_full(event)) }).into_glib()
487 }
488}
489
490unsafe extern "C" fn base_parse_src_event<T: BaseParseImpl>(
491 ptr: *mut ffi::GstBaseParse,
492 event: *mut gst::ffi::GstEvent,
493) -> glib::ffi::gboolean {
494 unsafe {
495 let instance = &*(ptr as *mut T::Instance);
496 let imp = instance.imp();
497
498 gst::panic_to_error!(imp, false, { imp.src_event(from_glib_full(event)) }).into_glib()
499 }
500}
501
502unsafe extern "C" fn base_parse_sink_query<T: BaseParseImpl>(
503 ptr: *mut ffi::GstBaseParse,
504 query_ptr: *mut gst::ffi::GstQuery,
505) -> glib::ffi::gboolean {
506 unsafe {
507 let instance = &*(ptr as *mut T::Instance);
508 let imp = instance.imp();
509 let query = gst::QueryRef::from_mut_ptr(query_ptr);
510
511 gst::panic_to_error!(imp, false, { imp.sink_query(query) }).into_glib()
512 }
513}
514
515unsafe extern "C" fn base_parse_src_query<T: BaseParseImpl>(
516 ptr: *mut ffi::GstBaseParse,
517 query_ptr: *mut gst::ffi::GstQuery,
518) -> glib::ffi::gboolean {
519 unsafe {
520 let instance = &*(ptr as *mut T::Instance);
521 let imp = instance.imp();
522 let query = gst::QueryRef::from_mut_ptr(query_ptr);
523
524 gst::panic_to_error!(imp, false, { imp.src_query(query) }).into_glib()
525 }
526}
527
528unsafe extern "C" fn base_parse_pre_push_frame<T: BaseParseImpl>(
529 ptr: *mut ffi::GstBaseParse,
530 frame_ptr: *mut ffi::GstBaseParseFrame,
531) -> gst::ffi::GstFlowReturn {
532 unsafe {
533 let instance = &*(ptr as *mut T::Instance);
534 let imp = instance.imp();
535 let instance = imp.obj();
536 let instance = instance.unsafe_cast_ref::<BaseParse>();
537 let mut frame = BaseParseFrame::new(frame_ptr, instance);
538
539 let res = gst::panic_to_error!(imp, Err(gst::FlowError::Error), {
540 imp.pre_push_frame(&mut frame)
541 });
542
543 match res {
544 Ok(flow) => gst::FlowReturn::from_ok(flow),
545 Err(flow) => gst::FlowReturn::from_error(flow),
546 }
547 .into_glib()
548 }
549}
550
551unsafe extern "C" fn base_parse_detect<T: BaseParseImpl>(
552 ptr: *mut ffi::GstBaseParse,
553 buffer: *mut gst::ffi::GstBuffer,
554) -> gst::ffi::GstFlowReturn {
555 unsafe {
556 let instance = &*(ptr as *mut T::Instance);
557 let imp = instance.imp();
558 let buffer = gst::BufferRef::from_ptr(buffer);
559
560 let res = gst::panic_to_error!(imp, Err(gst::FlowError::Error), { imp.detect(buffer) });
561
562 match res {
563 Ok(flow) => gst::FlowReturn::from_ok(flow),
564 Err(flow) => gst::FlowReturn::from_error(flow),
565 }
566 .into_glib()
567 }
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573 use std::sync::atomic::{self, AtomicBool, AtomicU32};
574
575 const MIN_FRAME_SIZE: u32 = 8;
576
577 mod imp {
578 use super::*;
579
580 fn test_pad_templates() -> &'static [gst::PadTemplate] {
581 static PAD_TEMPLATES: std::sync::OnceLock<Vec<gst::PadTemplate>> =
582 std::sync::OnceLock::new();
583
584 PAD_TEMPLATES.get_or_init(|| {
585 let caps = gst::Caps::new_any();
586 vec![
587 gst::PadTemplate::new(
588 "src",
589 gst::PadDirection::Src,
590 gst::PadPresence::Always,
591 &caps,
592 )
593 .unwrap(),
594 gst::PadTemplate::new(
595 "sink",
596 gst::PadDirection::Sink,
597 gst::PadPresence::Always,
598 &caps,
599 )
600 .unwrap(),
601 ]
602 })
603 }
604
605 #[derive(Default)]
606 pub struct TestParse {
607 sink_caps_set: AtomicBool,
608 pub handle_frame_count: AtomicU32,
609 pub pre_push_frame_count: AtomicU32,
610 }
611
612 #[glib::object_subclass]
613 impl ObjectSubclass for TestParse {
614 const NAME: &'static str = "TestParse";
615 type Type = super::TestParse;
616 type ParentType = crate::BaseParse;
617 }
618
619 impl ObjectImpl for TestParse {}
620
621 impl GstObjectImpl for TestParse {}
622
623 impl ElementImpl for TestParse {
624 fn pad_templates() -> &'static [gst::PadTemplate] {
625 test_pad_templates()
626 }
627 }
628
629 impl BaseParseImpl for TestParse {
630 fn start(&self) -> Result<(), gst::ErrorMessage> {
631 self.obj().set_min_frame_size(MIN_FRAME_SIZE);
632 self.parent_start()
633 }
634
635 fn handle_frame(
636 &self,
637 frame: BaseParseFrame,
638 ) -> Result<(gst::FlowSuccess, u32), gst::FlowError> {
639 self.handle_frame_count
640 .fetch_add(1, atomic::Ordering::SeqCst);
641 let instance = self.obj();
642 if !self.sink_caps_set.swap(true, atomic::Ordering::SeqCst) {
643 let caps = instance
644 .sink_pad()
645 .current_caps()
646 .expect("sink pad has no caps");
647 instance.src_pad().push_event(gst::event::Caps::new(&caps));
648 }
649
650 let flow = instance.finish_frame(frame, MIN_FRAME_SIZE)?;
651 Ok((flow, 0))
652 }
653
654 fn pre_push_frame(
655 &self,
656 frame: &mut BaseParseFrame,
657 ) -> Result<gst::FlowSuccess, gst::FlowError> {
658 self.pre_push_frame_count
659 .fetch_add(1, atomic::Ordering::SeqCst);
660 self.parent_pre_push_frame(frame)
661 }
662 }
663
664 #[derive(Default)]
665 pub struct TestParseDetect {
666 sink_caps_set: AtomicBool,
667 detected: AtomicBool,
668 pub detect_count: AtomicU32,
669 }
670
671 #[glib::object_subclass]
672 impl ObjectSubclass for TestParseDetect {
673 const NAME: &'static str = "TestParseDetect";
674 type Type = super::TestParseDetect;
675 type ParentType = crate::BaseParse;
676 }
677
678 impl ObjectImpl for TestParseDetect {}
679
680 impl GstObjectImpl for TestParseDetect {}
681
682 impl ElementImpl for TestParseDetect {
683 fn pad_templates() -> &'static [gst::PadTemplate] {
684 test_pad_templates()
685 }
686 }
687
688 impl BaseParseImpl for TestParseDetect {
689 const USE_DETECT: bool = true;
690
691 fn start(&self) -> Result<(), gst::ErrorMessage> {
692 self.obj().set_min_frame_size(MIN_FRAME_SIZE);
693 self.parent_start()
694 }
695
696 fn handle_frame(
697 &self,
698 frame: BaseParseFrame,
699 ) -> Result<(gst::FlowSuccess, u32), gst::FlowError> {
700 let instance = self.obj();
701 if !self.sink_caps_set.swap(true, atomic::Ordering::SeqCst) {
702 let caps = instance
703 .sink_pad()
704 .current_caps()
705 .expect("sink pad has no caps");
706 instance.src_pad().push_event(gst::event::Caps::new(&caps));
707 }
708
709 let flow = instance.finish_frame(frame, MIN_FRAME_SIZE)?;
710 Ok((flow, 0))
711 }
712
713 fn detect(&self, _buffer: &gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
714 self.detect_count.fetch_add(1, atomic::Ordering::SeqCst);
715
716 if self.detected.swap(true, atomic::Ordering::SeqCst) {
718 Ok(gst::FlowSuccess::Ok)
719 } else {
720 Err(gst::FlowError::NotNegotiated)
721 }
722 }
723 }
724 }
725
726 glib::wrapper! {
727 pub struct TestParse(ObjectSubclass<imp::TestParse>) @extends crate::BaseParse, gst::Element, gst::Object;
728 }
729
730 glib::wrapper! {
731 pub struct TestParseDetect(ObjectSubclass<imp::TestParseDetect>) @extends crate::BaseParse, gst::Element, gst::Object;
732 }
733
734 fn run_pipeline(element: &glib::Object, num_buffers: i32) -> u64 {
735 let element = element.downcast_ref::<gst::Element>().unwrap();
736 let pipeline = gst::Pipeline::new();
737 let src = gst::ElementFactory::make("audiotestsrc")
738 .property("num-buffers", num_buffers)
739 .build()
740 .unwrap();
741 let sink = gst::ElementFactory::make("fakesink").build().unwrap();
742
743 pipeline.add_many([&src, element, &sink]).unwrap();
744 gst::Element::link_many([&src, element, &sink]).unwrap();
745
746 pipeline.set_state(gst::State::Playing).unwrap();
747 let bus = pipeline.bus().unwrap();
748
749 let msg = bus
750 .timed_pop_filtered(
751 gst::ClockTime::NONE,
752 &[gst::MessageType::Eos, gst::MessageType::Error],
753 )
754 .expect("no EOS or Error message received");
755 assert_eq!(
756 msg.type_(),
757 gst::MessageType::Eos,
758 "expected EOS, got {:?}",
759 msg
760 );
761
762 let rendered = sink
763 .property::<gst::Structure>("stats")
764 .get::<u64>("rendered")
765 .unwrap();
766 pipeline.set_state(gst::State::Null).unwrap();
767
768 rendered
769 }
770
771 #[test]
772 fn test_parse_subclass() {
773 gst::init().unwrap();
774
775 let element = glib::Object::new::<TestParse>();
776 let rendered = run_pipeline(element.upcast_ref(), 10);
777
778 assert_eq!(rendered, 2560);
779 assert!(
780 element
781 .imp()
782 .handle_frame_count
783 .load(atomic::Ordering::SeqCst)
784 > 0,
785 "handle_frame must be invoked"
786 );
787 assert_eq!(
788 element
789 .imp()
790 .pre_push_frame_count
791 .load(atomic::Ordering::SeqCst) as u64,
792 rendered,
793 "every finished frame must be pushed exactly once"
794 );
795 }
796
797 #[test]
800 #[ignore]
801 fn test_parse_subclass_detect() {
802 gst::init().unwrap();
803
804 let element = glib::Object::new::<TestParseDetect>();
805 let rendered = run_pipeline(element.upcast_ref(), 3);
806
807 assert_eq!(rendered, 768, "expected output frames, got {rendered}");
808 assert_eq!(
809 element.imp().detect_count.load(atomic::Ordering::SeqCst),
810 2,
811 "detect is invoked until it reports the stream format"
812 );
813 }
814}