Major BugFix: avoid default gst g_main_context

Do NOT update g_main_context at RenderingManager update. Do not use g_main_context for Dialogs. Do not rely on default g_main_context for Device discoverer (implemented a thread save g_main_loop dedicated).
This commit is contained in:
Bruno Herbelin
2022-01-06 20:20:30 +01:00
parent d4f370c071
commit 3df6ffe280
8 changed files with 114 additions and 98 deletions

View File

@@ -150,6 +150,9 @@ void Device::add(GstDevice *device)
if (device==nullptr) if (device==nullptr)
return; return;
// lock before change
access_.lock();
gchar *device_name = gst_device_get_display_name (device); gchar *device_name = gst_device_get_display_name (device);
if ( std::find(manager().src_name_.begin(), manager().src_name_.end(), device_name) == manager().src_name_.end()) { if ( std::find(manager().src_name_.begin(), manager().src_name_.end(), device_name) == manager().src_name_.end()) {
@@ -171,6 +174,9 @@ void Device::add(GstDevice *device)
list_uptodate_ = false; list_uptodate_ = false;
} }
g_free (device_name); g_free (device_name);
// unlock access
access_.unlock();
} }
void Device::remove(GstDevice *device) void Device::remove(GstDevice *device)
@@ -178,6 +184,9 @@ void Device::remove(GstDevice *device)
if (device==nullptr) if (device==nullptr)
return; return;
// lock before change
access_.lock();
gchar *device_name = gst_device_get_display_name (device); gchar *device_name = gst_device_get_display_name (device);
std::vector< std::string >::iterator nameit = src_name_.begin(); std::vector< std::string >::iterator nameit = src_name_.begin();
@@ -204,47 +213,42 @@ void Device::remove(GstDevice *device)
++coit; ++coit;
} }
g_free (device_name); g_free (device_name);
// unlock access
access_.unlock();
} }
Device::Device(): list_uptodate_(false) Device::Device(): list_uptodate_(false)
{ {
std::thread(launchMonitoring, this).detach();
} }
void Device::init() void Device::launchMonitoring(Device *d)
{ {
GstBus *bus; d->monitor_ = gst_device_monitor_new ();
GstCaps *caps;
// create GStreamer device monitor to capture GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw");
// when a device is plugged in or out gst_device_monitor_add_filter (d->monitor_, "Video/Source", caps);
monitor_ = gst_device_monitor_new ();
bus = gst_device_monitor_get_bus (monitor_);
gst_bus_add_watch (bus, callback_device_monitor, NULL);
gst_object_unref (bus);
caps = gst_caps_new_empty_simple ("video/x-raw");
gst_device_monitor_add_filter (monitor_, "Video/Source", caps);
gst_caps_unref (caps); gst_caps_unref (caps);
gst_device_monitor_set_show_all_devices(monitor_, true); gst_device_monitor_set_show_all_devices(d->monitor_, true);
gst_device_monitor_start (monitor_); gst_device_monitor_start (d->monitor_);
// initial fill of the list // Add configs for plugged devices
GList *devices = gst_device_monitor_get_devices(monitor_); GList *devices = gst_device_monitor_get_devices(d->monitor_);
GList *tmp; GList *tmp;
for (tmp = devices; tmp ; tmp = tmp->next ) { for (tmp = devices; tmp ; tmp = tmp->next ) {
GstDevice *device = (GstDevice *) tmp->data; GstDevice *device = (GstDevice *) tmp->data;
add(device); d->add(device);
gst_object_unref (device); gst_object_unref (device);
} }
g_list_free(devices); g_list_free(devices);
// Add config for plugged screen // Add config for plugged screen
src_name_.push_back("Screen capture"); d->access_.lock();
src_description_.push_back(gst_plugin_vidcap); d->src_name_.push_back("Screen capture");
d->src_description_.push_back(gst_plugin_vidcap);
// Try to auto find resolution // Try to auto find resolution
DeviceConfigSet confs = getDeviceConfigs(gst_plugin_vidcap); DeviceConfigSet confs = getDeviceConfigs(gst_plugin_vidcap);
@@ -254,25 +258,48 @@ void Device::init()
DeviceConfigSet confscreen; DeviceConfigSet confscreen;
best.fps_numerator = 15; best.fps_numerator = 15;
confscreen.insert(best); confscreen.insert(best);
src_config_.push_back(confscreen); d->src_config_.push_back(confscreen);
} }
d->access_.unlock();
// TODO Use lib glfw to get monitors d->list_uptodate_ = true;
// TODO Detect auto removal of monitors
// create a local g_main_context for this threaded monitor
GMainContext *_gcontext_device = g_main_context_new();
if (g_main_context_acquire(_gcontext_device))
g_printerr("Starting Device monitoring... \n");
// temporarily push as default the default g_main_context for add_watch
g_main_context_push_thread_default(_gcontext_device);
GstBus *bus = gst_device_monitor_get_bus (d->monitor_);
gst_bus_add_watch (bus, callback_device_monitor, NULL);
gst_object_unref (bus);
g_main_context_pop_thread_default(_gcontext_device);
// start main loop for this context (blocking infinitely)
g_main_loop_run( g_main_loop_new (_gcontext_device, true) );
list_uptodate_ = true;
} }
int Device::numDevices()
int Device::numDevices() const
{ {
return src_name_.size(); access_.lock();
int ret = src_name_.size();
access_.unlock();
return ret;
} }
bool Device::exists(const std::string &device) const bool Device::exists(const std::string &device)
{ {
access_.lock();
std::vector< std::string >::const_iterator d = std::find(src_name_.begin(), src_name_.end(), device); std::vector< std::string >::const_iterator d = std::find(src_name_.begin(), src_name_.end(), device);
return d != src_name_.end(); bool ret = (d != src_name_.end());
access_.unlock();
return ret;
} }
struct hasDevice: public std::unary_function<DeviceSource*, bool> struct hasDevice: public std::unary_function<DeviceSource*, bool>
@@ -307,43 +334,57 @@ Source *Device::createSource(const std::string &device) const
return s; return s;
} }
bool Device::unplugged(const std::string &device) const bool Device::unplugged(const std::string &device)
{ {
if (list_uptodate_) if (list_uptodate_)
return false; return false;
return !exists(device); return !exists(device);
} }
std::string Device::name(int index) const std::string Device::name(int index)
{ {
std::string ret = "";
access_.lock();
if (index > -1 && index < (int) src_name_.size()) if (index > -1 && index < (int) src_name_.size())
return src_name_[index]; ret = src_name_[index];
else access_.unlock();
return "";
return ret;
} }
std::string Device::description(int index) const std::string Device::description(int index)
{ {
std::string ret = "";
access_.lock();
if (index > -1 && index < (int) src_description_.size()) if (index > -1 && index < (int) src_description_.size())
return src_description_[index]; ret = src_description_[index];
else access_.unlock();
return "";
return ret;
} }
DeviceConfigSet Device::config(int index) const DeviceConfigSet Device::config(int index)
{ {
DeviceConfigSet ret;
access_.lock();
if (index > -1 && index < (int) src_config_.size()) if (index > -1 && index < (int) src_config_.size())
return src_config_[index]; ret = src_config_[index];
else access_.unlock();
return DeviceConfigSet();
return ret;
} }
int Device::index(const std::string &device) const int Device::index(const std::string &device)
{ {
int i = -1; int i = -1;
std::vector< std::string >::const_iterator p = std::find(src_name_.begin(), src_name_.end(), device); access_.lock();
std::vector< std::string >::iterator p = std::find(src_name_.begin(), src_name_.end(), device);
if (p != src_name_.end()) if (p != src_name_.end())
i = std::distance(src_name_.begin(), p); i = std::distance(src_name_.begin(), p);
access_.unlock();
return i; return i;
} }

