gstreamer: pad: use ControlFlow with custom enum for sticky_events_foreach() return value

... instead of Result<Option<Event>,Option<Event>> which isn't very nice.

And use static dispatch instead of dynamic dispatch.

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/361
This commit is contained in:
Tim-Philipp Müller 2021-11-09 12:19:37 +00:00 committed by Sebastian Dröge
parent 1d78ac9323
commit e0e17b8b25
2 changed files with 142 additions and 27 deletions

View file

@ -173,6 +173,7 @@ mod gobject;
mod iterator;
mod object;
mod pad;
pub use pad::*;
mod registry;
pub use crate::pad::PadBuilder;
mod control_binding;

View file

@ -22,6 +22,7 @@ use crate::{SpecificFormattedValue, SpecificFormattedValueIntrinsic};
use std::cell::RefCell;
use std::mem;
use std::num::NonZeroU64;
use std::ops::ControlFlow;
use std::ptr;
use glib::ffi::gpointer;
@ -88,6 +89,13 @@ pub enum PadGetRangeSuccess {
NewBuffer(crate::Buffer),
}
#[derive(Debug)]
pub enum EventForeachAction {
Keep,
Remove,
Replace(Event),
}
pub trait PadExtManual: 'static {
#[doc(alias = "gst_pad_add_probe")]
fn add_probe<F>(&self, mask: PadProbeType, func: F) -> Option<PadProbeId>
@ -275,9 +283,10 @@ pub trait PadExtManual: 'static {
#[doc(alias = "get_mode")]
fn mode(&self) -> crate::PadMode;
#[doc(alias = "gst_pad_sticky_events_foreach")]
fn sticky_events_foreach<F: FnMut(Event) -> Result<Option<Event>, Option<Event>>>(
fn sticky_events_foreach<
F: FnMut(&Event) -> ControlFlow<EventForeachAction, EventForeachAction>,
>(
&self,
func: F,
);
@ -969,50 +978,51 @@ impl<O: IsA<Pad>> PadExtManual for O {
}
}
fn sticky_events_foreach<F: FnMut(Event) -> Result<Option<Event>, Option<Event>>>(
fn sticky_events_foreach<
F: FnMut(&Event) -> ControlFlow<EventForeachAction, EventForeachAction>,
>(
&self,
func: F,
) {
unsafe extern "C" fn trampoline(
unsafe extern "C" fn trampoline<
F: FnMut(&Event) -> ControlFlow<EventForeachAction, EventForeachAction>,
>(
_pad: *mut ffi::GstPad,
event: *mut *mut ffi::GstEvent,
user_data: glib::ffi::gpointer,
) -> glib::ffi::gboolean {
let func =
user_data as *mut &mut (dyn FnMut(Event) -> Result<Option<Event>, Option<Event>>);
let res = (*func)(from_glib_full(*event));
let func = user_data as *mut F;
let res = (*func)(&from_glib_borrow(*event));
match res {
Ok(Some(ev)) => {
*event = ev.into_ptr();
glib::ffi::GTRUE
}
Err(Some(ev)) => {
*event = ev.into_ptr();
glib::ffi::GFALSE
}
Ok(None) => {
let (do_continue, ev_action) = match res {
ControlFlow::Continue(ev_action) => (glib::ffi::GTRUE, ev_action),
ControlFlow::Break(ev_action) => (glib::ffi::GFALSE, ev_action),
};
use EventForeachAction::*;
match ev_action {
Keep => (), // do nothing
Remove => {
ffi::gst_mini_object_unref(*event as *mut _);
*event = ptr::null_mut();
glib::ffi::GTRUE
}
Err(None) => {
*event = ptr::null_mut();
glib::ffi::GFALSE
Replace(ev) => {
ffi::gst_mini_object_unref(*event as *mut _);
*event = ev.into_ptr();
}
}
do_continue
}
unsafe {
let mut func = func;
let func_obj: &mut (dyn FnMut(Event) -> Result<Option<Event>, Option<Event>>) =
&mut func;
let func_ptr = &func_obj
as *const &mut (dyn FnMut(Event) -> Result<Option<Event>, Option<Event>>)
as glib::ffi::gpointer;
let func_ptr = &mut func as *mut F as glib::ffi::gpointer;
ffi::gst_pad_sticky_events_foreach(
self.as_ref().to_glib_none().0,
Some(trampoline),
Some(trampoline::<F>),
func_ptr,
);
}
@ -2107,4 +2117,108 @@ mod tests {
"An event ref leaked!"
);
}
#[test]
fn test_sticky_events_foreach() {
crate::init().unwrap();
let pad = crate::Pad::builder(Some("sink"), crate::PadDirection::Sink).build();
pad.set_active(true).unwrap();
// Send some sticky events
assert!(pad.send_event(crate::event::StreamStart::new("test")));
let caps = crate::Caps::builder("some/x-caps").build();
assert!(pad.send_event(crate::event::Caps::new(&caps)));
let segment = crate::FormattedSegment::<crate::ClockTime>::new();
assert!(pad.send_event(crate::event::Segment::new(segment.as_ref())));
let mut sticky_events = Vec::new();
pad.sticky_events_foreach(|event| {
sticky_events.push(event.clone());
ControlFlow::Continue(EventForeachAction::Keep)
});
assert_eq!(sticky_events.len(), 3);
// Test early exit from foreach loop
let mut sticky_events2 = Vec::new();
pad.sticky_events_foreach(|event| {
sticky_events2.push(event.clone());
if event.type_() == crate::EventType::Caps {
ControlFlow::Break(EventForeachAction::Keep)
} else {
ControlFlow::Continue(EventForeachAction::Keep)
}
});
assert_eq!(sticky_events2.len(), 2);
let mut sticky_events3 = Vec::new();
pad.sticky_events_foreach(|event| {
sticky_events3.push(event.clone());
ControlFlow::Continue(EventForeachAction::Keep)
});
assert_eq!(sticky_events3.len(), 3);
for (e1, e2) in sticky_events.iter().zip(sticky_events3.iter()) {
assert_eq!(e1.as_ref() as *const _, e2.as_ref() as *const _);
}
// Replace segment event
pad.sticky_events_foreach(|event| {
let action = if event.type_() == crate::EventType::Segment {
let byte_segment = crate::FormattedSegment::<crate::format::Bytes>::new();
EventForeachAction::Replace(crate::event::Segment::new(&byte_segment))
} else {
EventForeachAction::Keep
};
ControlFlow::Continue(action)
});
// Check that segment event is different now
let mut sticky_events4 = Vec::new();
pad.sticky_events_foreach(|event| {
sticky_events4.push(event.clone());
ControlFlow::Continue(EventForeachAction::Keep)
});
assert_eq!(sticky_events4.len(), 3);
assert_eq!(
sticky_events[0].as_ref() as *const _,
sticky_events4[0].as_ref() as *const _
);
assert_eq!(
sticky_events[1].as_ref() as *const _,
sticky_events4[1].as_ref() as *const _
);
assert_ne!(
sticky_events[2].as_ref() as *const _,
sticky_events4[2].as_ref() as *const _
);
// Drop caps event
pad.sticky_events_foreach(|event| {
let action = if event.type_() == crate::EventType::Caps {
EventForeachAction::Remove
} else {
EventForeachAction::Keep
};
ControlFlow::Continue(action)
});
// Check that caps event actually got removed
let mut sticky_events5 = Vec::new();
pad.sticky_events_foreach(|event| {
sticky_events5.push(event.clone());
ControlFlow::Continue(EventForeachAction::Keep)
});
assert_eq!(sticky_events5.len(), 2);
assert_eq!(
sticky_events4[0].as_ref() as *const _,
sticky_events5[0].as_ref() as *const _
);
assert_eq!(
sticky_events4[2].as_ref() as *const _,
sticky_events5[1].as_ref() as *const _
);
}
}