本文给出了Gstreamer的总体设计。通过阅读本文可以了解Gstreamer的内部工作原理。本文编译自gstreamer源码中的文档,原文在源码中的位置是/gstreamer/docs/design/part-overview.txt。
概述
Gstreamer是一个libraries和plugins的集合,用于帮助实现各种类型的多媒体应用程序,比如播放器,转码工具,多媒体服务器等。
利用Gstreamer编写多媒体应用程序,就是利用elements构建一个pipeline。element是一个对多媒体流进行处理的object,比如如下的处理:
elements的输入叫做sink pads,输出叫做sourcepads。应用程序通过pad把element连接起来构成pipeline,如下图所示,其中顺着流的方向为downstream,相反方向是upstream。
概述
Gstreamer是一个libraries和plugins的集合,用于帮助实现各种类型的多媒体应用程序,比如播放器,转码工具,多媒体服务器等。
利用Gstreamer编写多媒体应用程序,就是利用elements构建一个pipeline。element是一个对多媒体流进行处理的object,比如如下的处理:
- 读取文件。
- 不同格式的编解码。
- 从硬件采集设备上采集数据。
- 在硬件设备上播放多媒体。
- 多个流的复用。
elements的输入叫做sink pads,输出叫做sourcepads。应用程序通过pad把element连接起来构成pipeline,如下图所示,其中顺着流的方向为downstream,相反方向是upstream。
应用程序会收到来自pipeline的消息和通知,比如EOS等。
总体设计
Gstreamer的设计目标如下:
- 快速处理大规模数据。
- 对多线程处理的完全支持。
- 能处理各种格式的流媒体。
- 不同数据流的同步。
- 处理多种设备的能力。
基于Gstreamer的应用程序能够具备的处理能力依赖于系统中安装的不同种类功能的elements的数量。
Gstreamer核心不具备处理具体的media的功能,但是element处理media时需要具备的特性很多是由Gstreamer的核心提供的。
elements
element是pipeline的最小组成部分。element提供了多个pads,或者为sink,或者为source。一个element有四种可能的状态,分别是NULL,READY,PAUSED,PLAYING。NULL和READY状态下,element不对数据做任何处理,PLAYING状态对数据进行处理,PAUSE状态介于两者之间,对数据进行preroll。应用程序通过函数调用控制pipeline在不同状态之间进行转换。
element的状态变换不能跳过中间状态,比如不能从READY状态直接变换到PLAYING状态,必须经过中间的PAUSE状态。
element的状态转换成PAUSE会激活element的pad。首先是source pad被激活,然后是sinkpad。pad被激活后会调用activate函数,有一些pad会启动一个Task。
PAUSE状态下,pipeline会进行数据的preroll,目的是为后续的PLAYING状态准备好数据,使得PLAYING启动的速度更快。一些element需接收到足够的数据才能完成向PAUSE状态的转变,sinkpad只有在接收到第一个数据才能实现向PAUSE的状态转变。
通常情况下,element的状态转变需要协调一致。
可对element进行如下分类:
- source,只提供数据源。
- sink,比如播放设备。
- transform
- demuxer
- muxer
Bin
bin是由多个element构成的特殊的element,用图来说明:
Pipeline
pipeline是具备如下特性的特殊的bin:
- 选择并管理一个全局的时钟。
- 基于选定的时钟管理running_time。running_time用于同步,指的是pipeline在PLAYING状态下花费的时间。
- 管理pipeline的延迟。
- 通过GstBus提供element与应用程序间的通讯方式。
- 管理elements的全局状态,比如EOS,Error等。
Dataflow and buffers
Gstreamer支持两种类型的数据流,分别是push模式和pull模式。在push模式下,upstream的element通过调用downstream的sinkpads的函数实现数据的传送。在pull模式下,downstream的element通过调用upstream的sourcepads的函数实现对数据的请求。
push模式是常用的模式,pull模式一般用于demuxer或者低延迟的音频应用等。
在pads之间传送的数据封装在Buffer里,Buffer中有一个指向实际数据的指针以及一些metadata。metadata的内容包括:
- timestamp
- offset
- duration
- media type
- 其它
在push模式下,element通过调用gst_pad_push()函数把buffer传送给对应的pad。在pull模式下,element通过调用gst_pad_pull_range()函数把pull过来。
element在pushbuffer之前需要确认对应的element具备处理buffer中的数据类型的能力。在传说红之前首先查询对应的element能够处理的格式的种类,并从中选择合适的格式,通过gst_buffer_set_caps()函数对buffer进行设置,然后才传送数据。
收到一个buffer后,element要首先对buffer进行检查以确认是否能够处理。
可以调用gst_buffer_new()函数创建一个新的buffer,也可以调用gst_pad_alloc_buffer()函数申请一个可用的buffer。采用第二种方法接收数据的buffer可以设定接收其它类型的数据,这是通过对buffer的caps进行设定来实现的。
选择媒体类型并对buffer进行设定的处理过程叫做caps negotianation。
Caps
Caps,也就是媒体类型,采用key/value对的列表来描述。key是一个字符串类型,value的类型可能是int/float/string类型的single/list/range。
Data flow and events
除了数据流,还有events流。与数据流不同,events的传送方向既有downstream的,也有upstream的。
events用于传递EOS,flushing,seeking等消息。
有的events必须和dataflow一起进行serialized。serialized的events比如TAG,非serialized的events比如FLUSH。
Pipeline construction
gst_pipeline_create()函数用于创建一个pipeline,gst_bin_add()函数用于向pipeline中添加element,gst_bin_remove()函数用于从pipeline中移除element。gst_element_get_pad()函数用于检索pipeline中的element。gst_pad_link()函数用于把pads连接在一起。
有的element会在数据流开始传送的时候创建新的pads,通过调用函数g_signal_connect()函数,能在新的pads被创建的时候接收到消息。
由于处理的数据互相不兼容,有的elements是不能被连接到一起的。gst_pad_get_caps()函数查询element能够处理的数据类型。
Pipeline clock
Pipeline的一个重要功能是为pipeline中的所有elements选择一个全局时钟。
时钟的作用是提供一个每秒为GST_SECOND的单调递增的时钟,单位是纳秒。element利用这个时钟时间来播放数据。
在pipeline被设为PLAYING之前,pipeline查询每一个element是否能提供clock,并按照如下次序来选择clock:
- 应用程序选择了一个clock。
- 如果source element提供了clock。
- 其它任何提供了clock的element。
- 选择一个默认的系统clock。
也有特殊的情况,比如存在音频sink提供了clock,那么就选择其提供的clock。
Pipeline states
完成了pads的链接和signals的链接,就可以设定pipeline为PAUSED状态启动数据流的处理。当bin(这里指的是pipeline)进行状态转换的时候要转换所有的children的状态,转换的次序是从sinkelement开始到source element结束,这样做的目的是为了确保upstreamelement提供数据的时候,downstream element已经准备好。
Pipeline status
Pipeline会通过bus向应用程序通报发生的events。bus是由pipeline提供的一个object,可以通过gst_pipeline_get_bus()函数取得。
bus分布到加入pipeline的每一个element。element利用bus来发布messages。有各种不同类型的messages,比如ERRORS,WARNINGS,EOS,STATE_CHANGED等。
pipeline以特殊的方式处理接收到的EOS message,只有当所有的sink element发送了EOSmessage的时候,pipeline才会把EOS发送给应用程序。
也可以通过gst_element_query()函数获取pipeline status,比如获取当前的位置或者播放的时间。
Pipeline EOS
当sourcefilter遇上了流结束,会沿着downstream的方向向下一个element发送一个EOS的event,这个event依次传送给每一个element,接收到EOSevent的element不再接收数据。
启动了线程的element发送了EOS event后就不再发送数据。
EOS event最终会到达sink element。sinkelement会发送一个EOS消息,通告流结束。pipeline在接收到EOS消息以后,把消息发送给应用程序。只有在PLAYING状态下会把EOS的消息传送给应用程序。
发送了EOS以后,pipeline保持PLAYING状态,等待应用程序把pipeline的状态置为PAUSE或者READY。应用程序也可以进行seek操作。