View File

@@ -100,15 +100,14 @@ public:
return _instance; return _instance;
} }
void init(); int numDevices () ;
int numDevices () const; std::string name (int index) ;
std::string name (int index) const; std::string description (int index) ;
std::string description (int index) const; DeviceConfigSet config (int index) ;
DeviceConfigSet config (int index) const;
int index (const std::string &device) const; int index (const std::string &device);
bool exists (const std::string &device) const; bool exists (const std::string &device) ;
bool unplugged (const std::string &device) const; bool unplugged (const std::string &device) ;
Source *createSource(const std::string &device) const; Source *createSource(const std::string &device) const;
@@ -117,9 +116,11 @@ public:
private: private:
static void launchMonitoring(Device *d);
void remove(GstDevice *device); void remove(GstDevice *device);
void add(GstDevice *device); void add(GstDevice *device);
std::mutex access_;
std::vector< std::string > src_name_; std::vector< std::string > src_name_;
std::vector< std::string > src_description_; std::vector< std::string > src_description_;
std::vector< DeviceConfigSet > src_config_; std::vector< DeviceConfigSet > src_config_;

View File

@@ -254,9 +254,10 @@ std::string saveSessionFileDialog(const std::string &label, const std::string &p
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *save_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); char *save_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (save_file_name) if (save_file_name) {
filename = std::string(save_file_name); filename = std::string(save_file_name);
g_free( save_file_name ); g_free( save_file_name );
}
} }
// remember position // remember position
@@ -314,9 +315,10 @@ std::string openSessionFileDialog(const std::string &label, const std::string &p
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_file_name) if (open_file_name) {
filename = std::string(open_file_name); filename = std::string(open_file_name);
g_free( open_file_name ); g_free( open_file_name );
}
} }
// remember position // remember position
@@ -371,9 +373,10 @@ std::string openMediaFileDialog(const std::string &label, const std::string &pat
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_file_name) if (open_file_name) {
filename = std::string(open_file_name); filename = std::string(open_file_name);
g_free( open_file_name ); g_free( open_file_name );
}
} }
// remember position // remember position
@@ -428,9 +431,10 @@ std::string openImageFileDialog(const std::string &label, const std::string &pat
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_file_name) if (open_file_name) {
filename = std::string(open_file_name); filename = std::string(open_file_name);
g_free( open_file_name ); g_free( open_file_name );
}
} }
// remember position // remember position
@@ -480,9 +484,10 @@ std::string openFolderDialog(const std::string &label, const std::string &path)
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_folder_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); char *open_folder_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_folder_name) if (open_folder_name) {
foldername = std::string(open_folder_name); foldername = std::string(open_folder_name);
g_free( open_folder_name ); g_free( open_folder_name );
}
} }
// remember position // remember position

