GStreamer is a library for constructing graphs of media-handling components. The applications it supports range from simple Ogg/Vorbis playback, audio/video streaming to complex audio (mixing) and video (non-linear editing) processing.
GStreamer是一个轻便的多媒体框架,在TX1上默认并未提供V4L2驱动,需要手动安装,而且这个驱动是基于GStreamer的。此外,在TX1上支持的H.264和H.265硬解码也是通过GStreamer的。
基本概念
Pipeline
GStreamer是通过管道和插件实现各种功能或构建各种流媒体应用的。
管道的概念在shell中已经很常见了,不过GStreamer中的管道符号不是|
而是!
。GStreamer中将很多元素串联起来,类似管道的命令,将前者输出作为后者输入。
如:
其中,每个元素说明如下:
filesrc
: 从本地磁盘加载了一个文件,使用该元素时你设置了location
属性指向该文件。decodebin
: 使用该元素从filesrc
解码。会自动检测文件的类型并在后台构造一些GStreamer
元素来解码。audioconvert
: 音频转换元素。alsasink
: 将音频使用ALSA传递给声卡。
Element
元素是GStreamer内置可使用在管道上的命令,GStreamer通过各种元素完成任务。可以使用gst-inspect-1.0
命令来查看可使用的元素。当创建Pipeline时,需要为各个Element设置各种属性。
GStreamer将GstElement细分成如下几类:
- Source Element: 数据源元素。只有输出端,它仅能用来产生供管道消费的数据,而不能对数据做任何处理。一个典型的数据源元素的例子是音频捕获单元,它负责从声卡读取原始的音频数据,然后作为数据源提供给其它模块使用。
- Filter Element: 过滤器元素。既有输入端又有输出端,它从输入端获得相应的数据,并在经过特殊处理之后传递给输出端。一个典型的过滤器元素的例子是音频编码单元,它首先从外界获得音频数据,然后根据特定的压缩算法对其进行编码,最后再将编码后的结果提供给其它模块使用。
- Sink Element: 接收器元素。只有输入端,它仅具有消费数据的能力,是整条媒体管道的终端。一个典型的接收器元素的例子是音频回放单元,它负责将接收到的数据写到声卡上,通常这也是音频处理过程中的最后一个环节。
Pad
一般元素都有输入输出接口,即pad。可将各个元素视作黑盒,则一般元素都有一个输入pad和输出pad,即过滤器元素。输入pad称为sink,输出pad称为src。管道命令模型基本如下:
最左边的元素只有一个src pad用来提供信息(如filesrc)。接下来的几个元素接收信息并做一些处理,因此他们有sink和src pad(例如decodebin和audiocovert),最后一个元素只接收信息(例如alsasink)。使用gst-inspect-1.0命令查看一个元素的详细信息时,就可以看到该元素的pad信息。
注意可能与平时大家认为的概念有些不同的是,src pad是用来发送数据的端点,即数据的 输出端;而sink pad是用来接收数据的端点,即数据的输入端。
一般来说,src pad只能连接到sink pad。但ghost pad两端就要连接相同类型的pad。
Cap
每个元素的cap表示该元素可以接收什么样的信息,类似于V4L2中的cap。
Bin
GStreamer中的bin类似于c中的结构体,是可以存放多个元素的容器。例如管道是a ! b ! c ! d
,可以把他们放进mybin,这样当使用mybin时其实是引用了a ! b ! c ! d
。
Ghost pad
从名字上来看,ghost pad即特殊的pad。
当你创建了一个bin并在里面放置了很多元素时,该bin变成了你自定义的元素,该元素按顺序调用里面的元素。要做到这样,bin很自然地需要它自己的pad,它自己的pad会挂接到bin里面元素的pad上,这就是 ghost pad了。当你创建一个bin时,你创建了ghost pad并告诉他们要去挂接里面哪一个元素。
Message
Message是Pipeline用向外来主动报告自己的运行状态。Message会被发送到一个消息队列,即Pipeline的Bus。应用程序则是从Bus中获取message,并进行处理。
Event
Event是Pipeline用来通信的机制,分为上行、下行和双向Event。在定义了某个事件的响应后,也可以由应用程序直接向该插件(bin)发送事件。通过Event可以控制整个Pipeline的运行状态。
- 上行事件: 由sink插件向source插件方向传输。包括:
|
|
- 下行事件: 由source插件向sink插件方向传输。包括:
|
|
- 双行事件:包括:
|
|
Signal
Signal是应用控制某一插件的运行状态,可以看做Glib对象的一个属性,属于同步操作,和Linux中的系统信号有差别。通过Signal可以让某个插件做一些对插件本身变量的操作,比如增加或删除一些维护信息等。
GStreamer程序处理
可以使用c或Python,Python中需要import gi
,c中使用include <gst/gst.h>
等头文件。
TX1中的GStreamer头文件可以在/usr/include/GStreamer-1.0
目录下找到。
元素处理
Elements是具有一定功能的基本单元,主要组成如下:
|
|
在应用程序中创建GstElement对象的方法是借助于工厂对象GstElementFactory。由于GStreamer框架提供了多种类型的GstElement对象,因此对应地提供了多种类型的GstElementFactory对象,它们是通过特定的工厂名称来进行区分的。
如:
|
|
该代码通过gst_element_factory_find()
函数创建了一个名为mad
的工厂对象。之后就可以通过gst_element_factory_create()
函数来创建特定的GstElement对象了。
函数gst_element_factory_create()
在调用时有两个参数,分别是需要用到的工厂对象,以及即将创建的元素名。元素名可以用查询的办法获得,也可以通过传入空指针(NULL)来生成工厂对象的默认元素。
GStreamer使用了与GObject相同的机制来对属性(property)进行管理,包括查询(query)、设置(set)和读取(get)等。所有的GstElement对象都需要从其父对象GstObject那里继承名称(name)这一最基本的属性,这是因为像
gst_element_factory_make()
和gst_element_factory_create()
这样的函数在创建工厂对象和元素对象时都会用到名称属性,通过调用gst_object_set_name()
和gst_object_get_name()
函数可以设置和读取GstElement对象的名称属性。
Pad处理
成功创建GstElement对象之后,可以通过gst_element_get_pad()
获得该元素的指定pad。例如,下面的代码将返回element元素中名为src的pad:
需要的话也可以通过gst_element_get_pad_list()
函数来查询指定元素中的所有pad。例如,下面的代码将输出elemen元素中所有pad的名称:
与元素一样,pad的名称也能够动态设置或者读取,这是通过调用gst_pad_get_name()
和gst_pad_set_name()
函数来完成的。所有元素的pad都可以细分成输入pad和输出pad两种,其中输入pad只能接收数据但不能产生数据,而输出pad则正好相反。函数gst_pad_get_direction()
可以获得指定pad的类型。GStreamer框架中的所有pad都必然依附于某个元素之上,调用gst_pad_get_parent()
可以获得指定pad所属的元素,该函数的返回值是一个指向GstElement的指针。 Pad从某种程度上可以看成是元素的代言人,因为它要负责向外界描述该元素所具有的能力。GStreamer框架提供了统一的机制来让pad描述元素所具有的能力(capability),这是借助结构体_GstCaps
来实现的:
GStreamer框架中的每个pad都可能对应于多个能力描述,它们能够通过函数gst_pad_get_caps()
来获得。例如,下面的代码将输出pad中所有能力描述的名称及其MIME类型:
Bin处理
在GStreamer应用程序中使用的bin主要有两种类型:
- GstPipeline 管道是最常用到的容器,对于一个GStreamer应用程序来讲,其顶层bin必须是一条管道。
- GstThread 线程的作用在于能够提供同步处理能力,如果GStreamer应用程序需要进行严格的音视频同步,一般都需要用到这种类型的bin。
GStreamer框架提供了两种方法来创建bin:一种是借助工厂方法,另一种则是使用特定的函数。下面的代码示范了如何使用工厂方法创建线程对象,以及如何使用特定函数来创建管道对象:
Bin成功创建之后,就可以调用gst_bin_add()
函数将已经存在的元素添加到其中来了:
从bin中找到特定的元素可以借助gst_bin_get_by_name()
函数实现:
|
|
由于GStreamer框架中的一个bin能够添加到另一个bin中,因此有可能会出现嵌套bin的情况,gst_bin_get_by_name()
函数在查找元素时会对嵌套的bin作递归查找。元素有添加到bin中后,在需要的时候还可以从中移出,通过调用gst_bin_remove()
函数来完成:
|
|
Ghost pad
具有ghost pad的bin在行为上与元素是完全相同的,所有元素具有的属性它都具有,所有针对元素能够进行的操作也同样能够针对bin进行,因此在GStreamer应用程序中能够像使用元素一样使用这类bin。通常通过以下方式为bin添加一个ghost pad:
|
|
元素连接
GStreamer框架中的元素是通过各自的pad连接起来的,如下所示:
如果需要建立起连接的元素都只有一个输入pad和一个输出pad,那么更简单的做法是调用gst_element_link()
函数直接在它们之间建立起连接,或者调用gst_element_unlink()
函数断开它们之间的连接:
元素状态
每个元素一般会有四种状态:
标识 | 状态 |
---|---|
NULL | 这是所有元素的默认状态,表明它刚刚创建,还没有开始做任何事情。 |
READY | 表明元素已经做好准备,随时可以开始处理流程。 |
PAUSED | 表明元素因某种原因暂时停止处理数据。 |
PLAYING | 表明元素正在进行数据处理。 |
所有的元素都从NULL状态开始,依次经历NULL、READY、PAUSED、PLAYING等状态间的转换。元素当前所处的状态可以通过调用gst_element_set_state()
函数进行切换:
默认情况下,管道及其包含的所有元素在创建之后将处于NULL状态,此时它们不会进行任何操作。当管道使用完毕之后,不要忘记重新将管道的状态切换回NULL状态,让其中包含的所有元素能够有机会释放它们正在占用的资源。
管道真正的处理流程是从第一次将其切换到READY状态时开始的,此时管道及其包含的所有元素将做好相应的初始化工作,来为即将执行的数据处理过程做好准备。对于一个典型的元素来讲,处于READY状态时需要执行的操作包括打开媒体文件和音频设备等,或者试图与位于远端的媒体服务器建立起连接。
处于READY状态的管道一旦切换到PLAYING状态,需要处理的多媒体数据就开始在整个管道中流动,并依次被管道中包含的各个元素进行处理,从而最终实现管道预先定义好的某种多媒体功能。GStreamer框架也允许将管道直接从NULL状态切换到PLAYING状态,而不必经过中间的READY状态。
正处于播放状态的管道能够随时切换到PAUSED状态,暂时停止管道中所有数据的流动,并能够在需要的时候再次切换回PLAYING状态。如果需要插入或者更改管道中的某个元素,必须先将其切换到PAUSED或者NULL状态,元素在处于PAUSED状态时并不会释放其占用的资源。
Probe
应用程序可以通过探针(Probe)来探测某个插件的pad中流过的数据,如:
该代码在m_pad_concert
插件的src pad加一个探针,每当有buf到达时,就调用callback_have_data()
,这里这个函数只是打印一下buf的大小,统计一下buf流过的个数。
例子
一个简单的程序:
然后使用gcc进行编译,编译时需加上参数pkg-config --cflags --libs gstreamer-0.10
。
其中,gst_init (&argc, &argv)
用于初始化所有内部结构,检查插件是否可用,以及执行用于GStreamer的命令参数。pipeline = gst_parse_launch ("playbin2 uri=http://docs.GStreamer.com/media/sintel_trailer-480p.webm", NULL)
中,gst_parse_launch
函数接受一个管道的文本表示并把它变成一个实际的管道。playbin2
是充当source和sink一个特殊的元素(element),并且能够实现整个管道。gst_element_set_state (pipeline, GST_STATE_PLAYING)
函数设置Pipeline状态为播放状态。gst_element_get_bus (pipeline);
函数获取管道的总线,gst_bus_timed_pop_filtered
函数将阻塞,直到收到错误或EOS(End-Of-Stream)。