forked from mirrors/gstreamer-rs
app: Handle panicking callbacks by converting into an error message
And never calling the callbacks again but instead just failing. Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/241
This commit is contained in:
parent
bcfc88a560
commit
b1b0103b3b
2 changed files with 136 additions and 6 deletions
|
@ -12,12 +12,15 @@ use glib::signal::SignalHandlerId;
|
||||||
use glib::translate::*;
|
use glib::translate::*;
|
||||||
use glib_sys::gpointer;
|
use glib_sys::gpointer;
|
||||||
use gst;
|
use gst;
|
||||||
|
use gst::gst_element_error;
|
||||||
use gst_app_sys;
|
use gst_app_sys;
|
||||||
use gst_sys;
|
use gst_sys;
|
||||||
use std::boxed::Box as Box_;
|
use std::boxed::Box as Box_;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
|
use std::panic;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use AppSink;
|
use AppSink;
|
||||||
|
|
||||||
#[cfg(any(feature = "v1_10"))]
|
#[cfg(any(feature = "v1_10"))]
|
||||||
|
@ -43,6 +46,7 @@ pub struct AppSinkCallbacks {
|
||||||
Box<dyn FnMut(&AppSink) -> Result<gst::FlowSuccess, gst::FlowError> + Send + 'static>,
|
Box<dyn FnMut(&AppSink) -> Result<gst::FlowSuccess, gst::FlowError> + Send + 'static>,
|
||||||
>,
|
>,
|
||||||
>,
|
>,
|
||||||
|
panicked: AtomicBool,
|
||||||
callbacks: gst_app_sys::GstAppSinkCallbacks,
|
callbacks: gst_app_sys::GstAppSinkCallbacks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +121,7 @@ impl AppSinkCallbacksBuilder {
|
||||||
eos: self.eos,
|
eos: self.eos,
|
||||||
new_preroll: self.new_preroll,
|
new_preroll: self.new_preroll,
|
||||||
new_sample: self.new_sample,
|
new_sample: self.new_sample,
|
||||||
|
panicked: AtomicBool::new(false),
|
||||||
callbacks: gst_app_sys::GstAppSinkCallbacks {
|
callbacks: gst_app_sys::GstAppSinkCallbacks {
|
||||||
eos: if have_eos { Some(trampoline_eos) } else { None },
|
eos: if have_eos { Some(trampoline_eos) } else { None },
|
||||||
new_preroll: if have_new_preroll {
|
new_preroll: if have_new_preroll {
|
||||||
|
@ -140,11 +145,37 @@ impl AppSinkCallbacksBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn post_panic_error_message(element: &AppSink, err: &dyn std::any::Any) {
|
||||||
|
if let Some(cause) = err.downcast_ref::<&str>() {
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
|
||||||
|
} else if let Some(cause) = err.downcast_ref::<String>() {
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
|
||||||
|
} else {
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn trampoline_eos(appsink: *mut gst_app_sys::GstAppSink, callbacks: gpointer) {
|
unsafe extern "C" fn trampoline_eos(appsink: *mut gst_app_sys::GstAppSink, callbacks: gpointer) {
|
||||||
let callbacks = &*(callbacks as *const AppSinkCallbacks);
|
let callbacks = &*(callbacks as *const AppSinkCallbacks);
|
||||||
|
let element: AppSink = from_glib_borrow(appsink);
|
||||||
|
|
||||||
|
if callbacks.panicked.load(Ordering::Relaxed) {
|
||||||
|
let element: AppSink = from_glib_borrow(appsink);
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref eos) = callbacks.eos {
|
if let Some(ref eos) = callbacks.eos {
|
||||||
(&mut *eos.borrow_mut())(&from_glib_borrow(appsink))
|
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||||
|
(&mut *eos.borrow_mut())(&element)
|
||||||
|
}));
|
||||||
|
match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(err) => {
|
||||||
|
callbacks.panicked.store(true, Ordering::Relaxed);
|
||||||
|
post_panic_error_message(&element, &err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,9 +184,27 @@ unsafe extern "C" fn trampoline_new_preroll(
|
||||||
callbacks: gpointer,
|
callbacks: gpointer,
|
||||||
) -> gst_sys::GstFlowReturn {
|
) -> gst_sys::GstFlowReturn {
|
||||||
let callbacks = &*(callbacks as *const AppSinkCallbacks);
|
let callbacks = &*(callbacks as *const AppSinkCallbacks);
|
||||||
|
let element: AppSink = from_glib_borrow(appsink);
|
||||||
|
|
||||||
|
if callbacks.panicked.load(Ordering::Relaxed) {
|
||||||
|
let element: AppSink = from_glib_borrow(appsink);
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
|
||||||
|
return gst::FlowReturn::Error.to_glib();
|
||||||
|
}
|
||||||
|
|
||||||
let ret = if let Some(ref new_preroll) = callbacks.new_preroll {
|
let ret = if let Some(ref new_preroll) = callbacks.new_preroll {
|
||||||
(&mut *new_preroll.borrow_mut())(&from_glib_borrow(appsink)).into()
|
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||||
|
(&mut *new_preroll.borrow_mut())(&element).into()
|
||||||
|
}));
|
||||||
|
match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(err) => {
|
||||||
|
callbacks.panicked.store(true, Ordering::Relaxed);
|
||||||
|
post_panic_error_message(&element, &err);
|
||||||
|
|
||||||
|
gst::FlowReturn::Error
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
gst::FlowReturn::Error
|
gst::FlowReturn::Error
|
||||||
};
|
};
|
||||||
|
@ -168,9 +217,27 @@ unsafe extern "C" fn trampoline_new_sample(
|
||||||
callbacks: gpointer,
|
callbacks: gpointer,
|
||||||
) -> gst_sys::GstFlowReturn {
|
) -> gst_sys::GstFlowReturn {
|
||||||
let callbacks = &*(callbacks as *const AppSinkCallbacks);
|
let callbacks = &*(callbacks as *const AppSinkCallbacks);
|
||||||
|
let element: AppSink = from_glib_borrow(appsink);
|
||||||
|
|
||||||
|
if callbacks.panicked.load(Ordering::Relaxed) {
|
||||||
|
let element: AppSink = from_glib_borrow(appsink);
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
|
||||||
|
return gst::FlowReturn::Error.to_glib();
|
||||||
|
}
|
||||||
|
|
||||||
let ret = if let Some(ref new_sample) = callbacks.new_sample {
|
let ret = if let Some(ref new_sample) = callbacks.new_sample {
|
||||||
(&mut *new_sample.borrow_mut())(&from_glib_borrow(appsink)).into()
|
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||||
|
(&mut *new_sample.borrow_mut())(&element).into()
|
||||||
|
}));
|
||||||
|
match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(err) => {
|
||||||
|
callbacks.panicked.store(true, Ordering::Relaxed);
|
||||||
|
post_panic_error_message(&element, &err);
|
||||||
|
|
||||||
|
gst::FlowReturn::Error
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
gst::FlowReturn::Error
|
gst::FlowReturn::Error
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,11 +10,15 @@ use futures_sink::Sink;
|
||||||
use glib::translate::*;
|
use glib::translate::*;
|
||||||
use glib_sys::{gboolean, gpointer};
|
use glib_sys::{gboolean, gpointer};
|
||||||
use gst;
|
use gst;
|
||||||
|
use gst::gst_element_error;
|
||||||
|
|
||||||
use gst_app_sys;
|
use gst_app_sys;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::panic;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::task::{Context, Poll, Waker};
|
use std::task::{Context, Poll, Waker};
|
||||||
use AppSrc;
|
use AppSrc;
|
||||||
|
@ -24,6 +28,7 @@ pub struct AppSrcCallbacks {
|
||||||
need_data: Option<RefCell<Box<dyn FnMut(&AppSrc, u32) + Send + 'static>>>,
|
need_data: Option<RefCell<Box<dyn FnMut(&AppSrc, u32) + Send + 'static>>>,
|
||||||
enough_data: Option<Box<dyn Fn(&AppSrc) + Send + Sync + 'static>>,
|
enough_data: Option<Box<dyn Fn(&AppSrc) + Send + Sync + 'static>>,
|
||||||
seek_data: Option<Box<dyn Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>>,
|
seek_data: Option<Box<dyn Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>>,
|
||||||
|
panicked: AtomicBool,
|
||||||
callbacks: gst_app_sys::GstAppSrcCallbacks,
|
callbacks: gst_app_sys::GstAppSrcCallbacks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +89,7 @@ impl AppSrcCallbacksBuilder {
|
||||||
need_data: self.need_data,
|
need_data: self.need_data,
|
||||||
enough_data: self.enough_data,
|
enough_data: self.enough_data,
|
||||||
seek_data: self.seek_data,
|
seek_data: self.seek_data,
|
||||||
|
panicked: AtomicBool::new(false),
|
||||||
callbacks: gst_app_sys::GstAppSrcCallbacks {
|
callbacks: gst_app_sys::GstAppSrcCallbacks {
|
||||||
need_data: if have_need_data {
|
need_data: if have_need_data {
|
||||||
Some(trampoline_need_data)
|
Some(trampoline_need_data)
|
||||||
|
@ -111,15 +117,41 @@ impl AppSrcCallbacksBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn post_panic_error_message(element: &AppSrc, err: &dyn std::any::Any) {
|
||||||
|
if let Some(cause) = err.downcast_ref::<&str>() {
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
|
||||||
|
} else if let Some(cause) = err.downcast_ref::<String>() {
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
|
||||||
|
} else {
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn trampoline_need_data(
|
unsafe extern "C" fn trampoline_need_data(
|
||||||
appsrc: *mut gst_app_sys::GstAppSrc,
|
appsrc: *mut gst_app_sys::GstAppSrc,
|
||||||
length: u32,
|
length: u32,
|
||||||
callbacks: gpointer,
|
callbacks: gpointer,
|
||||||
) {
|
) {
|
||||||
let callbacks = &*(callbacks as *const AppSrcCallbacks);
|
let callbacks = &*(callbacks as *const AppSrcCallbacks);
|
||||||
|
let element: AppSrc = from_glib_borrow(appsrc);
|
||||||
|
|
||||||
|
if callbacks.panicked.load(Ordering::Relaxed) {
|
||||||
|
let element: AppSrc = from_glib_borrow(appsrc);
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref need_data) = callbacks.need_data {
|
if let Some(ref need_data) = callbacks.need_data {
|
||||||
(&mut *need_data.borrow_mut())(&from_glib_borrow(appsrc), length);
|
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||||
|
(&mut *need_data.borrow_mut())(&element, length)
|
||||||
|
}));
|
||||||
|
match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(err) => {
|
||||||
|
callbacks.panicked.store(true, Ordering::Relaxed);
|
||||||
|
post_panic_error_message(&element, &err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,9 +160,23 @@ unsafe extern "C" fn trampoline_enough_data(
|
||||||
callbacks: gpointer,
|
callbacks: gpointer,
|
||||||
) {
|
) {
|
||||||
let callbacks = &*(callbacks as *const AppSrcCallbacks);
|
let callbacks = &*(callbacks as *const AppSrcCallbacks);
|
||||||
|
let element: AppSrc = from_glib_borrow(appsrc);
|
||||||
|
|
||||||
|
if callbacks.panicked.load(Ordering::Relaxed) {
|
||||||
|
let element: AppSrc = from_glib_borrow(appsrc);
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref enough_data) = callbacks.enough_data {
|
if let Some(ref enough_data) = callbacks.enough_data {
|
||||||
(*enough_data)(&from_glib_borrow(appsrc));
|
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| (*enough_data)(&element)));
|
||||||
|
match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(err) => {
|
||||||
|
callbacks.panicked.store(true, Ordering::Relaxed);
|
||||||
|
post_panic_error_message(&element, &err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,9 +186,26 @@ unsafe extern "C" fn trampoline_seek_data(
|
||||||
callbacks: gpointer,
|
callbacks: gpointer,
|
||||||
) -> gboolean {
|
) -> gboolean {
|
||||||
let callbacks = &*(callbacks as *const AppSrcCallbacks);
|
let callbacks = &*(callbacks as *const AppSrcCallbacks);
|
||||||
|
let element: AppSrc = from_glib_borrow(appsrc);
|
||||||
|
|
||||||
|
if callbacks.panicked.load(Ordering::Relaxed) {
|
||||||
|
let element: AppSrc = from_glib_borrow(appsrc);
|
||||||
|
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
|
||||||
|
return false.to_glib();
|
||||||
|
}
|
||||||
|
|
||||||
let ret = if let Some(ref seek_data) = callbacks.seek_data {
|
let ret = if let Some(ref seek_data) = callbacks.seek_data {
|
||||||
(*seek_data)(&from_glib_borrow(appsrc), offset)
|
let result =
|
||||||
|
panic::catch_unwind(panic::AssertUnwindSafe(|| (*seek_data)(&element, offset)));
|
||||||
|
match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(err) => {
|
||||||
|
callbacks.panicked.store(true, Ordering::Relaxed);
|
||||||
|
post_panic_error_message(&element, &err);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue