作者:fengcc 原创文章 转载请注明出处


GStreamer 是一个基于流水线的多媒体框架,基于 GObject,以 C 语言写成。

凭借 GStreamer,程序员可以很容易地创建各种多媒体功能组件,包括简单的音频回放,音频和视频播放,录音,流媒体和音频编辑。基于流水线设计,可以创建诸如视频编辑器、流媒体广播和媒体播放器等等的很多多媒体应用。


GstTask/GstTaskPool — streaming threads

Gstreamer 将GstElementGstPad中关于数据流处理的线程封装成 GstTask,并提供gst_task_start(),gst_task_pause(),gst_task_stop()等接口,使数据流的处理更加方便。例如:GstPad通常会启动一个GstTask从另一个 pad 上拉数据或者推数据到另一个 pad。

下面根据它提供的几个接口来详细介绍GstTask内部的实现。


(1)创建任务

GstTask *
gst_task_new (GstTaskFunction func,
gpointer user_data,
GDestroyNotify notify);

创建一个任务,该任务稍后将会在新线程里重复调用func函数,以user_data 为参数。注意,此时还没有创建或者启动一个新的线程。

GstTaskgst_task_class_init() 函数中会调用gst_task_pool_get_default()函数,该函数代码如下:

GstTaskPool *
gst_task_pool_get_default (void)
{
static GstTaskPool *pool = NULL; if (g_once_init_enter (&pool)) { //整个程序周期只会进入一次
GstTaskPool *_pool = gst_task_pool_new (); gst_task_pool_prepare (_pool, NULL);
g_once_init_leave (&pool, _pool);
} return gst_object_ref (pool);
}

它利用g_once_init_enter()/g_once_init_leave()操作来确保整个程序的运行周期中只会创建一个默认GstTaskPool结构,GstTaskPool结构是对 glib 线程池的封装。之后再调用gst_task_pool_prepare()函数,该函数会调用GstTaskPooldefault_prepare()函数来创建一个默认的 glib 线程池,使用的是上面提到的g_thread_pool_new()接口。

传递给g_thread_pool_new()的第一个参数为default_func函数:

static void
default_func (TaskData * tdata, GstTaskPool * pool)
{
GstTaskPoolFunction func;
gpointer user_data; func = tdata->func;
user_data = tdata->user_data;
g_slice_free (TaskData, tdata); func (user_data); //运行自定义的任务
}

它的做法很简单,拆解传进来的参数,调用相应的函数,从而让线程池中的每个线程运行自定义的任务。

当我们调用gst_task_new()函数创建一个新的任务时,会触发GstTaskgst_task_init()函数,该函数会将GstTask结构体中的GstTaskPool *pool变量指向刚才创建的默认的GstTaskPool。所以,整个程序中所有的GstTask 依赖的都是同一个GstTaskPool结构,即所有的GstTask线程都运行在同一个线程池中。


(2)启动任务

gboolean
gst_task_start (GstTask *task);

或:

gboolean
gst_task_set_state (GstTask *task,
GstTaskState state);

对于GstTask的任务,有三种状态:

  • GST_TASK_STARTED:任务已经启动,正在相应的线程中运行。
  • GST_TASK_STOPPED:任务已经停止,此时还没有启动相应的线程,或者线程已经运行结束退出。
  • GST_TASK_PAUSED:任务暂停。任务对应的线程没有退出,处于暂停状态,其实就是让线程阻塞在某个条件变量上。

gst_task_start()实际上就是调用gst_task_set_state (task, GST_TASK_STARTED),将任务设置为开始状态。

gst_task_set_state()会先记录任务的原始状态为old,再将任务设置为新的状态,然后根据old,执行相关的操作,关键代码如下:

/* if the state changed, do our thing */
old = GET_TASK_STATE (task);
if (old != state) {
SET_TASK_STATE (task, state); switch (old) {
case GST_TASK_STOPPED:
/* If the task already has a thread scheduled we don't have to do
* anything. */
if (G_UNLIKELY (!task->running) &&
(!task->priv->scheduleable || (task->priv->should_schedule
&& state == GST_TASK_STARTED)))
res = start_task (task);
break;
case GST_TASK_PAUSED:
/* when we are paused, signal to go to the new state */
GST_TASK_SIGNAL (task);
break;
case GST_TASK_STARTED:
/* if we were started, we'll go to the new state after the next
* iteration. */
break;
}
}
  • 如果旧状态是GST_TASK_STOPPED,则新状态肯定是 GST_TASK_STARTED,则调用start_task函数,该函数稍候会解释。
  • 如果旧状态是GST_TASK_PAUSED,则增加相应的条件变量,这样,任务对应的线程就会结束在此条件变量的阻塞,从而完成状态转换。
  • 如果旧状态是GST_TASK_STARTED,这个注释的解释没有看懂,还要继续琢磨一下,抱歉。

start_task()函数中,以gst_task_func()函数为参数调用gst_task_pool_push()gst_task_pool_push()是对default_push()函数的封装。default_push()函数代码如下:

static gpointer
default_push (GstTaskPool * pool, GstTaskPoolFunction func,
gpointer user_data, GError ** error)
{
TaskData *tdata; tdata = g_slice_new (TaskData);
tdata->func = func;
tdata->user_data = user_data; GST_OBJECT_LOCK (pool);
if (pool->pool)
g_thread_pool_push (pool->pool, tdata, error);
else {
g_slice_free (TaskData, tdata);
}
GST_OBJECT_UNLOCK (pool); return NULL;
}

funcuser_data封装成TaskData结构体,作为参数调用上面提到的g_thread_pool_push()函数将任务插入到线程池的任务列表中。TaskData结构体的拆解在上面提到的default_func()函数中,线程池中的每个线程启动时都会调用该函数拆解参数,然后线程便切换到gst_task_func()函数运行。

注意:这里的funcgst_task_func()函数,user_data是任务对应的GstTask结构体。而使用gst_task_new()创建任务时传入的自定义函数和参数是分别保存在GstTasktask->functask->user_data成员变量中,将会在gst_task_func()中采用task->func (task->user_data)的方式调用。

最后,每个线程中运行的其实都是gst_task_func()函数,该函数关键代码如下:

while (G_LIKELY (GET_TASK_STATE (task) != GST_TASK_STOPPED)) {
GST_OBJECT_LOCK (task); if (G_UNLIKELY (priv->scheduleable
&& GST_TASK_STATE (task) == GST_TASK_PAUSED)) {
GST_OBJECT_UNLOCK (task);
break;
} while (G_UNLIKELY (!priv->scheduleable
&& GST_TASK_STATE (task) == GST_TASK_PAUSED)) {
g_rec_mutex_unlock (lock); GST_TASK_SIGNAL (task);
GST_INFO_OBJECT (task, "Task going to paused");
GST_TASK_WAIT (task);
GST_INFO_OBJECT (task, "Task resume from paused");
GST_OBJECT_UNLOCK (task);
/* locking order.. */
g_rec_mutex_lock (lock);
GST_OBJECT_LOCK (task);
} if (G_UNLIKELY (GET_TASK_STATE (task) == GST_TASK_STOPPED)) {
GST_OBJECT_UNLOCK (task);
break;
} else {
GST_OBJECT_UNLOCK (task);
} // 调用用户自定义的函数。
task->func (task->user_data); if (priv->scheduleable)
break;
}

如果任务被设置成GST_TASK_STOPPED状态,则退出循环,结束线程运行。若任务为暂停状态,则在对应的条件变量上阻塞,否则,就一直循环调用用户自定义函数处理数据流。

Gstreamer 数据流线程(GstTask / GstTaskPool)分析的更多相关文章

  1. JAVA线程池的分析和使用

    1. 引言 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行.第三:提 ...

  2. [转]ThreadPoolExecutor线程池的分析和使用

    1. 引言 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 第 ...

  3. Java 线程池原理分析

    1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等 ...

  4. 线程组ThreadGroup分析详解 多线程中篇(三)

    线程组,顾名思义,就是线程的组,逻辑类似项目组,用于管理项目成员,线程组就是用来管理线程. 每个线程都会有一个线程组,如果没有设置将会有些默认的初始化设置 而在java中线程组则是使用类ThreadG ...

  5. ThreadPoolExecutor线程池的分析和使用

    1. 引言 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 第 ...

  6. Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  7. 聊聊并发(三)Java线程池的分析和使用

    1.    引言 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立即执行. ...

  8. java并发包&线程池原理分析&锁的深度化

          java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...

  9. 【Java线程与内存分析工具】VisualVM与MAT简明教程

    目录 前言 VisualVM 安装与配置 本地使用 远程监控 MAT 使用场景 安装与配置 获得堆转储文件 分析堆转储文件 窥探对象内存值 堆转储文件对比分析 总结 前言 本文将简要介绍Java线程与 ...

随机推荐

  1. 最简单的CRC32源码---逐BIT法

    CRC其实也就那么回事,却在网上被传得神乎其神.单纯从使用角度来说,只需要搞明白模二除法,再理解一些偷懒优化的技巧,就能写出自己的CRC校验程序. 下面的代码完全是模拟手算过程的,效率是最低的,发出来 ...

  2. 队列的实现(JAVA)

    定义    队列(queue)是一种特殊的线性表,它只允许在表的前端进行删除,在表的后端进行插入. 进行插入端的称为队尾,进行删除端的称为队头.队列是先进先出原则的.队列的实现同样可以 使用两种方式来 ...

  3. StorSimple 简介

     2014年 10月 28日,星期二 PRACHEETI NAGARKAR DESAI 混合云存储业务资深项目经理 在此我很荣幸地宣布StorSimple解决方案已经在中国正式上市.该方案为IT部 ...

  4. 微软开放技术发布开源 Jenkins 插件以将 Windows Azure Blob 服务用的开作存储库

     发布于 2014-02-10 作者 陈 忠岳 持续集成 (CI) 的历史源远流长, 其宗旨在于软件团队在敏捷环境中不断将他们的工作整合为持续构建.管理 CI 进程的工具已存在一段时间.过去几年中 ...

  5. 计算几何(凸包模板):HDU 1392 Surround the Trees

    There are a lot of trees in an area. A peasant wants to buy a rope to surround all these trees. So a ...

  6. android学习之BUG——The connection to adb is down, and a severe error has occured.

    开始--运行--cmd 进入命令提示符 输入netstat -ano 即可看到所有连接的PID 之后在任务管理器中找到这个PID所对应的程序如果任务管理器中没有PID这一项,可以在任务管理器中选&qu ...

  7. 数学之美 zt

    数学是美丽的,哪里有数哪里就有美. 数学的定义是研究数量关系和空间形式的一门科学.但有句名言说:数学比科学大得多,因为它是科学的语言.数学不仅用来写科学,而且可用来写人生.所以说数学是一切学科的基础, ...

  8. Enter键提交表单

    input type="submit"在360浏览器上不能提交   用了这个 <input type="button" class="btn b ...

  9. strcpy,memcpy,内存块重叠

    前段时间准备面试,看了一些库函数的实现,在看到memcpy时,发现有处理source和destination所指内存有重叠的情况,而strcpy没有,特别模仿库函数写了这个函数,并进行了测试.以下是具 ...

  10. windows下Qt5.1 for android开发环境配置(PS:Qt5.2出来了哈,稳定)

    说明:以下开发环境配置过程参考了这些文章: http://qt-project.org/wiki/building_qt-android_on_windows http://www.hyarm.com ...