从历史的角度来看,Linux 在多媒体方面已经远远落后于其它的操作系统。微软的Windows和苹果的MacOS它们对多媒体设备、多媒体创作、播放和实时处理等方面已经有了很好的支持。另一方面,Linux对多媒体应用的综合贡献比较少,这也使得Linux很难在专业级别的软件上与Windows和MacOS去竞争。GStreamer正是为解决Linux多媒体方面当前问题而设计的。
GStreamer是一个非常强大和通用的用于开发流媒体应用程序的框架。GStreamer框架的许多优点都来自于它的模块化:GStreamer可以无缝地合并新的插件模块,但是由于模块化和强大的功能往往以更大的复杂度为代价,开发新的应用程序并不总是简单。
我是因为想实现将USB相机,转换为RTSP流的网络相机,再这个过程中开始接触GStreamer,所以萌生了,记录这个学习过程中,分享我自己的一些实践经验的想法,部分内容也是从网上摘抄过来的,但是大部分我还是会自己仔细看一遍,思考一遍,无法逐一列举出处,请见谅。
GStreamer 是一个创建流媒体应用程序的框架。其基本设计思想来自于俄勒冈(Oregon)研究生学院有关视频管道的创意, 同时也借鉴了 DirectShow 的设计思想。
GStreamer 的程序开发框架使得编写任意类型的流媒体应用程序成为了可能。在编写处理音频、视频或者两者皆有的应用程序时,GStreamer 可以让你的工作变得简单。GStreamer 并不受限于音频和视频处理, 它能够处理任意类型的数据流。管道设计的方法对于实际应用的滤波器几乎没有负荷, 它甚至可以用来设计出对延时有很高要求的高端音频应用程序。
GStreamer 最显著的用途是在构建一个播放器上。GStreamer 已经支持很多格式的文件了, 包括:MP3、Ogg/Vorbis、MPEG-1/2、AVI、Quicktime、 mod 等等。从这个角度看,GStreamer 更象是一个播放器。但是它主要的优点却是在于: 它的可插入组件能够很方便的接入到任意的管道当中。这个优点使得利用 GStreamer 编写一个万能的可编辑音视频应用程序成为可能。
GStreamer 框架是基于插件的, 有些插件中提供了各种各样的多媒体数字信号编解码器,也有些提供了其他的功能。所有的插件都能够被链接到任意的已经定义了的数据流管道中。GStreamer 的管道能够被 GUI 编辑器编辑, 能够以 XML 文件来保存。这样的设计使得管道程序库的消耗变得非常少。
GStreamer 核心库函数是一个处理插件、数据流和媒体操作的框架。 GStreamer 核心库还提供了一个API, 这个API是开放给程序员使用的---当程序员需要使用其他的插件来编写他所需要的应用程序的时候可以使用它
下图是对基于Gstreamer框架的应用的简单分层:
最上面一层为应用,比如gstreamer自带的一些工具(gst-launch,gst-inspect等),以及基于gstreamer封装的库(gst-player,gst-rtsp-server,gst-editing-services等)根据不同场景实现的应用。
中间一层为Core Framework,主要提供:
上层应用所需接口
Plugin的框架
Pipline的框架
数据在各个Element间的传输及处理机制
多个媒体流(Streaming)间的同步(比如音视频同步)
其他各种所需的工具库
最下层为各种插件,实现具体的数据处理及音视频输出,应用不需要关注插件的细节,会由Core Framework层负责插件的加载及管理。主要分类为:
Protocols:负责各种协议的处理,file,http,rtsp等。
Sources:负责数据源的处理,alsa,v4l2,tcp/udp等。
Formats:负责媒体容器的处理,avi,mp4,ogg等。
Codecs:负责媒体的编解码,mp3,vorbis等。
Filters:负责媒体流的处理,converters,mixers,effects等。
Sinks:负责媒体流输出到指定设备或目的地,alsa,xvideo,tcp/udp等。
Gstreamer框架根据各个模块的成熟度以及所使用的开源协议,将core及plugins置于不同的源码包中:
gstreamer: 包含core framework及core elements。
gst-plugins-base: gstreamer应用所需的必要插件。
gst-plugins-good: 高质量的采用LGPL授权的插件。
gst-plugins-ugly: 高质量,但使用了GPL等其他授权方式的库的插件,比如使用GPL的x264,x265。
gst-plugins-bad: 质量有待提高的插件,成熟后可以移到good插件列表中。
gst-libav: 对libav封装,使其能在gstreamer框架中使用。
在进一步学习Gstreamer前,我们需要掌握一些gstreamer的基础概念。
Element是Gstreamer中最重要的对象类型之一。一个element实现一个功能(读取文件,解码,输出等),程序需要创建多个element,并按顺序将其串连起来,构成一个完整的pipeline。
Pad是一个element的输入/输出接口,分为src pad(生产数据)和sink pad(消费数据)两种。两个element必须通过pad才能连接起来,pad拥有当前element能处理数据类型的能力(capabilities),会在连接时通过比较src pad和sink pad中所支持的能力,来选择最恰当的数据类型用于传输,如果element不支持,程序会直接退出。在element通过pad连接成功后,数据会从上一个element的src pad传到下一个element的sink pad然后进行处理。当element支持多种数据处理能力时,我们可以通过Cap来指定数据类型. 例如,下面的命令通过Cap指定了视频的宽高,videotestsrc会根据指定的宽高产生相应数据:
gst-launch-1.0 videotestsrc ! "video/x-raw,width=1280,height=720" ! autovideosink
Bin是一个容器,用于管理多个element,改变bin的状态时,bin会自动去修改所包含的element的状态,也会转发所收到的消息。如果没有bin,我们需要依次操作我们所使用的element。通过bin降低了应用的复杂度。 Pipeline继承自bin,为程序提供一个bus用于传输消息,并且对所有子element进行同步。当将pipeline的状态设置为PLAYING时,pipeline会在一个/多个新的线程中通过element处理数据。
例如:
gst-launch-1.0 filesrc location=sintel_trailer-480p.ogv \
! oggdemux name=demux \
! queue ! vorbisdec ! autoaudiosink demux. \
! queue ! theoradec ! videoconvert ! autovideosink
通过上面的命令播放文件时,会创建如下pipeline:
可以看到这个pipeline由8个element构成,每个element都实现各自的功能: filesrc读取文件,oggdemux解析文件,分别提取audio,video数据,queue缓存数据,vorbisdec解码audio,autoaudiosink自动选择音频设备并输出,theoradec解码video,videoconvert转换video数据格式,autovideosink自动选择显示设备并输出。
不同的element拥有不同数量及类型的pad,只有src pad的element被称为source element,只有sink pad的被称为sink element。
element可以同时拥有多个相同的pad,例如oggdemux在解析文件后,会将audio,video通过不同的pad输出。
在pipeline运行的过程中,各个element以及应用之间不可避免的需要进行数据消息的传输,gstreamer提供了bus系统以及多种数据类型(Buffers、Events、Messages,Queries)来达到此目的:
Bus是gstreamer内部用于将消息从内部不同的streaming线程,传递到bus线程,再由bus所在线程将消息发送到应用程序。应用程序只需要向bus注册消息处理函数,即可接收到pipline中各element所发出的消息,使用bus后,应用程序就不用关心消息是从哪一个线程发出的,避免了处理多个线程同时发出消息的复杂性。
用于从sources到sinks的媒体数据传输。
用于element之间或者应用到element之间的信息传递,比如播放时的seek操作是通过event实现的。
是由element发出的消息,通过bus,以异步的方式被应用程序处理。通常用于传递errors, tags, state changes, buffering state, redirects等消息。消息处理是线程安全的。由于大部分消息是通过异步方式处理,所以会在应用程序里存在一点延迟,如果要及时的相应消息,需要在streaming线程捕获处理。
用于应用程序向gstreamer查询总时间,当前时间,文件大小等信息。
Gstreamer自带了gst-inspect-1.0和gst-launch-1.0等其他命令行工具,我们可以使用这些工具完成常见的处理任务。
查看gstreamer的plugin、element的信息。直接将plugin/element的类型作为参数,会列出其详细信息。如果不跟任何参数,会列出当前系统gstreamer所能查找到的所有插件。
$ gst-inspect-1.0 playbin
用于创建及执行一个Pipline,因此通常使用gst-launch先验证相关功能,然后再编写相应应用。 通过上面视频播放的例子,我们已经看到,一个pipeline的多个element之间通过 “!" 分隔,同时可以设置element及Cap的属性。例如: 播放音视频
gst-launch-1.0 playbin file:///home/root/test.mp4
转码
gst-launch-1.0 filesrc location=/videos/sintel_trailer-480p.ogv ! decodebin name=decode ! \
videoscale ! "video/x-raw,width=320,height=240" ! x264enc ! queue ! \
mp4mux name=mux ! filesink location=320x240.mp4 decode. ! audioconvert ! \
avenc_aac ! queue ! mux.
结构清晰且威力强大:我们可以使用一系列强有利的工具来创建媒体管道,而不用去写一行代码,从而使得复杂的媒体控制变得非常简单。GStreamer 向插件提供了简洁而简单的API来创建self- plugin(自包含)插件,同时还集成了大量的调试和跟踪机制和工具。GStreamer也提供了一系列现实例子。
灵活的可扩展性能:所有的GStreamer对象都可以采用GObject继承的方法进行扩展。所有的插件都可以被动态装载。
高性能主要体现在:使用GLib的g_ mem_ chunk和非模块化分配算法使得内存分配尽可能最小。插件之间的连接非常轻型(light-weight).数据在管道中的传递使用最小的消耗,管道中插件之间的数据传递只会涉及指针废弃。提供了一套对目标内存直接进行操作的机制。例如,插件可以向X server共享的内存空间直接写数据,缓冲区也可以指向任意的内存,如声卡的内部硬件缓冲区。refcounting和写拷贝将memcpy减少到最低。子缓冲区有效地将缓冲区分离为易于管理的块。使用线程联合(cothreads)减少线程消耗。线程联合(cothreads)是简单又高速的方法来切换子程序,作为衡量最低消耗600个cpu周期的标准。使用特殊的插件从而支持硬件加速。采用带有说明的插件注册,这样的话只在实际需要使用该插件才会去装载。所有的判断数据都不用互斥锁。
What is GStreamer? Foundations gst-launch-1.0