多媒体科技音视频开发项目架构

Android多媒体SDK中的组件化设计思想

2018-07-03  本文已影响99人  金山视频云

本文主要介绍金山云Android推流、短视频SDK设计中,为保证SDK的灵活性、可扩展性,在SDK组件化方向上所做的一些探索。

成熟的PC端多媒体架构简介

PC诞生之初,就有了强烈的多媒体处理需求,在几十年发展中,比较知名的几个多媒体框架有:

其中,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音频文件播放器:

GStreamer框架下的ogg播放器

如图中所示的source, demuxer, decoder, output模块,在GStreamer中被称为Element, Element上的引脚被称为Pad, 输入输出引脚分别被称为Source Pad和Sink Pad,而连接起来的各个Element则组成了一个Pipeline。

GStreamer同样支持使用不同的Element及连接方式来组成不同的Pipeline,以及对其中的Element进行增添、改动来调整Pipeline的功能特性。

背后的工作

前面我们看到了DirectShow和GStreamer直观、灵活的组合方式,以及强大的扩展性,但要实现这些特性是需要框架完成大量的配套工作的。

金山云Android多媒体SDK的架构设计

金山云Android多媒体SDK是以在保证性能前提下提供足够灵活的扩展性为目标的。为此,我们采用将SDK中的各个功能模块组件化,然后根据应用场景进行组装的方式来达成。

以下图为例,展示了推流SDK中各个模块的典型Pipeline结构:

推流SDK典型Pipeline结构图

图中的各个模块通过KSYStreamer类组合在一起,实现完整的直播推流功能。而通过不同的组织方式,又可以组成一个短视频合成SDK,如下图所示:

image.png

框架中对模块的形式,模块间的组织方式的处理参考了DirectShow和GStreamer框架中的一些概念,不过框架最初只是为了推流功能所设计,为兼顾实现难度及性能,做了较大幅度的简化及限制。

基于Pin的模块间连接方式

在金山云Android多媒体SDK中,参照DirectShow及GStreamer的概念,以简化模块间连接为目的,引入了Pin的概念,简要介绍如下:

在搭建推流Pipeline的时候,各个模块之间的连接使用 SrcPinSinkPin 来完成。

Pin的相关操作

public void connect(SinkPin<T> sinkPin)
// 断开所有已连接的SinkPin, recursive为true时表示需要递归断开后面所有已连接的模块
public void disconnect (boolean recursive)
// 断开指定的某个已连接的SinkPin,recursive为true时表示需要递归断开后面所有已连接的模块
public void disconnect (SinkPin<T> sinkPin, boolean recursive)

SrcPin调用disconnect后,SinkPin端可以收到onDisconnect事件

// 源端已断开连接,recursive为true时需要release当前模块,并递归断开后面所有已连接的模块
public abstract void onDisconnect (boolean recursive)

其他部分的处理方式

总结与改进

上述框架是在构建推流端SDK时所设计,为Android直播推流SDK提供了灵活强大的扩展能力,不过依然存在很多可优化部分。

需要完善对短视频SDK场景的支持

  1. 数据流部分加入pull模式的支持。
    短视频应用场景下,是以本地文件作为视频源的,其读取文件以及demux过程不会成为整个处理过程的瓶颈,另外,对解码节奏的控制交由对解码后数据进行处理的模块来进行更为合理,框架中加入pull模式支持对于短视频应用的构建更为方便。

  2. 加入全局的Clock机制来实现音视频同步。
    短视频预览、编辑、转场效果等场景下有音视频同步的需求,在框架中加入全局的时钟机制能够简化应用的复杂度。

简化模块实现以及模块组装的工作

考虑引入模块组装管理类(Pipeline类),连接、移除模块时不再直接通过SrcPin进行,而是通过Pipeline类代理实现。通过这种方式,可以达到:

上一篇下一篇

猜你喜欢

热点阅读