三大多媒体框架简介与分析


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

① DirectShow

② Gstreamer

③ FFmpeg




DirectShow


DirectShow的概念

DirectShow GraphEdit(graphedt.exe)来进行分析:

image.png


在上图中可以直观的看到播放这个媒体文件的基本模块,七个模块按广度顺序:读文件模块, 解复用模块, 视频解码模块, 音频解码音频, 颜色空间转换模块,视频显示模块,

音频播放模块。

按照DirectShow的称呼, 一个模块叫做一个filter(过滤器),模块的输入输出口叫做pin(管脚), 有input pin 和output pin两种; 第一个filter叫做Source filter, 每种媒体最后一个filter叫做Sink filter, 如上图所示连成串的所有filter组成一个Graph, 媒体文件的数据就像流水一样在Graph中流动, 各个相关的filter各司其职, 最后我们就看到了视频,也听到了声音(松耦合、高内聚)。


功能模块与组件分析

DirectShow中和播放器有关的filter粗略的分为五类, 分别是Source filter, Demux fiter,

Decoder filter, Color Space converter filter, Render filter,  各类的filter的功能与作用简述如下:

Source filter:源过滤器, 它的作用是为下级的demux filter以包的形式源源不断的提供数据流。在通常情况下,我们有多种方式可以获得数据流, 一种是从本地文件中读取,一种是从网上获取, Sourcefilter另外的一个作用就是屏蔽读本地文件和获取网络数据的差别,

在下一级的demux filter看来,本地文件和网络数据是一样的。总结一下, Sourcefilter有两个作用:其一是为下一级的demux filter提供数据流, 其二是屏蔽读本地文件和获取网络数据的差别即对于下一级的demux filter看来,本地文件和网络数据是一样的。


  Demux filter:解复用过滤器, 它的作用是识别文件类型, 媒体类型, 分离出各媒体的原始数据流, 并打上时钟信息后送给下级的decoder filter。为识别出不同的文件类型和媒体类型,常规的做法是读取一部分数据, 然后遍历解复用过滤器支持的文件格式和媒体数据格式, 做匹配来确定是哪种文件类型,哪种媒体类型;有些媒体类型的原始数据外面还有其他的信息,比如时间,包大小,是否完整包等等。Demux filter解析数据包后取出原始数据, 有些类型的媒体不管是否完整包都立即送往下级的decoder filter, 有些类型的媒体要送完整数据包, 此时可能有一些数据包拼接的动作;当然时钟信息的计算也是demux filter的工作内容, 这个时钟用于各媒体之间的同步。例如, AVI Splitter是Demux filter。


Decoder filter:解码过滤器的作用就是解码数据包,并且把同步时钟信息传递下去,对视频媒体而言,通常是解码成YUV数据,然后利用显卡硬件直接支持YUV格式数据Overlay快速显示的特性让显卡极速显示。YUV格式是一个统称, 常见的有YV12, YUY2, UYVY等等。有些非常古老的显卡和嵌入式系统不支持YUV数据显示, 那就要转换成RGB格式的数据, 每一帧的每一个像素点都要转换, 分别计算RGB分量,并且因为转换是浮点运算, 虽然有定点算法, 还是要耗掉相当一部分CPU,总体上效率低下。对音频媒体而言, 通常是解码成PCM数据,然后送给声卡直接输出, 在本例中,  AVI Decompress和ACM Warper是decoder filter。

Color space converter filter: 颜色空间转换过滤器,它的作用是把视频解码器解码出来的数据转换成当前显示系统支持的颜色格式。通常视频解码器解码出来的是YUV格式, PC系统是直接支持YUV格式的, 也支持RGB格式, 有些嵌入式系统只支持RGB格式。在本例中,视频解码器解码出来的是RGB8格式的数据, Color space converter filter把RGB8转换为RGB32显示。

