我们知道g_main_loop_new
用于创建一个main loop对象,g_main_loop_run
则是进入主循环,它会一直阻塞在这里,直到让它退出为止,有事件时,它就处理事件,没事件时就阻塞。
在Gstreamer中使用g_main_loop_new的地方,基本上都可以看到如下代码:
/* Create a GLib Main Loop and set it to run */
main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (main_loop);
这样的用法,在大多数情况下都没有问题,比如自己创建的player,或者pipeline里面几乎都是这么用的。
在gst-play.c里面就可以看到创建启动mainloop的代码:
// FALSE是不进入running状态
// NULL使用default context
play->loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (play->loop);
然后进入这篇的主题,在新项目中把这段代码放到chromium中的时候就出问题了:
Check failed: checker.CalledOnValidThread(&bound_at).
使用std::thread启动main loop线程,不行:
std::unique_ptr<std::thread> gstThread;
gst_thread.reset(new std::thread(std::bind(Decoder::mainLoop, gst_video_dec_.get())));
- 这里
Decoder::mainLoop
是Decoder类的mainloop()函数
使用base::Thread,也不行,报同样的错误:
std::Thread thread("gst video");
thread.Start();
thread.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&Decoder::mainLoop, base::Unretained(this)));
// stop thread
thread.Stop();
所以不是使用的用哪个thread实现的问题,而是另有原因。最后看到这篇基于GMainloop的GThread创建、退出与资源释放文档,增加了GMainContext的创建后就可以了,代码如下:
std::unique_ptr<std::thread> gst_thread;
GMainLoop *main_loop;
GMainContext *context;
context = g_main_context_new();
main_loop = g_main_loop_new (context, FALSE);
启动thread还是用std::thread:
gst_thread.reset(new std::thread(std::bind(&Decoder::mainLoop, this)));
大功告成,这时候就可以工作起来了!
GMainLoop介绍
再来回顾下GMainLoop,这段内容来自官方文档。
GMainLoop管理GLib和GTK+应用程序的所有可用事件源。这些事件可以来自任意数量的不同类型的源,例如文件描述符(普通文件、管道或套接字)和超时。
/**
* GMainLoop:
*
* The `GMainLoop` struct is an opaque data type
* representing the main event loop of a GLib or GTK+ application.
*/
typedef struct _GMainLoop GMainLoop;
常用API:
GMainLoop* g_main_loop_new (GMainContext *context,
gboolean is_running);
GMainLoop* g_main_loop_ref (GMainLoop *loop);
void g_main_loop_unref (GMainLoop *loop);
void g_main_loop_run (GMainLoop *loop);
void g_main_loop_quit (GMainLoop *loop);
gboolean g_main_loop_is_running (GMainLoop *loop);
GMainContext* g_main_loop_get_context (GMainLoop *loop);
GMainContext* g_main_context_new (void);
GMainContext* g_main_context_ref (GMainContext *context);
void g_main_context_unref (GMainContext *context);
GMainContext* g_main_context_default (void);
为了允许在不同的线程中处理多个独立的源集,每个源都与一个GMainContext关联。GMainContext只能在单个线程中运行,但sources可以添加到其中,也可以从其他线程中删除。
每个event source会被分配优先级,默认优先级G_priority_default为0。小于0的值表示优先级更高。大于0的值表示优先级较低。高优先级source的事件始终在低优先级source事件之前处理。
添加初始事件源后,调用g_main_loop_run()
开始主循环。这将持续检查来自每个事件源的事件并将其。最后,对于任何一个源调用g_main_loop_quit()
都会导致退出主循环。
gst-play中的用法
// 创建main loop,FALSE是不进入running状态
play->loop = g_main_loop_new (NULL, FALSE);
// gst_bus_add_watch注册play_bus_msg回调,add watch内部以glib的Source机制实现
// bus类型的source通过attach,attach到Default Main Context
play->bus_watch = gst_bus_add_watch (GST_ELEMENT_BUS (play->playbin),
play_bus_msg, play);
// 添加timeout源
play->timeout = g_timeout_add (100, play_timeout, play);
// 启动main loop
g_main_loop_run (play->loop);
// 退出main loop
g_main_loop_quit (play->loop);
// 从Default context中删除bus_watch和timeout源
g_source_remove (play->bus_watch);
g_source_remove (play->timeout);
g_main_loop_unref (play->loop);
webrtc中的用法
static gpointer
_gst_pc_thread (GstWebRTCBin * webrtc)
{
PC_LOCK (webrtc);
/* 创建 GMainContext*/
webrtc->priv->main_context = g_main_context_new ();
/* 创建 GMainLoop*/
webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
PC_COND_BROADCAST (webrtc);
g_main_context_invoke (webrtc->priv->main_context,
(GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
/* Having the thread be the thread default GMainContext will break the
* required queue-like ordering (from W3's peerconnection spec) of re-entrant
* tasks */
g_main_loop_run (webrtc->priv->loop);
PC_LOCK (webrtc);
g_main_context_unref (webrtc->priv->main_context);
webrtc->priv->main_context = NULL;
g_main_loop_unref (webrtc->priv->loop);
webrtc->priv->loop = NULL;
PC_COND_BROADCAST (webrtc);
PC_UNLOCK (webrtc);
return NULL;
}
启动thread和退出thread:
static void
_start_thread (GstWebRTCBin * webrtc)
{
PC_LOCK (webrtc);
webrtc->priv->thread = g_thread_new ("gst-pc-ops",
(GThreadFunc) _gst_pc_thread, webrtc);
while (!webrtc->priv->loop)
PC_COND_WAIT (webrtc);
webrtc->priv->is_closed = FALSE;
PC_UNLOCK (webrtc);
}
static void
_stop_thread (GstWebRTCBin * webrtc)
{
PC_LOCK (webrtc);
webrtc->priv->is_closed = TRUE;
g_main_loop_quit (webrtc->priv->loop);
while (webrtc->priv->loop)
PC_COND_WAIT (webrtc);
PC_UNLOCK (webrtc);
g_thread_unref (webrtc->priv->thread);
}
更多的使用细节还可以从glib/tests/mainloop.c
代码中学习。
mainloop退出同步
resource deadlock avoided' during 'pthread_join (pt->system_thread, null)'
发现在pipeline结束的时候通过g_thread_join(thread_)
等待mainloop退出会报这个错误,正确的做法是通过g_cond进行同步。
就在前面webrtcbin(gst-plugins-bad/ext/webrtc/gstwebrtcbin.c
)中的代码中可以看到:
mainloop中g_main_loop_run退出后broadcast:
g_main_loop_run (webrtc->priv->loop);
PC_LOCK (webrtc);
g_main_context_unref (webrtc->priv->main_context);
webrtc->priv->main_context = NULL;
g_main_loop_unref (webrtc->priv->loop);
webrtc->priv->loop = NULL;
PC_COND_BROADCAST (webrtc);
PC_UNLOCK (webrtc);
退出时等待:
static void
_stop_thread (GstWebRTCBin * webrtc)
{
PC_LOCK (webrtc);
webrtc->priv->is_closed = TRUE;
g_main_loop_quit (webrtc->priv->loop);
while (webrtc->priv->loop)
PC_COND_WAIT (webrtc);
PC_UNLOCK (webrtc);
g_thread_unref (webrtc->priv->thread);
}
参考
基于GMainloop的GThread创建、退出与资源释放
The Main Event Loop
GStreamer Bus的一个细节