g_main_loop_run 阻塞 Qthread 并且不允许停止视频
Posted
技术标签:
【中文标题】g_main_loop_run 阻塞 Qthread 并且不允许停止视频【英文标题】:g_main_loop_run blocks the Qthread and does not allow to stop video 【发布时间】:2016-03-23 08:48:53 【问题描述】:我为 gstreamer 创建了一个单独的类来流式传输视频。
此类通过使用 moveToThread() 在单独的线程上运行。
我正在使用 Qt5.5 进行开发。
当我在主线程上发出 startcommand 时,Qthread 启动并且 gstreamer 使用 g_main_loop_run
流式传输视频。这绝对没问题。但不知何故g_main_loop_run
阻塞了线程,当我发出信号以停止来自主线程的视频时,它不会执行 gstreamer 类中的插槽。
有人可以告诉我如何解决这个问题吗?我可以用其他命令替换g_main_loop_r
un,也可以使用g_main_loop_quit( gloop )
;以另一种方式。
void StreamingVideo::slotStartStream() // this slot called on start of thread from main thread
if( !isElementsLinked() )
qDebug() << " we are emitting in dummy server ";
//emit sigFailed( "elementsFailed" ); // WILL CONNECT IT WITH MAIN GUI ONXCE CODE IS FINISHED
return;
gst_bus_add_watch( bus, busCall, gloop );
gst_object_unref( bus );
//proper adding to pipe
gst_bin_add_many( GST_BIN( pipeline ), source, capsFilter, conv, videoRate, capsFilterRate,
clockDisplay, videoEnc, udpSink, NULL
);
//proper linking:
gst_element_link_many( source, capsFilter, conv, videoRate, capsFilterRate, clockDisplay, videoEnc, udpSink, NULL );
g_print("Linked all the Elements together\n");
gst_element_set_state( pipeline, GST_STATE_PLAYING );
// Iterate
g_print ("Running...\n");
emit sigStartStream(); // signal to main thread to issue success command . works fine
g_main_loop_run( gloop );
g_print ("Returned, stopping playback\n");
//gst_element_set_state (pipeline, GST_STATE_NULL);
if( g_main_loop_is_running( gloop ) )
qDebug() << " in g_main_loop_is_runnung emiting signal ";
emit sigStartStream();
if( !g_main_loop_is_running( gloop) )
qDebug() << "in not gmain running thread id";
qDebug() << QThread::currentThreadId();
void StreamingVideo::slotStopStream() // THIS SLOT IS NOT CALLED WHEN VIDEO RUNNING
qDebug() << " we are planning to stop streaming stramingVideo::slotStopStream ";
g_print ("Returned, stopping playback\n");
g_main_loop_quit( gloop );
gst_element_set_state (pipeline, GST_STATE_NULL);
// g_main_loop_quit( gloop );
releaseMemory();
emit sigStopStream(); // signal to main thread to issue message saying video has stopped.
// 主线程某处
threadStreaming = new QThread();
streamVideo = new StreamingVideo( "127.0.0.1"); // we will automate this ip address later on
streamVideo->moveToThread( threadStreaming );
connect( threadStreaming, SIGNAL( started() ), streamVideo, SLOT( slotStartStream() ) );
connect( streamVideo, SIGNAL( sigStopStream() ), threadStreaming, SLOT( quit() ) );
connect( streamVideo, SIGNAL( sigStopStream() ), streamVideo, SLOT(deleteLater() ) );
connect( threadStreaming, SIGNAL( finished() ), threadStreaming, SLOT(deleteLater() ) );
connect( streamVideo, SIGNAL( sigStartStream() ), this, SLOT( slotTrueStreamRun() ) );
connect( streamVideo, SIGNAL( sigStopStream() ), this, SLOT( slotFalseStreamRun() ) );
connect( this, SIGNAL( sigMopsCamStopCmd() ), streamVideo, SLOT(slotStopStream() ) );
threadStreaming->start();
【问题讨论】:
为什么不直接使用 QMediaPlayer 而不是尝试将 Qt 和 Gtk+ 拼凑在一起? 不幸的是,项目要求仅将 gstreamer-0.10 n 与 qt 一起使用。大声笑 所以你不能在没有 g_main_loop_run 的情况下使用 gstreamer? 在一个适当的设计中,你不应该杀死线程,事件循环,不管是什么,都会有某种终止方式,即使你没有线程之间的异步/排队通信.例如,您可以使用原子整数,并在每次循环迭代时检查它,您可以从主线程设置,以便流线程退出其循环。 @ddriver,问题是一旦 g_main_loop _run 开始执行它会消耗所有资源,所以即使检查或发送信号它也无法执行 【参考方案1】:感谢 OP 提出了 GLib/Qt interop 的一个重要问题,该问题在 Internet 上没有得到充分的介绍。这就是 GMainLoop
在 QThread
中的工作方式:
QObject context;
QThread thread;
void* arvMainLoop;
...
context.moveToThread(&thread);
QObject::connect(&thread, &QThread::started, &context, [&]()
GMainLoop* loop = g_main_loop_new(NULL, FALSE);
arvMainLoop = reinterpret_cast<void*>(loop);
GMainContext* context = g_main_loop_get_context(loop);
// TODO Maybe make interruption checks less frequent here.
while (!thread.isInterruptionRequested())
g_main_context_iteration(context, FALSE);
g_main_loop_quit(loop);
);
thread.start();
...
thread.requestInterruption();
thread.quit();
thread.wait();
GMainLoop* loop = reinterpret_cast<GMainLoop*>(arvMainLoop);
g_main_loop_unref(loop);
【讨论】:
【参考方案2】:没有必要在 Qt 应用程序中使用 glib 的 GMainLoop。 Qt 有自己的 GMainLoop (QEventLoop) 版本,您可以将其视为 exec() 方法。
例如,如果你有一个 QGuiApplication 类/派生类,你可以调用它的 exec() 方法来启动它的主事件循环。 http://doc.qt.io/qt-5/qguiapplication.html#exec
同样,如果你有一个 QThread 类/派生类,你可以调用它的 exec() 方法来启动它的本地事件循环。 http://doc.qt.io/qt-4.8/qthread.html#exec
总结,任何需要事件循环来触发其进程的glib代码(例如g_bus_own_name,在glib中你需要调用GMainLoop才能启动dbus。如果你把它放在Qt代码中,你只需要调用exec() 而不是处理 GMainLoop。
长话短说,只有一个事件循环,但不同的组织(例如 gnome、qt)做出了不同的实现
【讨论】:
这个不行,没有GMainLoop,你不能接受来自gstreamer的信号【参考方案3】:没有必要依赖aGMainLoop
。没有g_main_loop_run()
,管道应该可以正常运行。
您需要注意的一件事是,您的主 Qt 应用程序循环要么必须轮询管道的总线以获取消息,要么使用 gst_bus_set_sync_handler
设置回调函数,以便在消息到达时调用总线。对于后者,您必须注意,然后从管道的线程而不是应用程序的线程调用此函数。不过在这里发出信号应该没问题。
如果您想采用线程方式,则必须在运行GMainLoop
的应用程序中手动创建一个线程。也有可能——不过对我来说,上面的方法看起来更简单、更干净。
【讨论】:
【参考方案4】:免责声明:我对 GLib/GTK 一无所知,但我很快用谷歌搜索了一些东西——一些灵感来自这篇 SO 帖子 add callback for separate g_main_loop 和 https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-timeout-add 的文档
问题是您正在处理两个事件循环——线程的 Qt 事件循环,隐式输入到 QThread::run ()
中,以及 GLib 循环,您手动输入到 slotStartStream ()
中。您从主线程发送的所有 Qt 信号都必须通过 Qt 调度程序——因此您必须给 Qt 一些机会来处理它们,这意味着 GLib 循环需要定期将控制权移交给 Qt。所以想法是:安装一个 GLib 将定期调用的回调(例如一个简单的计时器),并从该回调发出一个 processEvents ()
函数让 Qt 完成它的工作。
我会尝试这样的:
gboolean myCallback (gpointer user_data)
// The Qt thread is passed as a parameter during callback installation
QThread* workerThread = reinterpret_cast<QThread*> (user_data);
// Access the Qt thread's event dispatcher...
QAbstractEventDispatcher* disp = workerThread->eventDispatcher ();
// ...and let it do its work
disp->processEvents (QEventLoop::AllEvents);
return G_SOURCE_CONTINUE;
void StreamingVideo::slotStartStream ()
[...]
g_print ("Running...\n");
// Install callback to intertwine Qt's thread-local eventloop with our GLib loop
g_timeout_add (50, myCallback, QThread::currentThread ());
emit sigStartStream(); // signal to main thread to issue success command . works fine
g_main_loop_run( gloop );
g_print ("Returned, stopping playback\n");
[...]
现在,我不知道这是否能解决你所有的问题(事实上我很确定它不会:-)),但我认为你至少会看到你的slotStopStream ()
实际上是在这些修改之后调用(在工作线程的范围内)。
总而言之,这是一个相当糟糕的设置,但它可能会奏效。
【讨论】:
其实,修改我的代码后,它进入了 slotStopStream 但执行 g_main_loop_quit 后,视频仍然没有停止以上是关于g_main_loop_run 阻塞 Qthread 并且不允许停止视频的主要内容,如果未能解决你的问题,请参考以下文章