作者: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. 合并多个python list以及合并多个 django QuerySet 的方法

    在用python或者django写一些小工具应用的时候,有可能会遇到合并多个list到一个 list 的情况.单纯从技术角度来说,处理起来没什么难度,能想到的办法很多,但我觉得有一个很简单而且效率比较 ...

  2. bzoj 2251: [2010Beijing Wc]外星联络 后缀数组

    2251: [2010Beijing Wc]外星联络 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 424  Solved: 232[Submit][ ...

  3. bzoj 3851: 2048 dp优化

    3851: 2048 Time Limit: 2 Sec  Memory Limit: 64 MBSubmit: 22  Solved: 9[Submit][Status] Description T ...

  4. Stanford CoreNLP--Named Entities Recognizer(NER)

    Standford Named Entities Recognizer(NER),命名实体识别是信息提取(Information Extraction)的一个子任务,它把文字的原子元素(Atomic ...

  5. Tmux:终端复用器

    转自Tmux:终端复用器 Tmux 是一个 C 语言编写的终端,它能够在单一窗口中同时访问和控制多个终端.它是一个类似于GNU Screen 的工具.使用它,用户可以在 Linux 系统上管理多个任务 ...

  6. 【BZOJ 1010】 [HNOI2008]玩具装箱toy (斜率优化)

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 9330  Solved: 3739 Descriptio ...

  7. 【POJ2406】 Power Strings (KMP)

    Power Strings Description Given two strings a and b we define a*b to be their concatenation. For exa ...

  8. 李洪强iOS开发Swift篇—09_属性

    李洪强iOS开发Swift篇—09_属性 一.类的定义 Swift与Objective-C定义类的区别 Objective-C:一般需要2个文件,1个.h声明文件和1个.m实现文件 Swift:只需要 ...

  9. View以自身中心旋转的代码解惑

    matrix.preTranslate(-centerX, -centerY); matrix.postTranslate(centerX, centerY); 经常在中心旋转的应用中看到这段代码. ...

  10. Android软件开发之发送短信与系统短信库解析

    今天我和同学们讨论一下Android平台下如何调用系统方法发送短信.接收短信.系统的短信库相关的问题.进入正题,我们先使用Eclipse工具模拟给自己的模拟器发送一条短信.在Eclipse下打开DDM ...