基于 Web 引擎扩展技术的 RTC 混合开发框架实践

为什么我们要独立开发WEBRTC框架

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

  1. 现在市面上缺失好的基于WEBRTC的框架,我们希望做的框架能够充分利用原生平台的*拓展能力、安全性、以及WEB的一些优良开发特性及生态等等。像传统原生的音视频应用,一般在针对特定平台系统的客户端,都会做特殊的技术优化,比如window平台会使用MFC或WCF等技术,能做到16/32/64路视频的高清解码等等。像一些内容分发型应用,就需要突出内容更新及布局拓展能力,比如我们经常使用的桌面版微信,就是利用了前面提到的特性,除了内容渲染,为了保证传输的安全性,在实现方面将网络通信部分放在了原生平台去做。

  2. 未来Web应用是什么样的呢?2007年谷歌CEO的对web3.0下定义的时候,他提到,希望未来的应用非常小,可以快速访问,并且能够支持跨平台。今天,就像我们现在经常使用的微信、支付宝里面的小程序,华为的快应用等,我们通过扫二维码或者点击链接的方式,就直接可以跳转到目标平台的应用,这就是一些WEB应用的优势,我们前端要发扬这些长处!

基于WebRTC的Web应用面临的问题

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

目前,针对WebRTC来说,最大的问题就是使用体验不理想,经常卡顿,主要问题我们可以简单从下面三点分析:

  1. Web平台碎片化严重。WebRTC官方正式标准一直到最近两年才被确定下来,而且标准的迭代非常迅速,比如国外的chrome、Firefox,国内的360浏览器、QQ浏览器等等,都有基于自己想法上的可选性实现。

  2. WebRTC实现细节不可控。对于很多Web的开发者而言,我们往往比较关注的是Media、Vedio这些HTML5标签本身,但比如我要想控制里面的一些网络传输FEC、弱网对抗、带宽估计、优先级保障等算法、使用特殊的编码解码,依赖纯Web是做不到的,这不是浏览器的使命,如果想要做这种更底层的控制,就只能要想其他办法参与到里面去。

  3. 计算密集型算法可选项少。我们很多时候需要对视频进行一些预处理的动作,比如美颜功能,现在通用的做法是首先需要获取到用户本地的一些原始的视频采集数据,然后绘制到canvas上,通过canvas接口获取到imagedata,将imagedata传递到Web Assembly,然后再对它进行进一步的算法运算,比如磨皮、提亮等等这些操作,然后重新绘制到canvas里面,这个过程链路本身比较长。还有一点就是Web Assembly技术本身,也是一直在不停发展,他本身有自己的限制,比如Web Assembly的实现是基于chrome本身的一个render主线程驱动。虽然后续出现了Web worker的工作线程实现以及并行计算等,但是执行性能也不是特别好,如果我们希望能够充分利用的计算资源,在这种情况下就很难实现。

另外,对于GPU部分,WebGl实现的细节上是有侧重点的,因为浏览器WEB本身是要面向整个标签页的渲染,而不仅是视频窗口这一小块,所以相比专业的视频渲染性能方面的表现并不太好。

声网的一些实践解决思路分享

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

  1. 统一渲染引擎。针对Web平台碎片化问题,我们提供了一致的渲染引擎,让用户使用一个统一的运行渲染环境,以此来解决厂商、版本等差异引入的碎片化问题。

  2. 对于WebRTC的实现细节、算法这一块,属于应用可扩展部分,我们会尽量想办法,将这些能力封装到原生平台内部去实现。

PC端应用的框架选型

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

在最开始我们做技术评估的时候,可选框架有几大类,Electron、CEF、Qt等等,但很多的开发框架,像CEF和Qt基础技术栈比较偏向C、C++、Java等等,开发习惯和生态上和我们前端开发体验不一致,尤其是它的开发工具链是需要独立构建,所以最终我们选择了Electorn,它是基于Nodejs的一个技术方案,前端在开发的时候,我们可以使用熟悉的开发环境,比如说Npm、yarn,还有包括后续的测试打包等整个构建过程。

