GStreamer框架
1、GStreamer是什么?
众所周知,Microsoft's Windows和Apple's MacOS对多媒体设备、多媒体创作、播放和实时处理等方面都有很好的支持,而Linux对多媒体应用一直略显不足,所以为了解决Linux上对多媒体方面的支持,才引进GStreamer。
GStreamer是一个通用的跨平台的流媒体应用程序框架,基于GObject,以C语言写成。 GStreamer并不受限于音频和视频处理,它能够处理任意类型的数据流,因此任意一种流媒体应用都可以支持,如:MeidaPlayer、A/V Editor、VOIP、RTSP、A/V Coder等等。 GStreamer框架是基于插件(plugin)和管道(pipeline)的体系结构,框架中所有功能模块都是可插拔的组件,可随意安装到任意管道上,因此造就了大量的GStreamer的共享库。
2、 pipeline是什么?
这是个典型的MeidaPlayer的模型。
source —— 数据来源,可能是file、http、rtp等。
demux —— 负责把容器里的音视频数据剥离出来,然后分别送给audio/video decoder。
decoder —— 解码,然后把解完后的数据(pcm/yuv)送给audio/video output输出。
output —— 负责将decoder过来的数据呈现出来。
如果把数据想象成流水的话,每个模块功能虽然不同,但基本都是接收上个模块过来的数据,然后加工,把加工后的数据送到下一个模块,这些模块通过某种方式连接起来,就形成了一个流水线(pipeline),这个流水线就是一个MediaPlayer。 GStreamer 把每个模块都看做是一个元件(element),然后构建连接和操作这些element的方法,用户可以通过自己的需求把不同的elements排列组合,形成不同的pipeline。
3. element是什么?
element是一个对多媒体流进行处理的object,也是一个具体的功能模块,是pipeline的最小组成部分。
element分类:source(只提供数据源),sink(如播放设备),transform,demuxer,muxer element的输入为sink pad,输出为source pad,通过pad把element连接起来构成pipeline 图中downstream为顺流方向,upstream为逆流方向
4. pad是什么?
element的衬垫(pad)对应输入和输出接口,对于输入衬垫为sink pad,对于输出衬垫为source pad element之间都是通过pad来链接的,顺序流向不能错,也基于pad类型进行分类
pad有处理特殊数据的能力,一个pad能够限制数据流类型(GstCaps)的通过。 链接成功的条件是:只有在两个衬垫(pads)允许通过的数据类型一致的时候才被建立,通过caps negotiation方法。 衬垫有三种类型的时效性: 永久型(always)、随机型(sometimes)、请求型(on request)。 永久型的衬垫一直会存在,随机型的衬垫只在某种特定的条件下才存在(会随机消失的衬垫也属于随机型), 请求型的衬垫只在应用程序明确发出请求时才出现。
5. 四种状态
element有四种可能的状态,分别是NULL,READY,PAUSED,PLAYING。
GST_STATE_NULL 默认状态,该状态将会回收所有被元件占用的资源。
GST_STATE_READY 准备状态,该状态会得到所需的全局资源,但数据流并未处理。
GST_STATE_PAUSED 暂停状态,元件已经对流开始处理,一旦状态变为 PLAYING,可以重放数据流, 与PLAYING 状态的区别是:时钟是禁止运行的,主要对数据进行preroll。
GST_STATE_PLAYING 与 PAUSED 状态一模一样,但可以运行时钟,对数据进行处理。
通过函数gst_element_set_state()可以改变一个元件的状态,但状态变换不能跳变,比如不能从READY状态 直接变换到PLAYING状态,必须经过中间的PAUSE状态。
6. element流程
element create
gstreamer加载时,扫描/usr/lib/gstreamer-1.0目录下的库,识别其中的feature,并记录相关信息。当使用时,检查gstreamer core是否支持该功能,如果有,则加载相应库,获取信息,创建相应的element实例。
element link
element创建后,会添加到pipeline,在link时会通过gst_pad_query_caps(pad, NULL)查询pad template caps。因为此时尚未打开设备、初始化等,所以不知道element真正支持的caps,但只要查询的caps有交集即可link成功。
NULL->READY
该状态下,会初始化设备,根据相应的class调用start()或open()等函数初始化相应的硬件设备,初始化class的结构参数等。
READY->PAUSED
进一步申请资源,确定相应的参数设置,同时会激活pad。然后数据预滚(preroll),当数据到达时,检查数据时间戳是否在segment内,进行数据同步,最后就是commit,进入PAUSED。
PAUSED->PLAYING
在这个过程,设置clock时钟运行,接收到数据时,检查时间有效性,进行数据同步、处理,push到下游,发送QOS事件到上游,完成一个循环。
7. bin是什么?
箱柜(bin)是由多个element构成的容器,同时bin本身也是一种element,所以能够像操作普通element一样操作一个bin,改变bin的状态可以改变bin内部所有elements的状态。
bin可以发送总线消息给它的子集elements ,包括:错误消息(error messages),标签消息(tag messages),EOS消息(EOS messages)。
管道(pipeline)是一个特殊的bin,当设定管道暂停或播放状态的时候,数据流将开始流动,并且媒体数据处理也开始处理。一旦开始,pipeline将在一个单独的线程中运行,直到被停止或者数据流播放完毕。
data flow:数据流在pads之间传送,封装在Buffer里,Buffer包含指向数据的指针和一些metadata。
event flow:事件流与数据量不同,既有downstream方向,也有upstream,可以捕捉事件信号,进行回调处理
pipeline构建过程
gst_pipeline_new()函数:创建一个pipeline
gst_bin_add()函数:向pipeline中添加elements
gst_bin_remove()函数:从pipeline中移除element
gst_element_link()函数:链接pipeline中的elements
8. 缓冲区
缓冲区是指管道里的数据流,通常一个源元件会创建一个新的缓冲区,同时元件还将会把缓冲区的数据传递给下一个元件。使用GStreamer创建管道,不需要自己来处理缓冲区,元件将会自动处理这些缓冲区。
一个缓冲区主要组成:
指向某块内存的指针
内存的大小
缓冲区的时间戳
一个引用计数,指出了缓冲区所使用的元件数。没有元件可引用的时候,这个引用将用于销毁缓冲区
9. message/event/signal
Bus message —— 用于gstreamer和app之间交互的,比如当一个文件播放结束的时候,gstreamer会发一个EOS的message到GstBus上,如果app有去侦听(函数gst_bus_add_watch),那么在处理消息的callback函数中就可以收到这个消息。
event —— 用于gstreamer内部element之间(或pad之间)传递事件的,比如source element数据已经结束,会发出一个EOS event,顺着pipeline依次向downstream方向传递,elements得到通知,做一些cleanup工作,当所有sink element都收到并处理之后,gstreamer内部产生一条GSTMessage,并post至GstBus,如果APP有监听,就能知道当前播放已经结束。
signal —— 属于GObject体系,用于app和GObject之间交互的一种机制。在gstreamer中,element本身也是gobject,所以通过signal,就可以将app和element联系起来。 当element发生了一些事情相让app知道时,就可以用signal的方式来通知app,比如动态创建了一个Pad。
10. 初步总结
需要包含头文件gst/gst.h来访问库函数。
使用gst_init初始化GStreamer库和一些必要的参数。
使用gst_element_factory_make创建元件,参数为工厂对象名和新元件名,在bin中可查询该元件。
使用gst_object_unref释放元件,元件引用记数减1,任一元件创建时,引用记数为1,当引用记数为0时,元件会被销毁。
使用gst_bin_new或gst_pipeline_new创建箱柜。
使用gst_bin_add/remove添加/移除元件,元件所属箱柜,销毁箱柜,则箱柜中的元件同样被销毁,元件移除则自动销毁。
使用gst_element_link链接元件
每个管道默认包含一个总线,应用程序不需要再创建总线,只需在总线上设置一个消息处理器,总线会轮询消息处理器是否有新的消息,当采集到消息后,总线将呼叫相应的回调函数来处理。使用gst_bus_add_watch或gst_bus_add_signal_watch侦听回调,或使用gst_bus_peek/poll主动轮询消息。
11. 编译说明
比如之前的helloworld程序
编译: gcc -Wall helloworld.c -o helloworld $(pkg-config --cflags --libs gstreamer-0.10)
编译时借助了pkg-config命令,用于获得某一个库/模块的所有编译相关的信息。
pkg-config --cflags --libs gstreamer-0.10 会把gstreamer-0.10编译所依赖的库路径和头文件路径全部找出来,不用再依次写出,这条命令参数相当于: -pthread -I/usr/include/glib-2.0 -I/usr/lib/i386-linux-gnu/glib-2.0/include -I/usr/include/gstreamer-0.10 -I/usr/include/libxml2 -pthread -L/usr/lib/i386-linux-gnu -lgstreamer-0.10 -lgobject-2.0 -lgmodule-2.0 -lxml2 -lgthread-2.0 -lrt -lglib-2.0)