Gstreamer架构分析

1.Gstreamer架构图:

A.Gstreamer是一个提供媒体处理能力的强大的库。

B.通过gstreamer,应用程序可以通过简单的接口处理媒体,不必理会具体的媒体处理过程。

 

Gsteamer包含以下包:

--gstreamer:the core package。//核心库,支撑整个框架,具体做事如解析文件、解码、输出。交给‘小弟’plugin去做。

--gst-plugins-base:包含基本element。

--gst-plugins-good:LGPL的高质量element。

--gst-plugins-ugly:高质量的element,可能有法律问题。

--其他

2.Gstreamer基本概念

Elements:

--  Gstreamer中最基本的元件,他的类型可能是sources,filters,muxers,demuxers,codecs或者sinks。

Pads:

--  element的输出或者输入。

Bins:

--  包含一组element的容器。

Pipelines:

--  特殊的bin,包含一组能完成某个任务需要的element,可以允许它包含的element可执行。

     (一般是将多个elements加入到pipeline里面)

Buses:

--  处理pipeline线程消息,并通知应用程序的系统。

--  每条pipeline缺省都有一个bus。

 

他们之间的关系,大致如下图:

(个人的理解)

Gstreamer编程,首先创建基本元件,pipeline,filesrc,decoder,alsasink等,然后把这些元件加入到pipeline中,并链接起来,最后改变pipeline的状态,就可以启动对媒体数据的处理。

在不知道文件类型的情况下,也可以动态创建元件,调用typefind函数,他能找出能够解析它的插件,然后连接到上一元件的后面,以此类推,根据前面不同的类型,在后面选择不同的插件连接到后面。最后同样把这些元件加入到一个pipeline中,并链接起来,这时改变这个pileline的状态就可以启动对数据的处理。

3 构建Gstreamer

3.1 初始化

GST库必须先初始化,调用gst_init()。

3.2 单元elements

GstElement是最重要的对 象。一些高级对象也是从它派生出来的。有好几种类型的elements,必须分清楚了。

1.源单元

source单元是数据的产生方, 对应一个源pad。一般画在右边。

源单元只能生产数据,不能接收数据

2.中间单元

中间单元包括过滤器,转换器,复用器,解复用器,编解码器等。

它有多个源pad,对应多个目标pad。

3.目标单元

只能接受数据。

4.创建和使用单元

通过factory_make和gst_object_unref来创建及释放单元。make需要两个参数,一个是工厂名,一个是单元名。工厂名实际就是插件名,所以需要先加载插件上来,才能创建对应的单元。

单元继承所有Gobject的属性,所以可以当做Gobject来处理。

单元有属性,单元还能触发信号,所以必须关注这些。

作为工厂,其功能还不仅限于创建单元,一个工厂有属性,它知道自己能创建怎样的单元。

其实就是这个插件知道自己能创建怎样的单元。可能需要看了插件编写才真正知道。

5.链接单元

单元必须链接起来,才能协同工作。

源单元à中间单元à目标单元

l        必须先加到管道后才能link起来。

l        不在同一bin中的不能link

6.单元状态

单元链接好后,啥事也不会发生,除非设置单元状态。单元一共有四种状态:

l        GST_STATE_NULL:默认 状态,内部会释放单元的所有资源,其实就是初始状态。

l        XXX_READY:就绪状态,分配 资源,打开设备。但是流不会打开,所以此时流信息都是零。如果之前打开了流,在这状态中将会被关闭,流信息都会被重设。

l        XXX_PAUSED:已经打开了 流,但是暂时不处理它。这个时候可以去修改例如seek位置等流信息。时间轴停止

l        XXX_PLAYING:时间轴运 行。设置为这个状态后,整个流程就开始启动了。内部会将消息发送从管道所在的线程转移到应用程序线程。(?)

通过set_state函数设置状态,GST会平滑设置,例如从PLAY设置为NULL,将平滑设置READY和PAUSED。

3.4 Bins

Bin不仅是一个单元的集合,更是 集成了对内部单元的管理。管道是一种特殊的bin,实际要播放一个视音频就得用到管道。管道能独立在后台运行。

1.创建Bin

Bin从element派生而来,所以创建 单元的函数均可创建Bin,但一般用两个更方便的函数:

l        gst_bin_new

l        gst_pipeline_new

Bin是一个集合,需要将单元加入 进去,也可以删除。

l        gst_bin_add,gst_bin_remove

注意,一旦加入到Bin,单元的所有者就变为Bin了,所以删除Bin的话,内部的单元也会相应减少引用。

2. 定制化Bin

还是得看插件编程指南才能真正理解。

3.5 Bus

1. 总论

Bus的好处是可以把pipeline所在的线程的消息 路由到应用程序指定的那个Context中去。

bus上要加入监控回调函数。通过

l        gst_bus_add_watch/gst_bus_add_signal_watch

l        要从bus中取消息,得调用gst_bus_pop/peek/pop

注意,Bus发出的消息是GstMessage结构,值得解释。

Bus含一个队列,每次post一个消息就加到队列里,然后出发maincontext的wakeup。 这样就完成了将消息路由到maincontext去了。因为maincontext等待的有这个bus队列。

有没有办法不用默认context呢?有一个函数将message转换为signal去emit:

l        gst_bus_async_signal_func:

这里要区分下message和signal。signal是Gobject系统提供的,message是GST提供的。message的处理是异步的,而signal的处理是同步的。

如果你不想写很多switch来区别message的话,那么另外一种办法就是注册对应的signal到系统。另外,如果不使 用mainloop的话,异步消息-信号不会发出去。

bus的消息分为异步和同步两类。

