王 蕊 , 刘卫东 ,2, 王金童
(1.中国海洋大学 信息科学与工程学院,山东 青岛 266100;2.海信电器股份有限公司 山东 青岛 266071;3.青岛海信信芯科技有限公司 山东 青岛 266071)
如今多媒体系统的功能越来越复杂,并且由于不同的多媒体系统之间的硬件平台和软件平台存在着巨大的差异性,这些都给多媒体系统的移植工作带来了问题。为了将多媒体系统移植到不同的软、硬件平台上,必须为这些平台重新编写多媒体处理代码,并且在应用程序开发时,要花很大的时间和精力对这些底层多媒体处理重新进行整合。但是如果这些多媒体系统中加入了多媒体框架GStreamer,那么就可以把复杂的底层多媒体处理的整合工作交给多媒体框架来处理。借助于GStreamer的管道机制,插件(编解码器,复用/解复用器,控制器等)中的各种多媒体处理代码对于上层的应用程序而言变得透明,在开发应用程序时就不需要把精力和注意力放在底层代码的整合工作上。与此同时,被封装成共享库的插件中的元件可以被多个应用程序共享使用,减少了重复代码的编写,简化了集成和复用,提高了开发的效率。
GStreamer是一个功能强大的开源多媒体应用程序框架,它的基本设计思想来自于俄勒冈(Oregon)研究生院有关视频管道的创意,同时也借鉴了DirectShow的设计思想。
GStreamer作为Gnome桌面环境推荐的多媒体应用框架,采用了基于插件的模块化设计,框架中所有功能模块都被实现成可以插拔的组件,并且在需要的时候能够很方便地安装到任意一个管道上,所有插件都通过管道机制进行统一的数据交换。GStreamer在构建播放器上有显著的用途,附带200多个音视频处理插件,能够支持多种格式的文件,包括MP3、 Ogg/Vorbis、MPEG-1/2、AVI、Quicktime、mod 等等。
GStreamer核心库是一个处理插件、数据流和媒体操作的框架,为程序员编写应用程序提供了一些公用的API。
GStreamer有以下3个基本概念[1-6]:
1)元件(Element) 元件是GStreamer中最重要的概念,它是组成管道的基本构件,也是框架中所有可用组件的基础。按照各自功能上的差异,GStreamer的元件可以分为3类:①数据源元件 Source Element,只有输出端,它仅能用来产生供管道处理的数据,而本身不对数据做任何处理。比如从磁盘或者声卡读取数据。②过滤器元件 Filter Element,既有输入端又有输出端,它从输入端获得相应的数据,并在经过特殊处理之后传递给输出端。比如格式转换,编解码等。③Sink Element接收器元件,只有输入端,它仅具有消费数据的能力,是整条媒体管道的终端。比如声卡播放声音以及视频输出等。
2)箱柜(Bin)和管道(Pipeline) 箱柜是一个可以装载元件的容器。管道是箱柜的一个特殊的子类型,管道可以操作包含在它自身内部的所有元件。因为箱柜本身又是元件的子集,所以能够像操作普通元件一样的操作一个箱柜,可以通过改变一个箱柜的状态来改变箱柜内部所有元件的状态。管道是一个高级的箱柜。当设定管道为播放状态的时候,数据流将开始流动,并且媒体数据也开始进行处理,一旦开始,管道将在一个单独的线程中运行,直到被停止或者数据流播放完毕。顶层箱柜必须为一个管道,因此每个GStreamer应用程序都至少需要一个管道。
3)衬垫(Pad) 衬垫在GStreamer中被用于多个元件的链接,从而让数据流能在这样的链接中流动。 一个衬垫可以被看作是一个元件插座或者端口,元件之间的链接就是依靠着衬垫。一个衬垫能够限制数据流类型的通过,只有在两个衬垫允许通过的数据类型一致的时候元件之间的链接才能被建立。
GStreamer通过抽象的管道概念进行工作。管道是一个有向图,数据流在这个有向图中从输入流到输出。管道由各种元件构成,而元件则是可以放入到管道中的对象,其中包装了对媒体进行的某种操作。在管道中不同的元件可以通过衬垫链接在一起,共同组成将输入转换为需要的输出的完整处理过程。通常,使用从左(上游)到右(下游)的数据流来对管道进行描述。所以,管道的工作过程可以简单的描述为对数据流进行输入、转换和输出的过程。基本的GStreamer应用工作原理如图1所示。
图1 GStreamer工作原理Fig.1 Operation principle of GStreamer
在GStreamer应用中,框架中的大部分API都会涉及到对元件的操作,元件通过衬垫与外界进行交互,向框架中的其余部分表明自己的特性或者功能,开发者可以通过特定的函数来设置元件的属性。衬垫根据是在元件的输入端还是输出端 , 分为 输出衬垫(Src pad)和 输入衬垫(Sink pad)。管道在箱柜之上,通过它控制媒体播放中的各种状态,包括插放、暂停、快进、快退等。总线[1](Bus)在箱柜之上,主要用来装入回调函数,传递一些管道运行后的消息。每个管道都默认有一个总线,不需要手工创建,只要设置消息处理器(Handler)到总线上,管道运行起来后,等待消息被捕捉到,就会立刻调用回调函数。
为了实现GStreamer的一般应用,需要安装的模块包括:gstreamer、gst-plugins-base、gst-plugins-good、gst-pluginsugly、gst-plugins-bad、gst-ffmpeg。 版本分别选择其对应的最新版本。从GStreamer官方网站的下载页面分别下载到本地硬盘,解压之后在终端中分别输入命令./configure、make、make install按照顺序逐个配置、编译、安装各个模块,最终完成GStreamer的安装。
GStreamer-0.10[3]的组成模块及其最新版本如表1所示。
表1 GStreamer-0.10组成模块版本Tab.1 Version of GStreamer-0.10’s composition modules
利用GStreamer的插件构建的媒体播放管道构建图[5]如图2所示。其中,Source element用来读取媒体文件,并连接到Demuxer element,由它负责把媒体文件里的压缩音视频数据分离出来,之后分别连接到音频解码元件Audio decoder element和视频解码元件Video decoder element分别对分离出来的音视频进行解码,然后再分别连接到音频输出元件Audio sink element和视频解码元件Video sink element完成媒体流的输出。
在整体管道构造中,使用到一些重要的GStreamer API[6]函数,来实现初始化库,创建元件,连接元件,设置元件属性等。以下为具体设计管道的主要步骤:
1)gst_init(),通过 gst_init()函数来初始化 GStreamer库,确保程序包含了 gst/gst.h头文件,这样GStreamer库中的对象和函数才能够被正确地定义。
2)g_main_loop_new (), 创建一个主循环, 在执行g_main_loop_run()后正式开始循环。
3)gst_pipeline_new(),创建一条新的管道,用来容纳和管理元件。
4)gst_element_factory_make(),根据元件 Factory 中的类型创建元件,包括输入源元件source,过滤器元件decoder和接收器元件sink。
5)g_object_set()[4],指定输入文件的路径。
6)gst_bin_add()和 gst_element_link()将新创建的元件添加到已创建的管道中,通过衬垫链接管道中的元件(元件要按照顺序链接)。
图2 媒体播放管道构建图Fig.2 Media-player pipeline
程序用gst_bin_add_many()函数将创建的所有元件都加入管道pipeline中,然后用gst_element_link_many()来连接他们,这样各个元件就可以配合工作了。
7)gst_pipeline_get_bus()和 gst_bus_add_watch(),从管道中得到总线,创建一个消息处理器来侦听管道。每当管道发出一个消息到总线,这个消息处理器就会被触发,它调用回调函数来处理消息。
8)gst_element_set_state(),设置管道的运行状态。状态可以设 置 为 GST_STATE_NULL,GST_STATE_READY,GST_STATE_PAUSED和GST_STATE_PLAYING,对应于初始化、准备、暂停和播放状态。
9)g_main_loop_run(),让主循环运行起来,开始码流的播放。
10)gst_object_unref(),播放完成后,释放系统资源。
通常当加载一个新的媒体流时,媒体的类型并不明了,所以在选择一条管道来对媒体流进行解码之前,首先需要检测媒体流的类型。在 GStreamer中使用了类型检测插件typefind[1]来达到此目的。在上层提出音频播放请求时,通过插件typefind分析音频文件的格式,若为不支持的格式,向上层发送一个无法播放的消息;若为支持的格式,及时动态地调用插件生成对应的插件,并连接封装成一个管道进行播放处理。
成功获取文件信息后,GStreamerBus会发出消息,并按照函数g_signal_connect()的设置,调用回调函数cb_typefound();
在函数cb_typefound()成功获取文件信息后,调用cb_typefound()时,会返回一个GstCaps数据,其中包含了诸如文件类型,宽度,高度比等信息。在cb_typefound()中,可使用gst_caps_to_string(caps)函数将数据强制转换成一个字符串,也可用 gst_caps_get_structure(caps,0)函数将数据转换成为一个structure类型数据,以便播放使用。
文中介绍了GStreamer的一些基础知识,并简单介绍了GStreamer库的安装。在阅读、分析GStreamer源码的基础上,着重讲述了GStreamer的工作原理,讨论了如何利用其管道的思想来实现多种音视频格式文件的播放,包括播放管道的设计以及媒体文件类型的获取,并演示了如何利用GStreamer框架构建简单、高效的媒体播放管道。由于GStreamer为用户提供了大量的公用API,使用它来构建播放管道具有易于开发,易于移植的特点,极具实际应用价值。
[1]Taymans W,Baker S,Wingo A,et al.GStreamer Application Development Manual(0.10.35.1) [EB/OL].[2011-07-15].http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/manual.pdf.
[2]Boulton R J,Walthinsen E, Baker S,et al.GStreamer Plugin Writer’s Guide(0.10.35.1) [EB/OL].[2011-08-11].http://gstreamer.freedesktop.org/data/doc/gstreamer/head/pwg/pwg.pdf.
[3]gstreamer-0.10.35.tar.gz[CP/OL]. [2011-07-15].http://gstreamer.freedesktop.org/src/gstreamer/.
[4]GObject Reference Manual[EB/OL].http://developer.gnome.org/gobject/stable/.
[5]张海滨,李挥,吴晔等.嵌入式高清播放器的设计与实现[J].计算机工程与设计,2010,31 (13):3086.
ZHANG Hai-bin,LIHui,WU Ye,etal.Design and implementation ofembedded high definition player[J].Computer Engineering and Design,2010,31(13):3086.
[6]GStreamer 0.10 Core Reference Manual (0.10.35.1) [EB/OL].[2011-07-10].http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/