Render filter:渲染过滤器, 它的作用是在适当的时间渲染相应的媒体,对视频媒体就是直接显示图像, 对音频就是播放声音。视音频同步的策略方法有好几种,其中最简单的一种就是默认视频和音频基准时间相同, 这时音频可以不打时钟信息,通过计算音频的采样频率,量化bit数, 声道数等基本参数就知道音频PCM的数据速率,按照这个速率往前播放即可;视频必须要使用同步时钟信息来决定什么时候显示。DirectShow采用一个有序链表,把接受到的数据包放进有序连表中,启动一个定时器, 每次定时器时间就扫描链表,比较时钟信息,或者显示相应的帧,或者什么也不做,每次收到新的数据帧,首先判断时钟信息,如果是历史数据帧就丢弃,如果是将来显示的数据帧就存放在有序链表,如果是当前时间帧就直接显示。如果这样,保持视频和音频在人体感觉误差范围内相对的动态同步。在本例中VideoRender和Default DirectSound Device是Render filter, 同时也是Sink filter。

GraphEdit应用程序:它可以看成是一个支撑平台,支撑框架。它容纳各种filter,在filter间的传递一些通讯消息, 控制filter的运行(启动暂停停止),维护filter运行状态。GraphEdit就像超级大管家一样, 既维护管理看得见的filter,又维护管理看不见的运行支持环境。

通过上面的讲述,希望能对多媒体的框架的基本元素有一定的了解,虽然ffmpeg库自成一体,但是也是能够归纳到上面的流程之中的。


知识普及:

(一)  YUV: 主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视。与RGB视频信号传输相比,它最大的优点在于只需占用极少的频宽(RGB要求三个独立的视频信号同时传输)。其中“Y”表示明亮度(Luminance或Luma),也就是灰阶值;而“U”和“V” 表示的则是色度(Chrominance或Chroma)。

(二)PCM:主要过程是将话音、图像等模拟信号每隔一定时间进行取样,使其离散化,同时将抽样值按分层单位四舍五入取整量化,同时将抽样值按一组二进制码来表示抽样脉冲的幅值。



gstreamer

什么是Gstreamer?

Gstreamer是一个支持Windows,Linux,Android, iOS的跨平台的多媒体框架,应用程序可以通过管道(Pipeline)的方式,将多媒体处理的各个步骤串联起来,达到预期的效果。每个步骤通过元素(Element)基于GObject对象系统通过插件(plugins)的方式实现,方便了各项功能的扩展。


下图是对基于Gstreamer框架的应用的简单分层:


image.png


 


Media Applications

最上面一层为应用,比如gstreamer自带的一些工具(gst-launch,gst-inspect等),以及基于gstreamer封装的库(gst-player,gst-rtsp-server,gst-editing-services等)根据不同场景实现的应用。


Core Framework

中间一层为Core Framework,主要提供:


上层应用所需接口

Plugin的框架

Pipline的框架

数据在各个Element间的传输及处理机制

多个媒体流(Streaming)间的同步(比如音视频同步)

其他各种所需的工具库



Plugins

最下层为各种插件,实现具体的数据处理及音视频输出,应用不需要关注插件的细节,会由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前,我们需要掌握一些gstreamer的基础概念。


Element

Element是Gstreamer中最重要的对象类型之一。一个element实现一个功能(读取文件,解码,输出等),程序需要创建多个element,并按顺序将其串连起来,构成一个完整的pipeline。


Pad

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和Pipeline

Bin是一个容器,用于管理多个element,改变bin的状态时,bin会自动去修改所包含的element的状态,也会转发所收到的消息。如果没有bin,我们需要依次操作我们所使用的element。通过bin降低了应用的复杂度。

Pipeline继承自bin,为程序提供一个bus用于传输消息,并且对所有子element进行同步。当将pipeline的状态设置为PLAYING时,pipeline会在一个/多个新的线程中通过element处理数据。


下面我们通过一个文件播放的例子来熟悉上述提及的概念:测试文件 sintel_trailer-480p.ogv

gst-launch-1.0 filesrc location=sintel_trailer-480p.ogv ! oggdemux name=demux ! queue ! vorbisdec ! autoaudiosink demux. ! queue ! theoradec ! videoconvert ! autovideosink

通过上面的命令播放文件时,会创建如下pipeline:


image.png


可以看到这个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输出。


 


Gstreamer数据消息交互

在pipeline运行的过程中,各个element以及应用之间不可避免的需要进行数据消息的传输,gstreamer提供了bus系统以及多种数据类型(Buffers、Events、Messages,Queries)来达到此目的:

image.png



Bus

