为应对愈发多样化的音视频互动场景下的挑战,Agora 开始设计自己的下一代视频处理引擎,在过程中关于引擎架构、性能调优、插件系统设计等方面总结了很多经验,希望与各位音视频领域的爱好者、行业从业者分享。
5月26日下午,QCon全球软件开发大会「实时音视频专场」活动中,声网Agora 架构师李雅琪带来了以《声网下一代视频处理引擎》为主题的大量干货分享,本文是对分享内容的整理。
今天的分享主要会分三部分来进行:
01 下一代视频处理引擎的设计目标
随着音视频技术快速发展,实时音视频互动在各种各样的领域,如社交娱乐、在线直播、医疗中都得到了广泛的应用和发展。疫情以来,越来越多的场景迁移到线上,不少读者可能也都有观看直播带货,或是陪小孩参加在线教育的经历。
那么这两个场景中会有哪些痛点呢?在视频直播场景中,处理多路视频源的需求越来越广泛。比如电商直播场景中,主播通常需要使用多个机位进行多角度拍摄,以达到更好的带货效果。这类直播可能还会同步搭配使用导播台,在多个视频源中进行多种实时直播组合和无缝切换。
而对于在线教育场景来说,比较传统的方式是摄像头拍摄老师,老师进行屏幕分享。为了丰富在线教育的手段,我们还可以增加一路视频拍摄,拍摄老师在手写板上的书写,甚至额外增加一个功能,支持老师播放本地课件或者在线课件。
在多路视频源的基础上,还会衍生出来对多路视频源的实时编辑和合图的需求。在直播助手的应用场景中,主播可能需要对多路视频源的采集进行实时合图和编辑,再添加本地素材和动态表情包来丰富直播效果,同时降低上行带宽压力。在多人互动场景中,为了降低接收端的带宽压力和性能损耗,需要在云端将多路主播视频合成一路流再发送给各个接收端。
随着 AI 技术在图像处理的快速发展,融合 AI 算法的高级视频前处理功能也得到越来越多的应用,诸如一些高级美颜功能、背景分割、背景替换。结合这三个场景我们可以看到,下一代视频引擎的灵活可扩展能力被提出了更高的要求。
另外,随着我们公司业务和团队规模的不断增长,下一代视频引擎的用户体量也在剧增,不同用户对集成开发的需求也各异。对于研发团队规模较小的开发者或个人开发者,他们需要的是引擎的集成简易度,低代码量,快速上线。而对于企业业务中台的开发者,会要求引擎开放更多的基础视频处理功能,从而可以定制化的实现视频处理业务。
面向各开发者群体的需求,声网的下一代视频处理引擎需要一个有弹性的设计来满足差异化的集成需求。
不仅是弹性的设计,视频直播体验也是很重要的指标。随着 5G 时代的到来,网络基础设施足够支持用户对更清晰更流畅直播的体验需求,下一代 SDK 必须在性能优化方面做到极致,支持更高的视频分辨率,更高的帧率。考虑到实时互动业务场景不断扩展,用户分布也越来越广,为了在网络基础设施较弱的国家和地区、性能较差的低端机型上,引擎都可以提供比较好的视频直播体验,我们就要支撑更好的弱网抗性,并优化性能资源消耗。
结合上面提到的场景丰富性、用户差异性,以及对视频直播体验的需求。我们将下一代视频处理引擎设计原则和目标可以总结为四个方面:
接下来我们进入第二部分,针对上面所提到的四个设计目标,声网具体采用了哪些软件设计的方法来进行实施落地。
02 下一代视频处理引擎的架构设计
前面提到的第一个设计目标,我们要满足不同用户差异化的集成需求:引擎的使用者是天然分层的,一部分使用者追求低代码快速上线,需要引擎尽可能提供贴近他业务的功能;另外一部分用户,需要我们提供更多的核心视频基础能力,在这之上客户可以按照自己的需要定制视频处理业务。
根据这个用户形态我们的架构也采取分层业务设计,分成 High Level 和 Low Level。Low level 部分是面向视频处理核心功能进行建模,抽象出了视频源处理单元,前/后处理单元、渲染器单元、编解码器单元、核心基础设施单元等。通过这些基础模块的组合和开发,在 Low level 的基础上,我们又抽象出面向客户业务的视频源 Track 的概念,网络视频流 Stream 的概念和场景的概念,在这上面封装了更贴近用户业务的 High Level API 。
我们来通过实际例子看一下 High Level 和 Low Level 两者在使用上的差别:假设现在要实现一个非常简单的场景,打开本地摄像头开启预览,发布到远端。
如果使用 High Level API,可以只通过 2 个简单 API 的调用,完成这个实时互动业务场景的搭建。首先通过 StartPreview API 开启本地摄像头并预览,然后通过 JoinChannel API 加入频道并发布视频流。若用户想在这一简单场景上实现更多定制业务功能,就可以使用 Low Level API。首先创建本地相机采集管线 CreateCameraTrack,这个 Track 提供了多种多样进行形态组建和状态控制的接口。同时我们将本地媒体处理和网络发布节点进行了解耦,视频流可以发布到声网自研的 RTC 系统中去或发布到 CDN 网络。
通过上面的例子可以看出。为了满足用户差异化需求我们采用了分层设计,High Level 面向业务提供易用性,Low Level 提供核心功能和灵活性。
我们看第二个目标,灵活可扩展这一点我们是如何做到的呢?在这之前我简单介绍一下关于视频处理当中的基本概念,视频处理的过程是以视频帧作为视频数据的载体。以本地发送处理流程为例,视频数据被采集之后会经过一系列的前处理单元,然后送到编码器单元进行压缩编码,最后根据不同网络协议通过封装之后发送到远端网络。接收的处理流程是从网络当中接收到视频流后进行解封装操作,送到解码器当中,经过一系列后处理单元后,再到渲染器进行展示。
我们把以视频帧为数据载体的序列化视频处理 称为视频处理管线,每一个视频处理单元我们把它称为一个模块。每个具体的视频处理单元可以有不同的实现,比如说视频源模块,可以是自采集的视频源,可以是摄像头采集的视频源或者屏幕共享的视频源。不同编码器根据编码标准和编码器实现也是可以有不同的扩展功能。网络发送节点可以根据不同协议发送到自研的 RTC 网络或者 CDN。不同的视频业务其实是基础视频处理单元根据业务灵活编排形成的。我们希望可以把灵活编排的能力作为我们视频处理引擎的基础能力开放给到开发者,这样开发者就可以通过灵活自由的 API 组合,搭建满足自己业务需求的处理管线。
为了做到这一点,我们的视频处理引擎核心架构是采用了 Microkernel Architecture 的架构,分离了整个引擎的变量和不变量。如图所示分两个部分,中间的 Core System 和外围的 Pluggable modules。中间黄色的部分是整个的核心系统部分,对应着整个下一代视频处理引擎的不变量。在核心系统中,我们抽象出来了各个基础视频处理单元的模块,以及提供了统一的控制面和数据面的接口。同时,引擎还提供了对这些基础视频模块进行组装和灵活编排的控制接口。此外,核心系统还提供了一系列基础设施功能,比如跟视频处理相关的视频数据格式转换,基础的视频处理算法,针对视频处理特征进行内存管理优化的内存池以及线程模型,日志系统和消息总线等。
利用核心系统底层能力,各个模块可以方便地进行业务扩展,比方说视频源模块,可以有推流模式视频源模块,也可以支持拉流模式的视频源模块,甚至支持一种特殊的视频源,即在转码的过程我们可以把远端用户的视频解码后的视频帧作为新的视频源加入到本地发送的管线当中去。前处理模块和后处理模块也可以扩展出各种各样的实现,如基础的裁减缩放功能、美颜、水印功能等。编解码模块更为复杂,一方面要支持多种编码标准,还有相应多种实现,软硬编等。同时,编解码选择还是一个比较复杂的动态决策过程,我们在编解码基础模块当中内置了根据能力协商、机型和实时视频编码质量进行动态选择切换的编码器选择策略。
接下来,结合实际运用场景看我们如何灵活的搭建视频处理管线满足不同的业务组合场景。回到在线教育场景当中,假设现在在一个复杂的在线教育场景当中,需要一个摄像头拍摄老师黑板书写,再有一路摄像头拍摄老师的人像,同时老师会通过屏幕共享进行课件分享,或者使用媒体播放器来播放本地或者在线的多媒体视频文件。一些高级场景当中,老师为了更好的直播效果,会开启背景分割和背景替换模式,把老师的头像和课件叠加在一起,达到更好的效果。老师还可以在本地开启录制功能,将自己直播上课的视频录制到本地。
针对复杂的组合应用都可以通过管线搭建来实现,上图是一个本地处理管线的概念图,对于刚才所说的拍摄黑板,老师人像和课件分享,我们可以通过动态替换采集源模块的具体实现来做。背景分割是特殊的前处理模块,可以将老师头像实时分析出来,然后再叠加到屏幕共享的采集源上。本地录制是一种特殊形态的渲染器模块,它是将本地视频帧按照文件格式封装、存储到本地路径当中,我们将整个媒体处理和最后网络发送进行解耦,它可以动态选择是推送到我们的 RTC 网络中还是推送到 CDN。
接下来看一个接收管线组合应用的场景,我们后台有一个后台媒体处理中心,可以根据用户业务处理器需求去进行实时的流媒体处理服务,其中包括云录制(对接收到的视频进行转储)云端进行视频鉴黄,低码高清处理,合图转码服务等。还有 Cloud Player 功能,将远端视频拉取下来之后推到 RTC 频道当中去。以及旁路推流,可以在我们 RTC 网络当中将接收到的视频流转推到 CDN 当中去。
那接下来我们看一下是如何通过搭建接收管线来满足不同运用场景的。
首先是我们网络接收源的模块,它可以通过动态切换来接收来自 RTN 网络或者 CDN 的视频流。通过解码器模块之后送到一系列后处理模块当中去,包括刚才提到的鉴黄模块、低码高清后处理模块等等。接收的渲染器模块的数量和位置都是可以灵活定制的,比如刚才云录制的功能,它实际上就是一个特殊的渲染器模块。
刚才介绍的是我们通过微内核式架构设计实现了灵活扩展目标,各个模块功能可以快速扩展的。视频处理管线也可以通过搭积木式的组合来实现业务的灵活编排。接下来我们看一下快速可靠这个目标,我们是想说我们核心系统要提供丰富且稳定的功能,在这基础上可以极大降低开发工作人员的心智负担,提升研发效能。
介绍这个之前,我们先想一个问题,如果我们没有一个稳定可靠的核心系统,一个开发人员要从零开始在我们的管线上开发一个美颜插件,需要思考哪些问题。
首先毫无疑问是要开发美颜本身的业务逻辑。除此之外,在跟管线集成的时候,首先要考虑模块在管线当中是否可以加载到正确的位置,前序处理模块对它有哪些影响,以及它的业务模块对后续功能产生哪些影响。
第二是数据格式问题,当管线上流转的数据格式不是美颜模块需要用到的格式的时候,它要对数据格式进行转换,这个数据转换算法实现的业务逻辑也需要模块的开发者来实现。
接下来是和管线集成过程当中,它需要了解整个管线的线程模型和内存管理模式。在配合管线的状态切换当中,美颜模块自身也要实现相应的状态控制的业务逻辑。同时,在一个管线当中,如果后续节点根据视频质量对前序节点有反馈的话,譬如后续节点说你需要调节你的吞吐量,它也需要有一种机制来接收和处理后续模块反馈的消息。同时,当美颜插件运行的时候,有一些消息通知要发送给用户的话,就需要设计一套消息通知机制。
由于美颜插件是集成到提供了核心功能的 SDK 当中,插件的开发就会变得特别简单,插件作者只要按照核心系统的接口协议的约定去实现相关的接口即可,核心系统会自动根据它的功能以及从全局性能优化的角度,把它加载到正确的位置,那我们 SDK 的使用者就可以使用这个插件了。
总结一下,关于快速可靠这一块实现了丰富强大的核心系统功能,可以极大降低模块开发者的心智负担,从而提升研发效能。
最后,我们来看一下性能优越可监控这一块,首先我们对整个视频处理管线在移动端上数据传递效率进行优化,实现了对移动端原生数据格式全链路支持,包括采集模块、渲染模块、前处理模块,使用硬件的情况下可以实现整个处理链路的零拷贝,同时根据各个模块处理特性协商,可以把相应模块在管线上的位置进行优化,减少 CPU 和 GPU 跨越,从而更好的提高数据传输效率。
另外,我们在刚才也提到了通过基础视频处理单元,将控制面和数据面进行了一定分离,这样有一定的好处,比如用户对模块控制可以得到及时的响应,对于摄像机这类设备操作,是属于比较重的操作。当用户频繁切换前置后置摄像头时,这类操作会阻塞用户 UI 造成较大延时。通过进行控制面和数据面的分离,我们可以在保证最后状态正确性的前提下实现快速响应的相机操作。并让控制路径不再阻塞数据流转。同时控制路径可以不阻塞数据流转,我们可以做到对本地图源进行实时编辑和发送。
降低系统资源消耗方面,我们构建了适用于视频数据存储格式的内存池,支持多种视频格式的帧间内存复用,同时,可以根据系统内存使用情况和管线负载情况、动态调整达到动态平衡的状态,这样可以减少频繁的内存分配和释放,从而降低 CPU 使用率。
最后,为了形成一个性能优化闭环反馈的通路,我们实现了全链路的性能质量监控机制,对每一个基础视频处理单元,都会统计和上报入帧和出帧的分辨率和和帧率,以及一些模块特有的数据。系统层面上对耗时较长的任务也有监控和上报,根据不同问题调查的需求,我们将这部分数据按需导入用户本地日志,并将体验相关的数据上报线上质量监控系统,达到对问题快速定位以及优化性能反馈的效果。
总结一下,在性能优越可监控方面,我们首先优化了移动端数据处理链路,分离了控制面和数据面,提升了整体视频数据的传输效率。另外构建了视频处理特性相关的内存池来降低系统资源消耗。最后,实现了全链路视频质量监控机制,来对视频优化性能达到闭环反馈的效果。
实际上,我们现在下一代视频处理引擎已经进入到了落地和打磨阶段。架构优越性在实践中也得到了验证,我们现在就来看一下实际应用案例。
下一代视频引擎具有高度的灵活性和可扩展性。基于这个视频引擎,通过业务组合方式搭建了前后端统一的合图转码通用框架,基于这个框架基础上,我们可以快速响应前端和后端的各类合图需求。比如在线视频相亲场景,这是典型的多人互动实时场景,传统一个嘉宾需要订阅红娘以及其他嘉宾视频流,对下行带宽和机器处理性能造成很大的压力,为了解决这个问题,我们快速运用合图转码通用框架上线云端合图项目,在云端将各个嘉宾和红娘视频合成一路流再推送给观众。同时合图布局和背景图、嘉宾视频中断显示策略可以根据用户业务进行定制: 比如显示最后一帧、背景图、占位图等等。
同样的合图转码框架应用在本地的话,我们实现了本地实时视频编辑混流的功能,可以用在电商直播等等领域当中,主播可以在本地将各种各样图源,比如多路摄像头,多路屏幕共享,媒体播放器和远端用户视频以及不同的图片素材进行实时合流推送。
我们分享就这些,谢谢大家!