l        异步信号是通过加入到mainloop中的Gsource来触发的,所以必须有mainloop再运行才可

l        同步信号必须先注册一个同步信号处理函数才可。

需要自己设置一个同步信号处理函数,在那里触发另外一个context,并且调用上面这个函 数发送signal。

2.  message类型

描述一个message有以下:

l        Message的来源:记录是从哪个 单元发出来的

l        类型:

l        时间戳

比较重要的是类型信息:有以下几类类型

l        error,warning,info等 信息:需要调用对应API进行解析

l        EOS:文件结尾,需要重设管道状态 等

l        Tag:标签信息(其实就是媒体信 息,比如长度,采样率等)State变化:

l        缓存信息:调用gst_bus_get_structure来解析缓存信息

l        单元信息:某些单元会发送特殊的信息应用程序自己的信息

3.6 Pads和属性

Pads很关键,代表了单元的出入 口。标示Pads特性的有两个:

l        方向,从单元内角度看,sink收数据,source发数据

l        可用性(availability)

方向好说,只有sink和source两种,可用性是一个新概念。主要代表这个pad是存活期的。

分三种:

l        pad一直存在

l        动态存在:有时候有,有时候没有,动态创建和删除

l        响应存在:根据外界要求来创建

这个可用性是针对媒体文件类型的一种简化表示。下面针对这个具体讲述,pad的可用性非常重要。

1. 动态pad

为何会有动态之说?原因很简单。例如播放音频的时候,要动态检测有几路音频,然后再创建对应的pad。

程序里边应该绑定一个消息处理函数到动态pad的创建通知上。

2. 响应pad

响应用户要求而创建的pad。必须从一个支持创建这种类型的单元中去创建,调用

l        gst_element_get_request_pad

这个element必须支持Request这种,这个熟悉由插件注册的时候指定的。

还有一种就是查找相容的pad

l        get_element_get_compatible_pad,根据源pad和caps来从单元中找一个相容的。

3. Pad的属性

刚才提到过,查找相容的pad,那么相容是怎么判断和体现的呢?pad有自己的能力熟悉(Capabilities)。

pad的能力熟悉是和pad模板以及pad绑定一起的。pad模板估计就是一个pad工厂。

一个pad有很多不同的能力,这个是最原始的信息。但是具体工作后,一个pad要和别的pad协商,大家按照规定的能力办 事。这样,pad的能力就是协商后的能力了。

能力在GST中用GstCaps来表示。

GstCaps含一到多个Gstructure,一个Gstructure代表一种pad能处理的媒体类型。

4. GST中属性和值的表示

GST除了使用GLIB中的数据类型外,还单独定 义了一些数据类型,用来表示属性值。

值得注意的有:

l        GST_TYPE_INT_RANGE:范围值

l        GST_TYPE_LIST:包含任 何基本类型都可以

l        GST_TYPE_ARRAY:只能 包含相同的类型

5. Caps的用处

Caps实际的用处很多,其实就是 一个寻找匹配pad或element之用。

Caps中有一项描述媒体信息的, 叫metadata。如何从caps中获取条目呢?

caps中存的是structure条 目,一个structure代表一种能力

gst_caps_get_structure/size

根据条目多少和属性,caps可分为:

l        简单caps:只含一条structure

l        固定caps:含一条structure,并且属性值没有range之类可变化的

l        任意caps和空caps是两种特殊cap

 

6. 创建过滤器使用的caps

刚才讲的全是从单元中获取caps,都是已经弄好了的。那么如果想动态创建caps该如何做呢?

l        gst_caps_new_simple:创建simple的

l        xx_full:创建n个structure的caps

这还只是创建pad,要把src和dst通过过滤单元链接起来,用

gst_element_link_filtered,内部会根据过滤pad自动创建一个capsfilter。

所以关闭链接的时候,需要把src和dst分别从capsfilter中关闭链接。而非简单 的关闭源和目标

7. 幽灵pad

有啥用?其实就是创建一个代理pad吧。

为啥要有个这个东西?因为bin本身是没有pad的。所以你就没办法把两个bin链接起来。

这个时候,可以用bin中的一个单元的pad构造一个代理pad,这样bin就有一个代理pad了。这个pad实际指向被代理的那个单元的pad。

 

3.7 缓冲与事件

数据流动是以缓冲传递来实际工作的,所以buffer比较在重要。

events和message不太一样,这个events实际就是命令,而且在 管道中流动。这么说的话,buffer对应的就是数据。

从命名习惯上来说,buffer更应该看成是一种容器,里边含data和events。

1. Buffer

GstBuffer有以下成员:

l        数据内存指针

l        缓冲大小

l        时间戳

l        用者计数(与引用计数对应)

l        标识

2. Events

事件是一种控制数据,能够在管道中上下流动。

一般来说,上游的控制命令可能是真的在控制什么,来自下游的events可 能大多数是些状态通知之类的。?原文是这么说的。

应用程序自己能发送控制?例如seek命令。

恩,确实应该有地方可以发送控制命令。典型的就是seek。用户也需要一个地方能做这个工作。

看来都是通过events方式来做到控制的。

l        gst_events_new_xxx

l        gst_element_send_event

先创建一个命令,然后发出去….

 

4. 小结:

        Gstreamer的core只负责把各个部分联系起来,组成一个可以运行工作的整体,真正做事的是元件(elements)。谈到元件不得不提到插件(plugin),Gstreamer有很多类插件,plugin-base、plugin-good等,插件中包含很多的元件,元件就是根据插件中的某一个元件动态创建的,完成具体某一项功能,如H264解码(它只负责接收特定格式的数据,然后解码,最后输出特定格式的数据)。