本文主要介绍金山云Android推流、短视频SDK设计中,为保证SDK的灵活性、可扩展性,在SDK组件化方向上所做的一些探索。
成熟的PC端多媒体架构简介
PC诞生之初,就有了强烈的多媒体处理需求,在几十年发展中,比较知名的几个多媒体框架有:
- 微软的DirectShow
- 开源跨平台的GStreamer
- FFMPEG
- VLC
其中,FFMPEG更偏重于提供muxer/demuxer, encoder/decoder等实用稳定的多媒体组件,VLC更偏重于提供ALL IN ONE的软件产品,其框架更多的是为特定的应用场景服务,灵活性及扩展性均不及DirectShow和GStreamer.
DirectShow和GStreamer的组件化设计
DirectShow与GStreamer均为组件化设计的多媒体框架,具体工作均交由各个组件来实现。
DirectShow的Filter Graph
微软提供了可视化的组件编辑工具GraphEdit,借助该工具,我们可以通过直观的方式将各个DirectShow的组件连接起来,并对实际效果进行预览。
比如下图的连接方式就实现了一个基本的视频文件播放器:
DirectShow框架下的视频播放Pipeline
根据上图,我们可以看到,一个典型的视频播放器包含一个视频源分离模块(Demuxer), 一个视频解码模块,一个音频解码模块,一个视频渲染模块,一个音频渲染模块。在DirectShow中,这些模块被称为Filter,连接起来的各个Filter组成了一个Filter Graph。
各个Filter包含不同类型与数目的引脚,通过引脚间的连接,实现数据流在不同模块间的传递。
这些引脚在DirectShow中称为Pin, 其中产生数据的Pin被称为Source Pin,接受数据的Pin称为Sink Pin。
例如分离模块中包含音视频两个Source Pin, 解码模块包含一个Sink Pin和一个Source Pin, 渲染模块只有一个Sink Pin。
当然我们也可以通过选择组合不同的Filter组成新的Filter Graph来达成不同的功能,或者增添、更改当前Filter Graph中的Filter来动态调整Filter Graph的功能特性。
GStreamer的Pipeline
GStreamer中存在类似的组件化结构,例如下图的Pipeline实现了一个简单的ogg音频文件播放器:
如图中所示的source, demuxer, decoder, output模块,在GStreamer中被称为Element, Element上的引脚被称为Pad, 输入输出引脚分别被称为Source Pad和Sink Pad,而连接起来的各个Element则组成了一个Pipeline。
GStreamer同样支持使用不同的Element及连接方式来组成不同的Pipeline,以及对其中的Element进行增添、改动来调整Pipeline的功能特性。
背后的工作
前面我们看到了DirectShow和GStreamer直观、灵活的组合方式,以及强大的扩展性,但要实现这些特性是需要框架完成大量的配套工作的。
模块间连接时的协商过程。
在多媒体处理中,存在着多种数据类型,例如未解码的视频数据(其中又存在多种编码格式HEVC/AVC/VP9等),解码后的视频数据(又包含RGB/I420/YV12/NV12等),不同模块能够处理的数据类型是不同的,因此两种框架均实现了完善的协商过程。
例如,对于不支持的连接方式,抛出错误,或者智能添加一个转换模块来完成连接。
模块的动态添加及移除处理。
例如在播放或者编辑视频的过程中,需要添加、改变或者移除一种特效Filter,就需要对已连接的模块进行动态重建。
两种框架均实现了该功能,不过为此也做了大量的工作,例如模块在变动过程中的状态处理、数据流处理等。
模块间的数据传送。
一般存在两种方式,一种为push模式,另一种为pull模式。
两种框架均对push及pull模式做了支持。例如上面GStremer框架下的ogg播放器,ogg-demuxer的sink pad就以pull模式从file-source拉取数据,后继模块则均以push模式运行,由上一级模块的src pad将数据推送到后一级的sink pad。
状态控制和事件响应。
GStreamer中,要控制Pipeline的c3jY{NyNX[8kXiuXY
yNi[h