Bus是gstreamer内部用于将消息从内部不同的streaming线程,传递到bus线程,再由bus所在线程将消息发送到应用程序。应用程序只需要向bus注册消息处理函数,即可接收到pipline中各element所发出的消息,使用bus后,应用程序就不用关心消息是从哪一个线程发出的,避免了处理多个线程同时发出消息的复杂性。


Buffers

用于从sources到sinks的媒体数据传输。


Events

用于element之间或者应用到element之间的信息传递,比如播放时的seek操作是通过event实现的。


Messages

是由element发出的消息,通过bus,以异步的方式被应用程序处理。通常用于传递errors, tags, state changes, buffering state, redirects等消息。消息处理是线程安全的。由于大部分消息是通过异步方式处理,所以会在应用程序里存在一点延迟,如果要及时的相应消息,需要在streaming线程捕获处理。


Queries

用于应用程序向gstreamer查询总时间,当前时间,文件大小等信息。






ffmpeg

简介

ffmpeg是一个非常有用的命令行程序(.exe),它可以用来转码媒体文件。它是领先的多媒体框架FFmpeg的一部分,其有很多功能,比如解码、编码、转码、混流、分离、转化为流、过滤以及播放几乎所有的由人和机器创建的媒体文件。

在这个框架中包含有各种工具,每一个用于完成特定的功能。例如,ffserver能够将多媒体文件转化为用于实时广播的流,ffprobe用于分析多媒体流,ffplay可以当作一个简易的媒体播放器,ffmpeg则能够转换多媒体文件格式。


FFMPEG从功能上划分为几个模块,分别为核心工具(libutils)、媒体格式(libavformat)、编解码(libavcodec)、设备(libavdevice)和后处理(libavfilter, libswscale, libpostproc),分别负责提供公用的功能函数、实现多媒体文件的读包和写包、完成音视频的编解码、管理音视频设备的操作以及进行音视频后处理。

libavutil是一个包含简化编程功能的库,其中包括随机数生成器,数据结构,数学代码,核心多媒体工具等更多东西。

libavcodec是一个包含音频/视频解码器和编码器的库。

libavformat是一个包含了多媒体格式的分离器和混流器的库。

libavdevice是一个包含输入输出设备的库,用于捕捉和渲染很多来自常用的多媒体输入/输出软件框架的数据,包括Video4Linux,Video4Linux2,VfW和ALSA。

libavfilter是一个包含媒体过滤器的库。AVFilter可以给视音频添加各种滤镜效果。可以给视频添加水印,给YUV数据加特效。

libswscale是一个用于执行高度优化的图像缩放和颜色空间/像素格式转换操作的库。

libswresample是一个用于执行高度优化的音频重采样,重新矩阵和取样格式转换操作的库。

image.png




FFmpeg处理要点

 总体来说,FFmpeg框架主要的作用在于对多媒体数据进行解协议、解封装、解码以及转码等操作,为了对FFmpeg在视音频中的应用有个更直观理解,下面给出解析rtsp网络流的流程图,该图演示了从打开rtsp流,到最终提取出解码数据或转码的大概过程,如下所示:

在这里插入图片描述


术语解释:


muxer:视音频复用器(封装器),即将视频文件、音频文件和字幕文件(如果有的话)合并为某一个视频格式,比如讲a.avi、a.mp3、a.srt合并为mkv格式的视频文件;

demuxer:视音频分离器(解封装器),即muxer的逆过程;

transcode:转码,即将视音频数据从某一种格式转换成另一种格式;

RTP包:Real-time Transport Protocol,实时传输协议,是一种基于UDP的网络传输协议,它介于应用层和传输层之间,负责对流媒体数据进行封包并实现媒体流的实时传输;

ES流:Elementary Streams,即原始流,也称视/音频裸流,是直接从编码器输出的数据流,可为视频数据流(如H.264、MJPEG等)或音频数据流(如AAC等);

PES流:Packetized Elementary Streams,分组ES流,PES流是ES流经过PES打包器将ES分组、打包、加入包头信息等处理后形成的数据流,是用来传递ES的一种数据结构。

解协议:取出网络数据流无关报文信息,以获取真正的视音频数据,常见的协议有rtsp、rtmp、http和mms等;

解封装:即demuxer,封装格式可以为.mp4/.avi/.flv/.mkv等;

解码:将编码数据还原成原始内容,比如将H.264解码为YUV、AAC解码为PCM等;





好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***