View File

@@ -374,7 +374,6 @@ void MediaPlayer::execute_open()
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 5); gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 5);
gst_app_sink_set_drop (GST_APP_SINK(sink), true); gst_app_sink_set_drop (GST_APP_SINK(sink), true);
#ifdef USE_GST_APPSINK_CALLBACKS
// set the callbacks // set the callbacks
GstAppSinkCallbacks callbacks; GstAppSinkCallbacks callbacks;
callbacks.new_preroll = callback_new_preroll; callbacks.new_preroll = callback_new_preroll;
@@ -388,15 +387,6 @@ void MediaPlayer::execute_open()
} }
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL); gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false); gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
#else
// connect signals callbacks
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
if (!media_.isimage) {
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
}
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
#endif
// done with ref to sink // done with ref to sink
gst_object_unref (sink); gst_object_unref (sink);

View File

@@ -368,9 +368,6 @@ void Rendering::draw()
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents(); glfwPollEvents();
// no g_main_loop_run(loop) : update global GMainContext
g_main_context_iteration(NULL, FALSE);
} }
void Rendering::terminate() void Rendering::terminate()

View File

@@ -241,7 +241,6 @@ void Stream::execute_open()
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 30); gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 30);
gst_app_sink_set_drop (GST_APP_SINK(sink), true); gst_app_sink_set_drop (GST_APP_SINK(sink), true);
#ifdef USE_GST_APPSINK_CALLBACKS
// set the callbacks // set the callbacks
GstAppSinkCallbacks callbacks; GstAppSinkCallbacks callbacks;
callbacks.new_preroll = callback_new_preroll; callbacks.new_preroll = callback_new_preroll;
@@ -256,15 +255,6 @@ void Stream::execute_open()
} }
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL); gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false); gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
#else
// connect signals callbacks
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
if (!single_frame_) {
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
}
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
#endif
// set to desired state (PLAY or PAUSE) // set to desired state (PLAY or PAUSE)
live_ = false; live_ = false;

View File

@@ -104,8 +104,6 @@
#define COLOR_SLIDER_CIRCLE 0.11f, 0.11f, 0.11f #define COLOR_SLIDER_CIRCLE 0.11f, 0.11f, 0.11f
#define COLOR_STASH_CIRCLE 0.06f, 0.06f, 0.06f #define COLOR_STASH_CIRCLE 0.06f, 0.06f, 0.06f
#define USE_GST_APPSINK_CALLBACKS
#define OSC_PORT_RECV_DEFAULT 7000 #define OSC_PORT_RECV_DEFAULT 7000
#define OSC_PORT_SEND_DEFAULT 7001 #define OSC_PORT_SEND_DEFAULT 7001
#define OSC_CONFIG_FILE "osc.xml" #define OSC_CONFIG_FILE "osc.xml"

View File

@@ -30,7 +30,6 @@
#include "ControlManager.h" #include "ControlManager.h"
#include "Connection.h" #include "Connection.h"
#include "Metronome.h" #include "Metronome.h"
#include "DeviceSource.h"
#if defined(APPLE) #if defined(APPLE)
extern "C"{ extern "C"{
@@ -105,29 +104,24 @@ int main(int argc, char *argv[])
return 1; return 1;
/// ///
/// CONTROLLER INIT /// CONTROLLER INIT (OSC)
/// ///
Control::manager().init(); Control::manager().init();
/// ///
/// METRONOME INIT /// METRONOME INIT (Ableton Link)
/// ///
if ( !Metronome::manager().init() ) if ( !Metronome::manager().init() )
return 1; return 1;
/// ///
/// RENDERING INIT /// RENDERING & GST INIT
/// ///
if ( !Rendering::manager().init() ) if ( !Rendering::manager().init() )
return 1; return 1;
/// ///
/// DEVICES INIT /// IMGUI INIT
///
Device::manager().init();
///
/// UI INIT
/// ///
if ( !UserInterface::manager().Init() ) if ( !UserInterface::manager().Init() )
return 1; return 1;