1use std::{
4 mem, panic,
5 pin::Pin,
6 ptr,
7 sync::{Arc, Mutex},
8 task::{Context, Poll, Waker},
9};
10
11#[cfg(not(panic = "abort"))]
12use std::sync::atomic::{AtomicBool, Ordering};
13
14use futures_sink::Sink;
15use glib::{
16 ffi::{gboolean, gpointer},
17 prelude::*,
18 translate::*,
19};
20
21use crate::{AppSrc, ffi};
22
23#[allow(clippy::type_complexity)]
24pub struct AppSrcCallbacks {
25 need_data: Option<Box<dyn FnMut(&AppSrc, u32) + Send + 'static>>,
26 enough_data: Option<Box<dyn Fn(&AppSrc) + Send + Sync + 'static>>,
27 seek_data: Option<Box<dyn Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>>,
28 #[cfg(not(panic = "abort"))]
29 panicked: AtomicBool,
30 callbacks: ffi::GstAppSrcCallbacks,
31}
32
33unsafe impl Send for AppSrcCallbacks {}
34unsafe impl Sync for AppSrcCallbacks {}
35
36impl AppSrcCallbacks {
37 pub fn builder() -> AppSrcCallbacksBuilder {
38 skip_assert_initialized!();
39
40 AppSrcCallbacksBuilder {
41 need_data: None,
42 enough_data: None,
43 seek_data: None,
44 }
45 }
46}
47
48#[allow(clippy::type_complexity)]
49#[must_use = "The builder must be built to be used"]
50pub struct AppSrcCallbacksBuilder {
51 need_data: Option<Box<dyn FnMut(&AppSrc, u32) + Send + 'static>>,
52 enough_data: Option<Box<dyn Fn(&AppSrc) + Send + Sync + 'static>>,
53 seek_data: Option<Box<dyn Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>>,
54}
55
56impl AppSrcCallbacksBuilder {
57 pub fn need_data<F: FnMut(&AppSrc, u32) + Send + 'static>(self, need_data: F) -> Self {
58 Self {
59 need_data: Some(Box::new(need_data)),
60 ..self
61 }
62 }
63
64 pub fn need_data_if<F: FnMut(&AppSrc, u32) + Send + 'static>(
65 self,
66 need_data: F,
67 predicate: bool,
68 ) -> Self {
69 if predicate {
70 self.need_data(need_data)
71 } else {
72 self
73 }
74 }
75
76 pub fn need_data_if_some<F: FnMut(&AppSrc, u32) + Send + 'static>(
77 self,
78 need_data: Option<F>,
79 ) -> Self {
80 if let Some(need_data) = need_data {
81 self.need_data(need_data)
82 } else {
83 self
84 }
85 }
86
87 pub fn enough_data<F: Fn(&AppSrc) + Send + Sync + 'static>(self, enough_data: F) -> Self {
88 Self {
89 enough_data: Some(Box::new(enough_data)),
90 ..self
91 }
92 }
93
94 pub fn enough_data_if<F: Fn(&AppSrc) + Send + Sync + 'static>(
95 self,
96 enough_data: F,
97 predicate: bool,
98 ) -> Self {
99 if predicate {
100 self.enough_data(enough_data)
101 } else {
102 self
103 }
104 }
105
106 pub fn enough_data_if_some<F: Fn(&AppSrc) + Send + Sync + 'static>(
107 self,
108 enough_data: Option<F>,
109 ) -> Self {
110 if let Some(enough_data) = enough_data {
111 self.enough_data(enough_data)
112 } else {
113 self
114 }
115 }
116
117 pub fn seek_data<F: Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>(
118 self,
119 seek_data: F,
120 ) -> Self {
121 Self {
122 seek_data: Some(Box::new(seek_data)),
123 ..self
124 }
125 }
126
127 pub fn seek_data_if<F: Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>(
128 self,
129 seek_data: F,
130 predicate: bool,
131 ) -> Self {
132 if predicate {
133 self.seek_data(seek_data)
134 } else {
135 self
136 }
137 }
138
139 pub fn seek_data_if_some<F: Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>(
140 self,
141 seek_data: Option<F>,
142 ) -> Self {
143 if let Some(seek_data) = seek_data {
144 self.seek_data(seek_data)
145 } else {
146 self
147 }
148 }
149
150 #[must_use = "Building the callbacks without using them has no effect"]
151 pub fn build(self) -> AppSrcCallbacks {
152 let have_need_data = self.need_data.is_some();
153 let have_enough_data = self.enough_data.is_some();
154 let have_seek_data = self.seek_data.is_some();
155
156 AppSrcCallbacks {
157 need_data: self.need_data,
158 enough_data: self.enough_data,
159 seek_data: self.seek_data,
160 #[cfg(not(panic = "abort"))]
161 panicked: AtomicBool::new(false),
162 callbacks: ffi::GstAppSrcCallbacks {
163 need_data: if have_need_data {
164 Some(trampoline_need_data)
165 } else {
166 None
167 },
168 enough_data: if have_enough_data {
169 Some(trampoline_enough_data)
170 } else {
171 None
172 },
173 seek_data: if have_seek_data {
174 Some(trampoline_seek_data)
175 } else {
176 None
177 },
178 _gst_reserved: [
179 ptr::null_mut(),
180 ptr::null_mut(),
181 ptr::null_mut(),
182 ptr::null_mut(),
183 ],
184 },
185 }
186 }
187}
188
189unsafe extern "C" fn trampoline_need_data(
190 appsrc: *mut ffi::GstAppSrc,
191 length: u32,
192 callbacks: gpointer,
193) {
194 unsafe {
195 let callbacks = callbacks as *mut AppSrcCallbacks;
196 let element: Borrowed<AppSrc> = from_glib_borrow(appsrc);
197
198 #[cfg(not(panic = "abort"))]
199 if (*callbacks).panicked.load(Ordering::Relaxed) {
200 let element: Borrowed<AppSrc> = from_glib_borrow(appsrc);
201 gst::subclass::post_panic_error_message(
202 element.upcast_ref(),
203 element.upcast_ref(),
204 None,
205 );
206 return;
207 }
208
209 if let Some(ref mut need_data) = (*callbacks).need_data {
210 let result =
211 panic::catch_unwind(panic::AssertUnwindSafe(|| need_data(&element, length)));
212 match result {
213 Ok(result) => result,
214 Err(err) => {
215 #[cfg(panic = "abort")]
216 {
217 unreachable!("{err:?}");
218 }
219 #[cfg(not(panic = "abort"))]
220 {
221 (*callbacks).panicked.store(true, Ordering::Relaxed);
222 gst::subclass::post_panic_error_message(
223 element.upcast_ref(),
224 element.upcast_ref(),
225 Some(err),
226 );
227 }
228 }
229 }
230 }
231 }
232}
233
234unsafe extern "C" fn trampoline_enough_data(appsrc: *mut ffi::GstAppSrc, callbacks: gpointer) {
235 unsafe {
236 let callbacks = callbacks as *const AppSrcCallbacks;
237 let element: Borrowed<AppSrc> = from_glib_borrow(appsrc);
238
239 #[cfg(not(panic = "abort"))]
240 if (*callbacks).panicked.load(Ordering::Relaxed) {
241 let element: Borrowed<AppSrc> = from_glib_borrow(appsrc);
242 gst::subclass::post_panic_error_message(
243 element.upcast_ref(),
244 element.upcast_ref(),
245 None,
246 );
247 return;
248 }
249
250 if let Some(ref enough_data) = (*callbacks).enough_data {
251 let result = panic::catch_unwind(panic::AssertUnwindSafe(|| enough_data(&element)));
252 match result {
253 Ok(result) => result,
254 Err(err) => {
255 #[cfg(panic = "abort")]
256 {
257 unreachable!("{err:?}");
258 }
259 #[cfg(not(panic = "abort"))]
260 {
261 (*callbacks).panicked.store(true, Ordering::Relaxed);
262 gst::subclass::post_panic_error_message(
263 element.upcast_ref(),
264 element.upcast_ref(),
265 Some(err),
266 );
267 }
268 }
269 }
270 }
271 }
272}
273
274unsafe extern "C" fn trampoline_seek_data(
275 appsrc: *mut ffi::GstAppSrc,
276 offset: u64,
277 callbacks: gpointer,
278) -> gboolean {
279 unsafe {
280 let callbacks = callbacks as *const AppSrcCallbacks;
281 let element: Borrowed<AppSrc> = from_glib_borrow(appsrc);
282
283 #[cfg(not(panic = "abort"))]
284 if (*callbacks).panicked.load(Ordering::Relaxed) {
285 let element: Borrowed<AppSrc> = from_glib_borrow(appsrc);
286 gst::subclass::post_panic_error_message(
287 element.upcast_ref(),
288 element.upcast_ref(),
289 None,
290 );
291 return false.into_glib();
292 }
293
294 let ret = if let Some(ref seek_data) = (*callbacks).seek_data {
295 let result =
296 panic::catch_unwind(panic::AssertUnwindSafe(|| seek_data(&element, offset)));
297 match result {
298 Ok(result) => result,
299 Err(err) => {
300 #[cfg(panic = "abort")]
301 {
302 unreachable!("{err:?}");
303 }
304 #[cfg(not(panic = "abort"))]
305 {
306 (*callbacks).panicked.store(true, Ordering::Relaxed);
307 gst::subclass::post_panic_error_message(
308 element.upcast_ref(),
309 element.upcast_ref(),
310 Some(err),
311 );
312
313 false
314 }
315 }
316 }
317 } else {
318 false
319 };
320
321 ret.into_glib()
322 }
323}
324
325unsafe extern "C" fn destroy_callbacks(ptr: gpointer) {
326 unsafe {
327 let _ = Box::<AppSrcCallbacks>::from_raw(ptr as *mut _);
328 }
329}
330
331impl AppSrc {
332 pub fn builder<'a>() -> AppSrcBuilder<'a> {
337 assert_initialized_main_thread!();
338 AppSrcBuilder {
339 builder: gst::Object::builder(),
340 callbacks: None,
341 automatic_eos: None,
342 }
343 }
344
345 #[doc(alias = "gst_app_src_set_callbacks")]
366 pub fn set_callbacks(&self, callbacks: AppSrcCallbacks) {
367 unsafe {
368 let src = self.to_glib_none().0;
369 #[allow(clippy::manual_dangling_ptr)]
370 #[cfg(not(feature = "v1_18"))]
371 {
372 static SET_ONCE_QUARK: std::sync::OnceLock<glib::Quark> =
373 std::sync::OnceLock::new();
374
375 let set_once_quark = SET_ONCE_QUARK
376 .get_or_init(|| glib::Quark::from_str("gstreamer-rs-app-src-callbacks"));
377
378 if gst::version() < (1, 16, 3, 0) {
381 if !glib::gobject_ffi::g_object_get_qdata(
382 src as *mut _,
383 set_once_quark.into_glib(),
384 )
385 .is_null()
386 {
387 panic!("AppSrc callbacks can only be set once");
388 }
389
390 glib::gobject_ffi::g_object_set_qdata(
391 src as *mut _,
392 set_once_quark.into_glib(),
393 1 as *mut _,
394 );
395 }
396 }
397
398 ffi::gst_app_src_set_callbacks(
399 src,
400 mut_override(&callbacks.callbacks),
401 Box::into_raw(Box::new(callbacks)) as *mut _,
402 Some(destroy_callbacks),
403 );
404 }
405 }
406
407 #[doc(alias = "gst_app_src_set_latency")]
414 pub fn set_latency(
415 &self,
416 min: impl Into<Option<gst::ClockTime>>,
417 max: impl Into<Option<gst::ClockTime>>,
418 ) {
419 unsafe {
420 ffi::gst_app_src_set_latency(
421 self.to_glib_none().0,
422 min.into().into_glib(),
423 max.into().into_glib(),
424 );
425 }
426 }
427
428 #[doc(alias = "get_latency")]
439 #[doc(alias = "gst_app_src_get_latency")]
440 pub fn latency(&self) -> (Option<gst::ClockTime>, Option<gst::ClockTime>) {
441 unsafe {
442 let mut min = mem::MaybeUninit::uninit();
443 let mut max = mem::MaybeUninit::uninit();
444 ffi::gst_app_src_get_latency(self.to_glib_none().0, min.as_mut_ptr(), max.as_mut_ptr());
445 (from_glib(min.assume_init()), from_glib(max.assume_init()))
446 }
447 }
448
449 #[doc(alias = "do-timestamp")]
450 #[doc(alias = "gst_base_src_set_do_timestamp")]
451 pub fn set_do_timestamp(&self, timestamp: bool) {
452 unsafe {
453 gst_base::ffi::gst_base_src_set_do_timestamp(
454 self.as_ptr() as *mut gst_base::ffi::GstBaseSrc,
455 timestamp.into_glib(),
456 );
457 }
458 }
459
460 #[doc(alias = "do-timestamp")]
461 #[doc(alias = "gst_base_src_get_do_timestamp")]
462 pub fn do_timestamp(&self) -> bool {
463 unsafe {
464 from_glib(gst_base::ffi::gst_base_src_get_do_timestamp(
465 self.as_ptr() as *mut gst_base::ffi::GstBaseSrc
466 ))
467 }
468 }
469
470 #[doc(alias = "do-timestamp")]
471 pub fn connect_do_timestamp_notify<F: Fn(&Self) + Send + Sync + 'static>(
472 &self,
473 f: F,
474 ) -> glib::SignalHandlerId {
475 unsafe extern "C" fn notify_do_timestamp_trampoline<
476 F: Fn(&AppSrc) + Send + Sync + 'static,
477 >(
478 this: *mut ffi::GstAppSrc,
479 _param_spec: glib::ffi::gpointer,
480 f: glib::ffi::gpointer,
481 ) {
482 unsafe {
483 let f: &F = &*(f as *const F);
484 f(&AppSrc::from_glib_borrow(this))
485 }
486 }
487 unsafe {
488 let f: Box<F> = Box::new(f);
489 glib::signal::connect_raw(
490 self.as_ptr() as *mut _,
491 b"notify::do-timestamp\0".as_ptr() as *const _,
492 Some(mem::transmute::<*const (), unsafe extern "C" fn()>(
493 notify_do_timestamp_trampoline::<F> as *const (),
494 )),
495 Box::into_raw(f),
496 )
497 }
498 }
499
500 #[doc(alias = "set-automatic-eos")]
501 #[doc(alias = "gst_base_src_set_automatic_eos")]
502 pub fn set_automatic_eos(&self, automatic_eos: bool) {
503 unsafe {
504 gst_base::ffi::gst_base_src_set_automatic_eos(
505 self.as_ptr() as *mut gst_base::ffi::GstBaseSrc,
506 automatic_eos.into_glib(),
507 );
508 }
509 }
510
511 pub fn sink(&self) -> AppSrcSink {
512 AppSrcSink::new(self)
513 }
514}
515
516#[must_use = "The builder must be built to be used"]
521pub struct AppSrcBuilder<'a> {
522 builder: gst::gobject::GObjectBuilder<'a, AppSrc>,
523 callbacks: Option<AppSrcCallbacks>,
524 automatic_eos: Option<bool>,
525}
526
527impl<'a> AppSrcBuilder<'a> {
528 #[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"]
536 pub fn build(self) -> AppSrc {
537 let appsrc = self.builder.build().unwrap();
538
539 if let Some(callbacks) = self.callbacks {
540 appsrc.set_callbacks(callbacks);
541 }
542
543 if let Some(automatic_eos) = self.automatic_eos {
544 appsrc.set_automatic_eos(automatic_eos);
545 }
546
547 appsrc
548 }
549
550 pub fn automatic_eos(self, automatic_eos: bool) -> Self {
551 Self {
552 automatic_eos: Some(automatic_eos),
553 ..self
554 }
555 }
556
557 pub fn block(self, block: bool) -> Self {
558 Self {
559 builder: self.builder.property("block", block),
560 ..self
561 }
562 }
563
564 pub fn callbacks(self, callbacks: AppSrcCallbacks) -> Self {
565 Self {
566 callbacks: Some(callbacks),
567 ..self
568 }
569 }
570
571 pub fn caps(self, caps: &'a gst::Caps) -> Self {
572 Self {
573 builder: self.builder.property("caps", caps),
574 ..self
575 }
576 }
577
578 pub fn do_timestamp(self, do_timestamp: bool) -> Self {
579 Self {
580 builder: self.builder.property("do-timestamp", do_timestamp),
581 ..self
582 }
583 }
584
585 pub fn duration(self, duration: u64) -> Self {
586 Self {
587 builder: self.builder.property("duration", duration),
588 ..self
589 }
590 }
591
592 pub fn format(self, format: gst::Format) -> Self {
593 Self {
594 builder: self.builder.property("format", format),
595 ..self
596 }
597 }
598
599 #[cfg(feature = "v1_18")]
600 #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
601 pub fn handle_segment_change(self, handle_segment_change: bool) -> Self {
602 Self {
603 builder: self
604 .builder
605 .property("handle-segment-change", handle_segment_change),
606 ..self
607 }
608 }
609
610 pub fn is_live(self, is_live: bool) -> Self {
611 Self {
612 builder: self.builder.property("is-live", is_live),
613 ..self
614 }
615 }
616
617 #[cfg(feature = "v1_20")]
618 #[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
619 pub fn leaky_type(self, leaky_type: crate::AppLeakyType) -> Self {
620 Self {
621 builder: self.builder.property("leaky-type", leaky_type),
622 ..self
623 }
624 }
625
626 #[cfg(feature = "v1_20")]
627 #[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
628 pub fn max_buffers(self, max_buffers: u64) -> Self {
629 Self {
630 builder: self.builder.property("max-buffers", max_buffers),
631 ..self
632 }
633 }
634
635 pub fn max_bytes(self, max_bytes: u64) -> Self {
636 Self {
637 builder: self.builder.property("max-bytes", max_bytes),
638 ..self
639 }
640 }
641
642 pub fn max_latency(self, max_latency: i64) -> Self {
643 Self {
644 builder: self.builder.property("max-latency", max_latency),
645 ..self
646 }
647 }
648
649 #[cfg(feature = "v1_20")]
650 #[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
651 pub fn max_time(self, max_time: gst::ClockTime) -> Self {
652 Self {
653 builder: self.builder.property("max-time", max_time),
654 ..self
655 }
656 }
657
658 pub fn min_latency(self, min_latency: i64) -> Self {
659 Self {
660 builder: self.builder.property("min-latency", min_latency),
661 ..self
662 }
663 }
664
665 pub fn min_percent(self, min_percent: u32) -> Self {
666 Self {
667 builder: self.builder.property("min-percent", min_percent),
668 ..self
669 }
670 }
671
672 pub fn size(self, size: i64) -> Self {
673 Self {
674 builder: self.builder.property("size", size),
675 ..self
676 }
677 }
678
679 pub fn stream_type(self, stream_type: crate::AppStreamType) -> Self {
680 Self {
681 builder: self.builder.property("stream-type", stream_type),
682 ..self
683 }
684 }
685
686 #[cfg(feature = "v1_28")]
687 #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))]
688 pub fn silent(self, silent: bool) -> Self {
689 Self {
690 builder: self.builder.property("silent", silent),
691 ..self
692 }
693 }
694
695 #[inline]
700 pub fn property(self, name: &'a str, value: impl Into<glib::Value> + 'a) -> Self {
701 Self {
702 builder: self.builder.property(name, value),
703 ..self
704 }
705 }
706
707 #[inline]
710 pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
711 Self {
712 builder: self.builder.property_from_str(name, value),
713 ..self
714 }
715 }
716
717 gst::impl_builder_gvalue_extra_setters!(property_and_name);
718}
719
720#[derive(Debug)]
721pub struct AppSrcSink {
722 app_src: glib::WeakRef<AppSrc>,
723 waker_reference: Arc<Mutex<Option<Waker>>>,
724}
725
726impl AppSrcSink {
727 fn new(app_src: &AppSrc) -> Self {
728 skip_assert_initialized!();
729
730 let waker_reference = Arc::new(Mutex::new(None as Option<Waker>));
731
732 app_src.set_callbacks(
733 AppSrcCallbacks::builder()
734 .need_data({
735 let waker_reference = Arc::clone(&waker_reference);
736
737 move |_, _| {
738 if let Some(waker) = waker_reference.lock().unwrap().take() {
739 waker.wake();
740 }
741 }
742 })
743 .build(),
744 );
745
746 Self {
747 app_src: app_src.downgrade(),
748 waker_reference,
749 }
750 }
751}
752
753impl Drop for AppSrcSink {
754 fn drop(&mut self) {
755 #[cfg(not(feature = "v1_18"))]
756 {
757 if gst::version() >= (1, 16, 3, 0)
760 && let Some(app_src) = self.app_src.upgrade()
761 {
762 app_src.set_callbacks(AppSrcCallbacks::builder().build());
763 }
764 }
765 }
766}
767
768impl Sink<gst::Sample> for AppSrcSink {
769 type Error = gst::FlowError;
770
771 fn poll_ready(self: Pin<&mut Self>, context: &mut Context) -> Poll<Result<(), Self::Error>> {
772 let mut waker = self.waker_reference.lock().unwrap();
773
774 let Some(app_src) = self.app_src.upgrade() else {
775 return Poll::Ready(Err(gst::FlowError::Eos));
776 };
777
778 let current_level_bytes = app_src.current_level_bytes();
779 let max_bytes = app_src.max_bytes();
780
781 if current_level_bytes >= max_bytes && max_bytes != 0 {
782 waker.replace(context.waker().to_owned());
783
784 Poll::Pending
785 } else {
786 Poll::Ready(Ok(()))
787 }
788 }
789
790 fn start_send(self: Pin<&mut Self>, sample: gst::Sample) -> Result<(), Self::Error> {
791 let Some(app_src) = self.app_src.upgrade() else {
792 return Err(gst::FlowError::Eos);
793 };
794
795 app_src.push_sample(&sample)?;
796
797 Ok(())
798 }
799
800 fn poll_flush(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Self::Error>> {
801 Poll::Ready(Ok(()))
802 }
803
804 fn poll_close(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Self::Error>> {
805 let Some(app_src) = self.app_src.upgrade() else {
806 return Poll::Ready(Ok(()));
807 };
808
809 app_src.end_of_stream()?;
810
811 Poll::Ready(Ok(()))
812 }
813}
814
815#[cfg(test)]
816mod tests {
817 use std::sync::atomic::{AtomicUsize, Ordering};
818
819 use futures_util::{sink::SinkExt, stream::StreamExt};
820 use gst::prelude::*;
821
822 use super::*;
823
824 #[test]
825 fn test_app_src_sink() {
826 gst::init().unwrap();
827
828 let appsrc = gst::ElementFactory::make("appsrc").build().unwrap();
829 let fakesink = gst::ElementFactory::make("fakesink")
830 .property("signal-handoffs", true)
831 .build()
832 .unwrap();
833
834 let pipeline = gst::Pipeline::new();
835 pipeline.add(&appsrc).unwrap();
836 pipeline.add(&fakesink).unwrap();
837
838 appsrc.link(&fakesink).unwrap();
839
840 let mut bus_stream = pipeline.bus().unwrap().stream();
841 let mut app_src_sink = appsrc.dynamic_cast::<AppSrc>().unwrap().sink();
842
843 let sample_quantity = 5;
844
845 let samples = (0..sample_quantity)
846 .map(|_| gst::Sample::builder().buffer(&gst::Buffer::new()).build())
847 .collect::<Vec<gst::Sample>>();
848
849 let mut sample_stream = futures_util::stream::iter(samples).map(Ok);
850
851 let handoff_count_reference = Arc::new(AtomicUsize::new(0));
852
853 fakesink.connect("handoff", false, {
854 let handoff_count_reference = Arc::clone(&handoff_count_reference);
855
856 move |_| {
857 handoff_count_reference.fetch_add(1, Ordering::AcqRel);
858
859 None
860 }
861 });
862
863 pipeline.set_state(gst::State::Playing).unwrap();
864
865 futures_executor::block_on(app_src_sink.send_all(&mut sample_stream)).unwrap();
866 futures_executor::block_on(app_src_sink.close()).unwrap();
867
868 while let Some(message) = futures_executor::block_on(bus_stream.next()) {
869 match message.view() {
870 gst::MessageView::Eos(_) => break,
871 gst::MessageView::Error(_) => unreachable!(),
872 _ => continue,
873 }
874 }
875
876 pipeline.set_state(gst::State::Null).unwrap();
877
878 assert_eq!(
879 handoff_count_reference.load(Ordering::Acquire),
880 sample_quantity
881 );
882 }
883
884 #[test]
885 fn builder_caps_lt() {
886 gst::init().unwrap();
887
888 let caps = &gst::Caps::new_any();
889 {
890 let stream_type = "random-access".to_owned();
891 let appsrc = AppSrc::builder()
892 .property_from_str("stream-type", &stream_type)
893 .caps(caps)
894 .build();
895 assert_eq!(
896 appsrc.property::<crate::AppStreamType>("stream-type"),
897 crate::AppStreamType::RandomAccess
898 );
899 assert!(appsrc.property::<gst::Caps>("caps").is_any());
900 }
901
902 let stream_type = &"random-access".to_owned();
903 {
904 let caps = &gst::Caps::new_any();
905 let appsrc = AppSrc::builder()
906 .property_from_str("stream-type", stream_type)
907 .caps(caps)
908 .build();
909 assert_eq!(
910 appsrc.property::<crate::AppStreamType>("stream-type"),
911 crate::AppStreamType::RandomAccess
912 );
913 assert!(appsrc.property::<gst::Caps>("caps").is_any());
914 }
915 }
916}