扩展能力混合注入时的一些问题

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

  1. 原生扩展能力问题
    对于原生扩展能力的注入,我们将一些关键的业务逻辑的实现,通过用C++代码放到V8引擎里与原生能力结合起来的,后续通过bridge,将能力暴露给上层。这里最大的问题在于每一个V8引擎版本,底层C++的接口并不一致,并且,随着nodejs版本的升级,每个版本里面的接口实现也略微不同的,这也是长期以来让社区比较诟病问题。这里我们的解决方案是,采用nodejs提供的node api实现,它是向后兼容的,以此来保持底层接口一致性,解决开发插件接口和node版本依赖的问题。

当我们的js层调用通过bridge注入到native后,驱动我们另外一个团队开发的native SDK,包含了声网主要的功能和算法,比如我们的RTN网络接入、AI降噪算法、背景分割、超分算法等等。另外从前端开发体验上,用户只需要改动少量的代码,就可以完成基于agora sdk ng开发的自有APP的适配,这也是我们框架的初衷之一,保持外部开发用户的体验的基础上,能够提升它里面的应用能力。

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

  1. 异步事件驱动
    音视频应用的特点就是它的数据体积比较大,每一帧数据根据用户视频分辨率的不同,从几K到几十M都有可能,同时,我们还必须对这些数据进行解码、编码等等操作,这就要求我们能够去充分使用用户本地的计算能力,这里就涉及到子线程相关的调度操作,通过异步事件驱动,我们可以设计异步事件的队列管理,原生平台生产、主线程消费的模式。另外因为单个数据体积比较大,如果渲染线程和异步线程数据交互通过copy进行交互,内存开销可想而知,这里就必须使用到一些node api提供的external-ArrayBuffer技术,类似智能指针引用的解决方案,我们只需要保证Buffer的生命周期即可,即在js层调用期间可用,指针销毁时同时销毁buffer,避免内存泄漏。

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

  1. 动态分辨率问题
    受限于网络风暴等等问题,我们用户很多时候分辨率很难保持1080P不变,这个时候我们就需要一些平衡算法,在帧率和分辨率之间做平衡,这种情况下,视频宽窄就有可能发生变化,那我们就需要针对这种情况做一些特殊处理,canvas是可以通过更新texture的方式,适配视频数据帧的宽高、步长等,然后通过CSS样式控制整体页面渲染比例及布局即可,不需要再做重采样来保证宽高比,这个做法也是和传统原生视频应用渲染方面有明显差异的地方。

音频播放

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

本地音频采集完一般不需要本地回放,但在一些业务场景下,我们的用户希望能够根据自己的声音反馈做一些特殊处理,比如用户会希望可以通过直观的听到自己的音量来调整麦克风的相对位置等等。而且这也算是我们为了能够保证与agora sdk ng这一致性功能。由于浏览器音频播放是异步执行的,即主线程将音频数据转发给音频线程,音频线程播放结束后通知主线程,主线程再处理下一个音频数据段,这个过程本身是会有始终消耗的,所以在实时音频播放场景中的直观体验就是,随着时间增长,时延累加明显,并且数据播放间隔出现有明显噪音。

我们的解决方案比较简单直接,采用audio workletprocessor接口,由我们来控制音频传递给音频播放线程的频率,流式传递音频数据到异步线程,这样就避免线程间反馈造成的时延累加,听起来会很顺滑,加上每次数据段比较短,约10ms左右,用户听起来就感觉自己的声音变大了,没有明显的回音感。

未来展望

基于 Web 引擎扩展技术的 RTC 混合开发框架实践

未来我们希望能够把更多声网nativeSDK能力暴露到上层应用中,比如我们的桌面共享、AI降噪、美声、vp9编码解码、背景分割、美颜、超分等等,我们声网内部已经进入到测试环节,后续也希望能够赋能给我们的用户更加方便、强大的Web应用开发能力。

上一篇:基于 Web 引擎扩展技术的 RTC 混合开发框架实践


下一篇:nrf52832 利用app_timer 产生精确的1ms 心跳