Gstreamer中g_main_loop_new的使用

我们知道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的一个细节

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`gsf_input_stdio_new` 是 GStreamer 的一个函数,用于创建一个从标准输入流(stdin)读取数据的 GStreamer 输入源。它的函数原型如下: ```c GstElement *gsf_input_stdio_new (void); ``` 使用该函数创建的输入源可以通过 `gst_element_set_state` 函数设置为播放状态,然后就可以从标准输入流读取数据并进行处理了。通常情况下,我们会将标准输入重定向到一个文件或管道,以便从文件或管道读取数据。 以下是一个示例代码,用于创建一个从标准输入读取数据并将其写入文件的 GStreamer pipeline: ```c #include <gst/gst.h> int main(int argc, char *argv[]) { GstElement *pipeline, *input, *output; GstBus *bus; GstMessage *msg; GMainLoop *loop; /* Initialize GStreamer */ gst_init (&argc, &argv); /* Create the elements */ pipeline = gst_pipeline_new ("mypipeline"); input = gsf_input_stdio_new (); output = gst_element_factory_make ("filesink", "mysink"); /* Set the properties */ g_object_set (G_OBJECT (output), "location", "output.txt", NULL); /* Add the elements to the pipeline */ gst_bin_add_many (GST_BIN (pipeline), input, output, NULL); /* Link the elements */ if (!gst_element_link (input, output)) { g_printerr ("Failed to link elements\n"); return -1; } /* Set the pipeline to playing state */ gst_element_set_state (pipeline, GST_STATE_PLAYING); /* Create a main loop and attach it to the default context */ loop = g_main_loop_new (NULL, FALSE); /* Listen for messages on the bus */ bus = gst_element_get_bus (pipeline); gst_bus_add_watch (bus, (GstBusFunc) gst_message_print, loop); gst_object_unref (bus); /* Run the main loop */ g_main_loop_run (loop); /* Clean up */ gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (GST_OBJECT (pipeline)); g_main_loop_unref (loop); return 0; } ``` 在上面的例子,我们创建了一个 GStreamer pipeline,其包含一个从标准输入读取数据的输入源和一个将数据写入文件的输出元素。我们将输出元素的 `location` 属性设置为 `output.txt`,这样数据就会被写入到 `output.txt` 文件。最后,我们将 pipeline 设置为播放状态,并运行一个主循环来等待事件。当用户输入数据时,数据会被从标准输入读取并写入到输出文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值