From 49a6eb6a1f82a47932ec295233510e434ad66a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 10 Dec 2017 15:18:54 +0200 Subject: [PATCH] Replace AppSrcCallbacks/AppSinkCallbacks with a builder so that no empty closures have to be provided for unused callbacks --- examples/src/bin/appsink.rs | 103 +++++++++++++++++----------------- gstreamer-app/src/app_sink.rs | 98 +++++++++++++++++++++++++------- gstreamer-app/src/app_src.rs | 72 +++++++++++++++++------- gstreamer-app/src/lib.rs | 4 +- 4 files changed, 184 insertions(+), 93 deletions(-) diff --git a/examples/src/bin/appsink.rs b/examples/src/bin/appsink.rs index bee770409..fc999b375 100644 --- a/examples/src/bin/appsink.rs +++ b/examples/src/bin/appsink.rs @@ -60,67 +60,64 @@ fn create_pipeline() -> Result { ], )); - appsink.set_callbacks(gst_app::AppSinkCallbacks::new( - /* eos */ - |_| {}, - /* new_preroll */ - |_| gst::FlowReturn::Ok, - /* new_samples */ - |appsink| { - let sample = match appsink.pull_sample() { - None => return gst::FlowReturn::Eos, - Some(sample) => sample, - }; + appsink.set_callbacks( + gst_app::AppSinkCallbacksBuilder::new() + .new_sample(|appsink| { + let sample = match appsink.pull_sample() { + None => return gst::FlowReturn::Eos, + Some(sample) => sample, + }; - let buffer = if let Some(buffer) = sample.get_buffer() { - buffer - } else { - gst_element_error!( - appsink, - gst::ResourceError::Failed, - ("Failed to get buffer from appsink") - ); + let buffer = if let Some(buffer) = sample.get_buffer() { + buffer + } else { + gst_element_error!( + appsink, + gst::ResourceError::Failed, + ("Failed to get buffer from appsink") + ); - return gst::FlowReturn::Error; - }; + return gst::FlowReturn::Error; + }; - let map = if let Some(map) = buffer.map_readable() { - map - } else { - gst_element_error!( - appsink, - gst::ResourceError::Failed, - ("Failed to map buffer readable") - ); + let map = if let Some(map) = buffer.map_readable() { + map + } else { + gst_element_error!( + appsink, + gst::ResourceError::Failed, + ("Failed to map buffer readable") + ); - return gst::FlowReturn::Error; - }; + return gst::FlowReturn::Error; + }; - let samples = if let Ok(samples) = map.as_slice().as_slice_of::() { - samples - } else { - gst_element_error!( - appsink, - gst::ResourceError::Failed, - ("Failed to interprete buffer as S16 PCM") - ); + let samples = if let Ok(samples) = map.as_slice().as_slice_of::() { + samples + } else { + gst_element_error!( + appsink, + gst::ResourceError::Failed, + ("Failed to interprete buffer as S16 PCM") + ); - return gst::FlowReturn::Error; - }; + return gst::FlowReturn::Error; + }; - let sum: f64 = samples - .iter() - .map(|sample| { - let f = f64::from(*sample) / f64::from(i16::MAX); - f * f - }) - .sum(); - let rms = (sum / (samples.len() as f64)).sqrt(); - println!("rms: {}", rms); + let sum: f64 = samples + .iter() + .map(|sample| { + let f = f64::from(*sample) / f64::from(i16::MAX); + f * f + }) + .sum(); + let rms = (sum / (samples.len() as f64)).sqrt(); + println!("rms: {}", rms); - gst::FlowReturn::Ok - }, - )); + gst::FlowReturn::Ok + }) + .build(), + ); Ok(pipeline) } diff --git a/gstreamer-app/src/app_sink.rs b/gstreamer-app/src/app_sink.rs index 092789d07..b4396d310 100644 --- a/gstreamer-app/src/app_sink.rs +++ b/gstreamer-app/src/app_sink.rs @@ -16,29 +16,76 @@ use glib_ffi::gpointer; use std::ptr; pub struct AppSinkCallbacks { - eos: Box, - new_preroll: Box gst::FlowReturn + Send + Sync + 'static>, - new_sample: Box gst::FlowReturn + Send + Sync + 'static>, + eos: Option>, + new_preroll: Option gst::FlowReturn + Send + Sync + 'static>>, + new_sample: Option gst::FlowReturn + Send + Sync + 'static>>, callbacks: ffi::GstAppSinkCallbacks, } -impl AppSinkCallbacks { - pub fn new(eos: F, new_preroll: G, new_sample: H) -> Self - where - F: Fn(&AppSink) + Send + Sync + 'static, - G: Fn(&AppSink) -> gst::FlowReturn + Send + Sync + 'static, - H: Fn(&AppSink) -> gst::FlowReturn + Send + Sync + 'static, - { +pub struct AppSinkCallbacksBuilder { + eos: Option>, + new_preroll: Option gst::FlowReturn + Send + Sync + 'static>>, + new_sample: Option gst::FlowReturn + Send + Sync + 'static>>, +} + +impl AppSinkCallbacksBuilder { + pub fn new() -> Self { skip_assert_initialized!(); + AppSinkCallbacksBuilder { + eos: None, + new_preroll: None, + new_sample: None, + } + } + + pub fn eos(self, eos: F) -> Self { + Self { + eos: Some(Box::new(eos)), + ..self + } + } + + pub fn new_preroll gst::FlowReturn + Send + Sync + 'static>( + self, + new_preroll: F, + ) -> Self { + Self { + new_preroll: Some(Box::new(new_preroll)), + ..self + } + } + + pub fn new_sample gst::FlowReturn + Send + Sync + 'static>( + self, + new_sample: F, + ) -> Self { + Self { + new_sample: Some(Box::new(new_sample)), + ..self + } + } + + pub fn build(self) -> AppSinkCallbacks { + let have_eos = self.eos.is_some(); + let have_new_preroll = self.new_preroll.is_some(); + let have_new_sample = self.new_sample.is_some(); AppSinkCallbacks { - eos: Box::new(eos), - new_preroll: Box::new(new_preroll), - new_sample: Box::new(new_sample), + eos: self.eos, + new_preroll: self.new_preroll, + new_sample: self.new_sample, callbacks: ffi::GstAppSinkCallbacks { - eos: Some(trampoline_eos), - new_preroll: Some(trampoline_new_preroll), - new_sample: Some(trampoline_new_sample), + eos: if have_eos { Some(trampoline_eos) } else { None }, + new_preroll: if have_new_preroll { + Some(trampoline_new_preroll) + } else { + None + }, + new_sample: if have_new_sample { + Some(trampoline_new_sample) + } else { + None + }, _gst_reserved: [ ptr::null_mut(), ptr::null_mut(), @@ -54,7 +101,10 @@ unsafe extern "C" fn trampoline_eos(appsink: *mut ffi::GstAppSink, callbacks: gp let _guard = CallbackGuard::new(); let callbacks = &*(callbacks as *const AppSinkCallbacks); - (callbacks.eos)(&from_glib_borrow(appsink)); + callbacks + .eos + .as_ref() + .map(|f| f(&from_glib_borrow(appsink))); } unsafe extern "C" fn trampoline_new_preroll( @@ -64,7 +114,12 @@ unsafe extern "C" fn trampoline_new_preroll( let _guard = CallbackGuard::new(); let callbacks = &*(callbacks as *const AppSinkCallbacks); - (callbacks.new_preroll)(&from_glib_borrow(appsink)).to_glib() + callbacks + .new_preroll + .as_ref() + .map(|f| f(&from_glib_borrow(appsink))) + .unwrap_or(gst::FlowReturn::Error) + .to_glib() } unsafe extern "C" fn trampoline_new_sample( @@ -74,7 +129,12 @@ unsafe extern "C" fn trampoline_new_sample( let _guard = CallbackGuard::new(); let callbacks = &*(callbacks as *const AppSinkCallbacks); - (callbacks.new_sample)(&from_glib_borrow(appsink)).to_glib() + callbacks + .new_sample + .as_ref() + .map(|f| f(&from_glib_borrow(appsink))) + .unwrap_or(gst::FlowReturn::Error) + .to_glib() } unsafe extern "C" fn destroy_callbacks(ptr: gpointer) { diff --git a/gstreamer-app/src/app_src.rs b/gstreamer-app/src/app_src.rs index 76010b39a..a32785a51 100644 --- a/gstreamer-app/src/app_src.rs +++ b/gstreamer-app/src/app_src.rs @@ -15,29 +15,63 @@ use glib_ffi::{gboolean, gpointer}; use std::ptr; pub struct AppSrcCallbacks { - need_data: Box, - enough_data: Box, - seek_data: Box bool + Send + Sync + 'static>, + need_data: Option>, + enough_data: Option>, + seek_data: Option bool + Send + Sync + 'static>>, callbacks: ffi::GstAppSrcCallbacks, } -impl AppSrcCallbacks { - pub fn new(need_data: F, enough_data: G, seek_data: H) -> Self - where - F: Fn(&AppSrc, u32) + Send + Sync + 'static, - G: Fn(&AppSrc) + Send + Sync + 'static, - H: Fn(&AppSrc, u64) -> bool + Send + Sync + 'static, - { +pub struct AppSrcCallbacksBuilder { + need_data: Option>, + enough_data: Option>, + seek_data: Option bool + Send + Sync + 'static>>, +} + +impl AppSrcCallbacksBuilder { + pub fn new() -> Self { skip_assert_initialized!(); + AppSrcCallbacksBuilder { + need_data: None, + enough_data: None, + seek_data: None, + } + } + + pub fn need_data(self, need_data: F) -> Self { + Self { + need_data: Some(Box::new(need_data)), + ..self + } + } + + pub fn enough_data(self, enough_data: F) -> Self { + Self { + enough_data: Some(Box::new(enough_data)), + ..self + } + } + + pub fn seek_data bool + Send + Sync + 'static>(self, seek_data: F) -> Self { + Self { + seek_data: Some(Box::new(seek_data)), + ..self + } + } + + pub fn build(self) -> AppSrcCallbacks { + let have_need_data = self.need_data.is_some(); + let have_enough_data = self.enough_data.is_some(); + let have_seek_data = self.seek_data.is_some(); + AppSrcCallbacks { - need_data: Box::new(need_data), - enough_data: Box::new(enough_data), - seek_data: Box::new(seek_data), + need_data: self.need_data, + enough_data: self.enough_data, + seek_data: self.seek_data, callbacks: ffi::GstAppSrcCallbacks { - need_data: Some(trampoline_need_data), - enough_data: Some(trampoline_enough_data), - seek_data: Some(trampoline_seek_data), + need_data: if have_need_data { Some(trampoline_need_data) } else { None }, + enough_data: if have_enough_data { Some(trampoline_enough_data) } else { None }, + seek_data: if have_seek_data { Some(trampoline_seek_data) } else { None }, _gst_reserved: [ ptr::null_mut(), ptr::null_mut(), @@ -57,14 +91,14 @@ unsafe extern "C" fn trampoline_need_data( let _guard = CallbackGuard::new(); let callbacks = &*(callbacks as *const AppSrcCallbacks); - (callbacks.need_data)(&from_glib_borrow(appsrc), length); + callbacks.need_data.as_ref().map(|f| f(&from_glib_borrow(appsrc), length)); } unsafe extern "C" fn trampoline_enough_data(appsrc: *mut ffi::GstAppSrc, callbacks: gpointer) { let _guard = CallbackGuard::new(); let callbacks = &*(callbacks as *const AppSrcCallbacks); - (callbacks.enough_data)(&from_glib_borrow(appsrc)); + callbacks.enough_data.as_ref().map(|f| f(&from_glib_borrow(appsrc))); } unsafe extern "C" fn trampoline_seek_data( @@ -75,7 +109,7 @@ unsafe extern "C" fn trampoline_seek_data( let _guard = CallbackGuard::new(); let callbacks = &*(callbacks as *const AppSrcCallbacks); - (callbacks.seek_data)(&from_glib_borrow(appsrc), offset).to_glib() + callbacks.seek_data.as_ref().map(|f| f(&from_glib_borrow(appsrc), offset)).unwrap_or(false).to_glib() } unsafe extern "C" fn destroy_callbacks(ptr: gpointer) { diff --git a/gstreamer-app/src/lib.rs b/gstreamer-app/src/lib.rs index c8606070b..2d2c060f5 100644 --- a/gstreamer-app/src/lib.rs +++ b/gstreamer-app/src/lib.rs @@ -41,8 +41,8 @@ pub use auto::*; mod app_src; mod app_sink; -pub use app_src::AppSrcCallbacks; -pub use app_sink::AppSinkCallbacks; +pub use app_src::*; +pub use app_sink::*; // Re-export all the traits in a prelude module, so that applications // can always "use gst::prelude::*" without getting conflicts