diff --git a/examples/src/bin/appsink.rs b/examples/src/bin/appsink.rs index 3b2119d66..8a777fee6 100644 --- a/examples/src/bin/appsink.rs +++ b/examples/src/bin/appsink.rs @@ -37,25 +37,21 @@ fn create_pipeline() -> Result { let pipeline = gst::Pipeline::new(None); let src = gst::ElementFactory::make("audiotestsrc").build()?; - let sink = gst::ElementFactory::make("appsink").build()?; + let appsink = gst_app::AppSink::builder() + // Tell the appsink what format we want. It will then be the audiotestsrc's job to + // provide the format we request. + // This can be set after linking the two objects, because format negotiation between + // both elements will happen during pre-rolling of the pipeline. + .caps( + &gst_audio::AudioCapsBuilder::new_interleaved() + .format(gst_audio::AUDIO_FORMAT_S16) + .channels(1) + .build(), + ) + .build(); - pipeline.add_many(&[&src, &sink])?; - src.link(&sink)?; - - let appsink = sink - .dynamic_cast::() - .expect("Sink element is expected to be an appsink!"); - - // Tell the appsink what format we want. It will then be the audiotestsrc's job to - // provide the format we request. - // This can be set after linking the two objects, because format negotiation between - // both elements will happen during pre-rolling of the pipeline. - appsink.set_caps(Some( - &gst_audio::AudioCapsBuilder::new_interleaved() - .format(gst_audio::AUDIO_FORMAT_S16) - .channels(1) - .build(), - )); + pipeline.add_many(&[&src, appsink.upcast_ref()])?; + src.link(&appsink)?; // Getting data out of the appsink is done by setting callbacks on it. // The appsink will then call those handlers, as soon as data is available. diff --git a/examples/src/bin/appsrc.rs b/examples/src/bin/appsrc.rs index 1d0109310..6dc3b6fdb 100644 --- a/examples/src/bin/appsrc.rs +++ b/examples/src/bin/appsrc.rs @@ -34,16 +34,6 @@ fn create_pipeline() -> Result { gst::init()?; let pipeline = gst::Pipeline::new(None); - let src = gst::ElementFactory::make("appsrc").build()?; - let videoconvert = gst::ElementFactory::make("videoconvert").build()?; - let sink = gst::ElementFactory::make("autovideosink").build()?; - - pipeline.add_many(&[&src, &videoconvert, &sink])?; - gst::Element::link_many(&[&src, &videoconvert, &sink])?; - - let appsrc = src - .dynamic_cast::() - .expect("Source element is expected to be an appsrc!"); // Specify the format we want to provide as application into the pipeline // by creating a video info with the given format and creating caps from it for the appsrc element. @@ -53,8 +43,16 @@ fn create_pipeline() -> Result { .build() .expect("Failed to create video info"); - appsrc.set_caps(Some(&video_info.to_caps().unwrap())); - appsrc.set_format(gst::Format::Time); + let appsrc = gst_app::AppSrc::builder() + .caps(&video_info.to_caps().unwrap()) + .format(gst::Format::Time) + .build(); + + let videoconvert = gst::ElementFactory::make("videoconvert").build()?; + let sink = gst::ElementFactory::make("autovideosink").build()?; + + pipeline.add_many(&[appsrc.upcast_ref(), &videoconvert, &sink])?; + gst::Element::link_many(&[appsrc.upcast_ref(), &videoconvert, &sink])?; // Our frame counter, that is stored in the mutable environment // of the closure of the need-data callback diff --git a/examples/src/bin/custom_meta.rs b/examples/src/bin/custom_meta.rs index 6c6270d10..1c462ad58 100644 --- a/examples/src/bin/custom_meta.rs +++ b/examples/src/bin/custom_meta.rs @@ -182,16 +182,8 @@ fn example_main() { // This creates a pipeline with appsrc and appsink. let pipeline = gst::Pipeline::new(None); - let appsrc = gst::ElementFactory::make("appsrc") - .build() - .unwrap() - .downcast::() - .unwrap(); - let appsink = gst::ElementFactory::make("appsink") - .build() - .unwrap() - .downcast::() - .unwrap(); + let appsrc = gst_app::AppSrc::builder().build(); + let appsink = gst_app::AppSink::builder().build(); pipeline.add(&appsrc).unwrap(); pipeline.add(&appsink).unwrap(); diff --git a/examples/src/bin/fd_allocator.rs b/examples/src/bin/fd_allocator.rs index 9edc57653..f9cb8fc95 100644 --- a/examples/src/bin/fd_allocator.rs +++ b/examples/src/bin/fd_allocator.rs @@ -47,31 +47,24 @@ fn create_receiver_pipeline( let caps = video_info.to_caps()?; let pipeline = gst::Pipeline::new(None); - let src = gst::ElementFactory::make("appsrc").build()?; + let src = gst_app::AppSrc::builder() + .caps(&caps) + .do_timestamp(true) + .is_live(true) + .build(); let filter = video_filter::FdMemoryFadeInVideoFilter::default().upcast::(); let convert = gst::ElementFactory::make("videoconvert").build()?; let queue = gst::ElementFactory::make("queue").build()?; let sink = gst::ElementFactory::make("autovideosink").build()?; - src.downcast_ref::() - .ok_or_else(|| anyhow::anyhow!("is not a appsrc"))? - .set_caps(Some(&caps)); - - pipeline.add_many(&[&src, &filter, &convert, &queue, &sink])?; - gst::Element::link_many(&[&src, &filter, &convert, &queue, &sink])?; - - let appsrc = src - .downcast::() - .map_err(|_| anyhow::anyhow!("is not a appsrc"))?; - - appsrc.set_do_timestamp(true); - appsrc.set_is_live(true); + pipeline.add_many(&[src.upcast_ref(), &filter, &convert, &queue, &sink])?; + gst::Element::link_many(&[src.upcast_ref(), &filter, &convert, &queue, &sink])?; let fd_allocator = gst_allocators::FdAllocator::new(); let video_info = video_info.clone(); let mut fd_buf = [-1; 253]; - appsrc.set_callbacks( + src.set_callbacks( gst_app::AppSrcCallbacks::builder() .need_data(move |appsrc, _| { // Read the next fds from the socket, if 0 diff --git a/examples/src/glupload.rs b/examples/src/glupload.rs index 87fe5939e..30a5b2b9e 100644 --- a/examples/src/glupload.rs +++ b/examples/src/glupload.rs @@ -533,19 +533,17 @@ impl App { let pipeline = gst::Pipeline::new(None); let src = gst::ElementFactory::make("videotestsrc").build()?; - let appsink = gst::ElementFactory::make("appsink") - .build()? - .dynamic_cast::() - .expect("Sink element is expected to be an appsink!"); - - appsink.set_enable_last_sample(true); - appsink.set_max_buffers(1); let caps = gst_video::VideoCapsBuilder::new() .features(&[&gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY]) .format(gst_video::VideoFormat::Rgba) .field("texture-target", "2D") .build(); - appsink.set_caps(Some(&caps)); + + let appsink = gst_app::AppSink::builder() + .enable_last_sample(true) + .max_buffers(1) + .caps(&caps) + .build(); if let Some(gl_element) = gl_element { let glupload = gst::ElementFactory::make("glupload").build()?; diff --git a/gstreamer-app/src/app_sink.rs b/gstreamer-app/src/app_sink.rs index c46bb4d8e..be1dea64b 100644 --- a/gstreamer-app/src/app_sink.rs +++ b/gstreamer-app/src/app_sink.rs @@ -268,6 +268,14 @@ unsafe extern "C" fn destroy_callbacks(ptr: gpointer) { } impl AppSink { + // rustdoc-stripper-ignore-next + /// Creates a new builder-pattern struct instance to construct [`AppSink`] objects. + /// + /// This method returns an instance of [`AppSinkBuilder`](crate::builders::AppSinkBuilder) which can be used to create [`AppSink`] objects. + pub fn builder() -> AppSinkBuilder { + AppSinkBuilder::default() + } + #[doc(alias = "gst_app_sink_set_callbacks")] pub fn set_callbacks(&self, callbacks: AppSinkCallbacks) { use once_cell::sync::Lazy; @@ -914,6 +922,205 @@ impl AppSink { } } +#[derive(Default)] +// rustdoc-stripper-ignore-next +/// A [builder-pattern] type to construct [`AppSink`] objects. +/// +/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html +#[must_use = "The builder must be built to be used"] +pub struct AppSinkBuilder { + async_: Option, + buffer_list: Option, + callbacks: Option, + caps: Option, + drop: Option, + drop_out_of_segment: Option, + enable_last_sample: Option, + max_bitrate: Option, + max_buffers: Option, + max_lateness: Option, + #[cfg(any(feature = "v1_16", feature = "dox"))] + processing_deadline: Option, + qos: Option, + render_delay: Option>, + sync: Option, + throttle_time: Option, + ts_offset: Option, + wait_on_eos: Option, + name: Option, +} + +impl AppSinkBuilder { + // rustdoc-stripper-ignore-next + /// Create a new [`AppSinkBuilder`]. + pub fn new() -> Self { + Self::default() + } + + // rustdoc-stripper-ignore-next + /// Build the [`AppSink`]. + #[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"] + pub fn build(self) -> AppSink { + let mut properties: Vec<(&str, &dyn ToValue)> = vec![]; + if let Some(ref async_) = self.async_ { + properties.push(("async", async_)); + } + if let Some(ref buffer_list) = self.buffer_list { + properties.push(("buffer-list", buffer_list)); + } + if let Some(ref caps) = self.caps { + properties.push(("caps", caps)); + } + if let Some(ref drop) = self.drop { + properties.push(("drop", drop)); + } + if let Some(ref enable_last_sample) = self.enable_last_sample { + properties.push(("enable-last-sample", enable_last_sample)); + } + if let Some(ref max_bitrate) = self.max_bitrate { + properties.push(("max-bitrate", max_bitrate)); + } + if let Some(ref max_buffers) = self.max_buffers { + properties.push(("max-buffers", max_buffers)); + } + if let Some(ref max_lateness) = self.max_lateness { + properties.push(("max-lateness", max_lateness)); + } + #[cfg(any(feature = "v1_16", feature = "dox"))] + if let Some(ref processing_deadline) = self.processing_deadline { + properties.push(("processing-deadline", processing_deadline)); + } + if let Some(ref qos) = self.qos { + properties.push(("qos", qos)); + } + if let Some(ref render_delay) = self.render_delay { + properties.push(("render-delay", render_delay)); + } + if let Some(ref sync) = self.sync { + properties.push(("sync", sync)); + } + if let Some(ref throttle_time) = self.throttle_time { + properties.push(("throttle-time", throttle_time)); + } + if let Some(ref ts_offset) = self.ts_offset { + properties.push(("ts-offset", ts_offset)); + } + if let Some(ref qos) = self.qos { + properties.push(("qos", qos)); + } + if let Some(ref wait_on_eos) = self.wait_on_eos { + properties.push(("wait-on-eos", wait_on_eos)); + } + if let Some(ref name) = self.name { + properties.push(("name", name)); + } + + let appsink = glib::Object::new::(&properties); + + if let Some(callbacks) = self.callbacks { + appsink.set_callbacks(callbacks); + } + + if let Some(drop_out_of_segment) = self.drop_out_of_segment { + appsink.set_drop_out_of_segment(drop_out_of_segment); + } + + appsink + } + + pub fn async_(mut self, async_: bool) -> Self { + self.async_ = Some(async_); + self + } + + pub fn buffer_list(mut self, buffer_list: bool) -> Self { + self.buffer_list = Some(buffer_list); + self + } + + pub fn callbacks(mut self, callbacks: AppSinkCallbacks) -> Self { + self.callbacks = Some(callbacks); + self + } + + pub fn caps(mut self, caps: &gst::Caps) -> Self { + self.caps = Some(caps.clone()); + self + } + + pub fn drop(mut self, drop: bool) -> Self { + self.drop = Some(drop); + self + } + + pub fn drop_out_of_segment(mut self, drop_out_of_segment: bool) -> Self { + self.drop_out_of_segment = Some(drop_out_of_segment); + self + } + + pub fn enable_last_sample(mut self, enable_last_sample: bool) -> Self { + self.enable_last_sample = Some(enable_last_sample); + self + } + + pub fn max_bitrate(mut self, max_bitrate: u64) -> Self { + self.max_bitrate = Some(max_bitrate); + self + } + + pub fn max_buffers(mut self, max_buffers: u32) -> Self { + self.max_buffers = Some(max_buffers); + self + } + + pub fn max_lateness(mut self, max_lateness: i64) -> Self { + self.max_lateness = Some(max_lateness); + self + } + + #[cfg(any(feature = "v1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_16")))] + pub fn processing_deadline(mut self, processing_deadline: i64) -> Self { + self.processing_deadline = Some(processing_deadline); + self + } + + pub fn qos(mut self, qos: bool) -> Self { + self.qos = Some(qos); + self + } + + pub fn render_delay(mut self, render_delay: Option) -> Self { + self.render_delay = Some(render_delay); + self + } + + pub fn sync(mut self, sync: bool) -> Self { + self.sync = Some(sync); + self + } + + pub fn throttle_time(mut self, throttle_time: u64) -> Self { + self.throttle_time = Some(throttle_time); + self + } + + pub fn ts_offset(mut self, ts_offset: gst::ClockTimeDiff) -> Self { + self.ts_offset = Some(ts_offset); + self + } + + pub fn wait_on_eos(mut self, wait_on_eos: bool) -> Self { + self.wait_on_eos = Some(wait_on_eos); + self + } + + pub fn name(mut self, name: &str) -> Self { + self.name = Some(name.to_string()); + self + } +} + #[derive(Debug)] pub struct AppSinkStream { app_sink: glib::WeakRef, diff --git a/gstreamer-app/src/app_src.rs b/gstreamer-app/src/app_src.rs index 4c94f4cb7..7169a5eca 100644 --- a/gstreamer-app/src/app_src.rs +++ b/gstreamer-app/src/app_src.rs @@ -205,6 +205,14 @@ unsafe extern "C" fn destroy_callbacks(ptr: gpointer) { } impl AppSrc { + // rustdoc-stripper-ignore-next + /// Creates a new builder-pattern struct instance to construct [`AppSrc`] objects. + /// + /// This method returns an instance of [`AppSrcBuilder`](crate::builders::AppSrcBuilder) which can be used to create [`AppSrc`] objects. + pub fn builder() -> AppSrcBuilder { + AppSrcBuilder::default() + } + #[doc(alias = "gst_app_src_push_buffer")] pub fn push_buffer(&self, buffer: gst::Buffer) -> Result { unsafe { @@ -352,6 +360,227 @@ impl AppSrc { } } +#[derive(Default)] +// rustdoc-stripper-ignore-next +/// A [builder-pattern] type to construct [`AppSrc`] objects. +/// +/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html +#[must_use = "The builder must be built to be used"] +pub struct AppSrcBuilder { + automatic_eos: Option, + block: Option, + callbacks: Option, + caps: Option, + do_timestamp: Option, + duration: Option, + format: Option, + #[cfg(any(feature = "v1_18", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_18")))] + handle_segment_change: Option, + is_live: Option, + #[cfg(any(feature = "v1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_20")))] + leaky_type: Option, + #[cfg(any(feature = "v1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_20")))] + max_buffers: Option, + max_bytes: Option, + max_latency: Option, + #[cfg(any(feature = "v1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_20")))] + max_time: Option>, + min_latency: Option, + min_percent: Option, + size: Option, + stream_type: Option, + name: Option, +} + +impl AppSrcBuilder { + // rustdoc-stripper-ignore-next + /// Create a new [`AppSrcBuilder`]. + pub fn new() -> Self { + Self::default() + } + + // rustdoc-stripper-ignore-next + /// Build the [`AppSrc`]. + #[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"] + pub fn build(self) -> AppSrc { + let mut properties: Vec<(&str, &dyn ToValue)> = vec![]; + if let Some(ref block) = self.block { + properties.push(("block", block)); + } + if let Some(ref caps) = self.caps { + properties.push(("caps", caps)); + } + if let Some(ref do_timestamp) = self.do_timestamp { + properties.push(("do-timestamp", do_timestamp)); + } + if let Some(ref duration) = self.duration { + properties.push(("duration", duration)); + } + if let Some(ref format) = self.format { + properties.push(("format", format)); + } + #[cfg(any(feature = "v1_18", feature = "dox"))] + if let Some(ref handle_segment_change) = self.handle_segment_change { + properties.push(("handle-segment-change", handle_segment_change)); + } + if let Some(ref is_live) = self.is_live { + properties.push(("is-live", is_live)); + } + #[cfg(any(feature = "v1_20", feature = "dox"))] + if let Some(ref leaky_type) = self.leaky_type { + properties.push(("leaky-type", leaky_type)); + } + #[cfg(any(feature = "v1_20", feature = "dox"))] + if let Some(ref max_buffers) = self.max_buffers { + properties.push(("max-buffers", max_buffers)); + } + if let Some(ref max_bytes) = self.max_bytes { + properties.push(("max-bytes", max_bytes)); + } + if let Some(ref max_latency) = self.max_latency { + properties.push(("max-latency", max_latency)); + } + #[cfg(any(feature = "v1_20", feature = "dox"))] + if let Some(ref max_time) = self.max_time { + properties.push(("max-time", max_time)); + } + if let Some(ref min_latency) = self.min_latency { + properties.push(("min-latency", min_latency)); + } + if let Some(ref min_percent) = self.min_percent { + properties.push(("min-percent", min_percent)); + } + if let Some(ref size) = self.size { + properties.push(("size", size)); + } + if let Some(ref stream_type) = self.stream_type { + properties.push(("stream-type", stream_type)); + } + if let Some(ref name) = self.name { + properties.push(("name", name)); + } + + let appsrc = glib::Object::new::(&properties); + + if let Some(callbacks) = self.callbacks { + appsrc.set_callbacks(callbacks); + } + + if let Some(automatic_eos) = self.automatic_eos { + appsrc.set_automatic_eos(automatic_eos); + } + + appsrc + } + + pub fn automatic_eos(mut self, automatic_eos: bool) -> Self { + self.automatic_eos = Some(automatic_eos); + self + } + + pub fn block(mut self, block: bool) -> Self { + self.block = Some(block); + self + } + + pub fn callbacks(mut self, callbacks: AppSrcCallbacks) -> Self { + self.callbacks = Some(callbacks); + self + } + + pub fn caps(mut self, caps: &gst::Caps) -> Self { + self.caps = Some(caps.clone()); + self + } + + pub fn do_timestamp(mut self, do_timestamp: bool) -> Self { + self.do_timestamp = Some(do_timestamp); + self + } + + pub fn duration(mut self, duration: u64) -> Self { + self.duration = Some(duration); + self + } + + pub fn format(mut self, format: gst::Format) -> Self { + self.format = Some(format); + self + } + + #[cfg(any(feature = "v1_18", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_18")))] + pub fn handle_segment_change(mut self, handle_segment_change: bool) -> Self { + self.handle_segment_change = Some(handle_segment_change); + self + } + + pub fn is_live(mut self, is_live: bool) -> Self { + self.is_live = Some(is_live); + self + } + + #[cfg(any(feature = "v1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_20")))] + pub fn leaky_type(mut self, leaky_type: crate::AppLeakyType) -> Self { + self.leaky_type = Some(leaky_type); + self + } + + #[cfg(any(feature = "v1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_20")))] + pub fn max_buffers(mut self, max_buffers: u64) -> Self { + self.max_buffers = Some(max_buffers); + self + } + + pub fn max_bytes(mut self, max_bytes: u64) -> Self { + self.max_bytes = Some(max_bytes); + self + } + + pub fn max_latency(mut self, max_latency: i64) -> Self { + self.max_latency = Some(max_latency); + self + } + + #[cfg(any(feature = "v1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_20")))] + pub fn max_time(mut self, max_time: Option) -> Self { + self.max_time = Some(max_time); + self + } + + pub fn min_latency(mut self, min_latency: i64) -> Self { + self.min_latency = Some(min_latency); + self + } + + pub fn min_percent(mut self, min_percent: u32) -> Self { + self.min_percent = Some(min_percent); + self + } + + pub fn size(mut self, size: i64) -> Self { + self.size = Some(size); + self + } + + pub fn stream_type(mut self, stream_type: crate::AppStreamType) -> Self { + self.stream_type = Some(stream_type); + self + } + + pub fn name(mut self, name: &str) -> Self { + self.name = Some(name.to_string()); + self + } +} + #[derive(Debug)] pub struct AppSrcSink { app_src: glib::WeakRef, diff --git a/tutorials/src/bin/basic-tutorial-8.rs b/tutorials/src/bin/basic-tutorial-8.rs index cc747fec4..1c03aebf5 100644 --- a/tutorials/src/bin/basic-tutorial-8.rs +++ b/tutorials/src/bin/basic-tutorial-8.rs @@ -47,10 +47,16 @@ fn main() { return; } - let appsrc = gst::ElementFactory::make("appsrc") - .name("audio_source") + let info = AudioInfo::builder(gst_audio::AudioFormat::S16le, SAMPLE_RATE, 1) .build() .unwrap(); + let audio_caps = info.to_caps().unwrap(); + + let appsrc = gst_app::AppSrc::builder() + .name("audio_source") + .caps(&audio_caps) + .format(gst::Format::Time) + .build(); let tee = gst::ElementFactory::make("tee") .name("tee") .build() @@ -97,16 +103,16 @@ fn main() { .name("app_queue") .build() .unwrap(); - let appsink = gst::ElementFactory::make("appsink") + let appsink = gst_app::AppSink::builder() + .caps(&audio_caps) .name("app_sink") - .build() - .unwrap(); + .build(); let pipeline = gst::Pipeline::new(Some("test-pipeline")); pipeline .add_many(&[ - &appsrc, + appsrc.upcast_ref(), &tee, &audio_queue, &audio_convert1, @@ -118,11 +124,11 @@ fn main() { &video_convert, &video_sink, &app_queue, - &appsink, + appsink.upcast_ref(), ]) .unwrap(); - gst::Element::link_many(&[&appsrc, &tee]).unwrap(); + gst::Element::link_many(&[appsrc.upcast_ref(), &tee]).unwrap(); gst::Element::link_many(&[&audio_queue, &audio_convert1, &audio_resample, &audio_sink]) .unwrap(); gst::Element::link_many(&[ @@ -133,7 +139,7 @@ fn main() { &video_sink, ]) .unwrap(); - gst::Element::link_many(&[&app_queue, &appsink]).unwrap(); + gst::Element::link_many(&[&app_queue, appsink.upcast_ref()]).unwrap(); let tee_audio_pad = tee.request_pad_simple("src_%u").unwrap(); println!( @@ -154,22 +160,6 @@ fn main() { let queue_app_pad = app_queue.static_pad("sink").unwrap(); tee_app_pad.link(&queue_app_pad).unwrap(); - // configure appsrc - let info = AudioInfo::builder(gst_audio::AudioFormat::S16le, SAMPLE_RATE, 1) - .build() - .unwrap(); - let audio_caps = info.to_caps().unwrap(); - - let appsrc = appsrc - .dynamic_cast::() - .expect("Source element is expected to be an appsrc!"); - appsrc.set_caps(Some(&audio_caps)); - appsrc.set_format(gst::Format::Time); - - let appsink = appsink - .dynamic_cast::() - .expect("Sink element is expected to be an appsink!"); - let data: Arc> = Arc::new(Mutex::new(CustomData::new(&appsrc, &appsink))); let data_weak = Arc::downgrade(&data); @@ -250,9 +240,6 @@ fn main() { .build(), ); - // configure appsink - appsink.set_caps(Some(&audio_caps)); - let data_weak = Arc::downgrade(&data); appsink.set_callbacks( gst_app::AppSinkCallbacks::builder()