需求
随着 RTC 技术的发展,音视频通讯的门槛降到了一个极低的标准。移动端、PC 端、Web 端、小程序,随手拿起一个设备就可以完成高质量的音视频通话。而且伴随着移动互联网的发展(4G,5G),AI 技术的演进,人们对于音视频通讯的需求不再停留在听得见看得到,而是开始追求更多交互新颖的通讯方式,如美颜、道具、互动涂鸦等等。音视频通讯方向的拓展层出不穷,尤其在 ToC 的场景。
从技术的角度来讲,原生的视频处理技术已经并不稀奇。很多类似于 OpenCV 这样的库早已将自己的人脸捕捉,图像处理等能力开源,新建一个工程调用不多的接口就可以实现一些简单的视频处理。然而 Web 端在这一块却始终落后原生,再厉害的前端技术在鼓吹性能的时候都只敢说接近原生,其瓶颈在此可见一斑(JavaScript 的设计初衷并不是运行速度)。
技术选型
ActiveX 方案
2000年左右,微软为了打败当时崛起的新兴浏览器 Netscape,希望研发一个方案可以让自己的龙头产品 office 在 IE 上运行,这便是 ActiveX 技术。听起来很梦幻的一项技术原生与浏览器的无缝交互。ActiveX 与 Office 的结合最终也确实遏制了 Netscape 的发展,让 IE 浏览器在很长一段时间霸占着主流的地位。
ActiveX 实际就是一个基于 COM 标准开发的 COM 组件,其通过在安装的时候将自己的 GUID 配合安装路径写入到注册表中,JavaScript 可以轻松通过 GUID 来加载到这个原生对象并通过简单的点语法完成调用。由于是 COM 组件,接口调用居然是直接在内存间进行,与原生工程调用动态库(DLL)无异。更离谱的是,ActiveX 支持原生的 UserControl 直接渲染在浏览器。MFC、QT、winform、WPF,主流的 Windows 界面开发框架都可以完成 ActiveX 的开发。(不得不承认,随着移动互联网的蓬勃发展,PC 上的开发技术开始式微了,这些名词远没有 flutter、vue 等名词来得耳熟能详)。在使用 WPF 完成了一个 ActiveX 插件的开发调用后我们大受震撼。就是这样一个听起来无所不能的方案,为什么就变得如此冷门?答案是:安全性。
由于 ActiveX 的高权限,高灵活性,使得它可以在用户的 PC 上“为所欲为”。肆意操作新增或修改本地文件内容、访问登录信息、在浏览器中直接运行外部可执行文件等,光听这些就让人觉得毛骨悚然。在21世纪初,互联网刚刚兴起的年代,大家普遍没搞明白计算机、互联网是什么的年代,不知道有多少游戏账号就因为用户点击了允许加载 ActiveX 插件而被盗。
所以 Chrome、Firefox 等浏览器开始逐渐抛弃对 ActiveX 的支持,就连微软自己也在 Edge 中不再支持 ActiveX ,只有年老失修的 IE 还在顽强支持。然而可惜的是,IE 浏览器也已经停止维护,而且即将退出 Windows 系统预装的名单。大势所趋,ActiveX 的方案也注定被淹没在技术发展的潮流中。
ActiveX 很好,尤其是银行、政府等使用私有网络的单位,ActiveX 的安全性问题对他们来说似乎不那么致命。然而我们不可能针对一个将死的技术来进行我们的新方案设计,或者说 ActiveX 会是我们特定场景下的备选方案,但永远不可能成为我们的首选。
WebAssembly 方案
随着 ActiveX 的没落,急需有一个新的方案来补充原生与前端交互的需求,此时 WebAssembly 应运而生。
通过 Emscripten 即可将 C、C++、Rust 代码编译为 WebAssembly,编译获得的 .wasm 文件为一种可被 JavaScript 调用的字节码。
看到这些,这个方案是令人期待的,于是我们动手来搭建自己的 WebAssembly。目前比较成熟的支持 WebAssembly 框架有 Unity、QT 等,Unity 与 QT 编译 WebAssembly 的过程都很简单,可以轻易搭建出测试 Demo,且原生的界面也很好地渲染到了前端,不禁让我想起 ActiveX 曾经的荣光!
接下来让我们来调起摄像头并做些简单的视频处理吧。满怀期待写好代码,尝试在前端运行,无法完成。看了一眼 QT WebAssembly 的官网:
QtMultimedia 框架已经被确定无法在 WebAssembly 使用,连他们自身都还没搞清楚哪些 Module 可用哪些不可用,在我们看来前路有数不清的“坑”。
为了保证安全性,WebAssembly 运行在沙盒环境,其权限必然受限。我们开玩笑地聊到,对于开发者而言 WebAssembly 相对于 ActiveX 都像是一种退步(对用户无疑是进步)。
本着科学严谨的态度,我们决定另辟蹊径将这个方案验证到底,由前端采集视频,WebAssembly 做处理,以此来验证其最终的可行性,以及网上所吹嘘的接近原生的运行速度。
幸运的是 OpenCV 提供了 WebAssembly 的版本,正好可以供我们做一些简单验证。搭建原生工程,集成 OpenCV 的 C++ 版本,而 WebAssembly 版本的 OpenCV 其官方已经提供了测试地址,帮我们节省了不少工作。
以双边滤波为例,选取一组合适参数进行对比验证,diameter 选取15,sigma 选取30。
WebAssembly 的表现如下:
视频的帧率已经下降到了 4FPS(上下浮动),观感已有明显卡顿。
原生上的表现如下:
视频的帧率依然保持 16FPS(上下浮动),虽然体验有所影响,但是该值依然满足 RTC 传输要求(RTC 传输一般视 13~30FPS 为正常)。
继续在原生上添加高斯滤波处理,选取高斯核长宽各为3,表现如下:
视频的帧率依然保持在 14FPS(上下浮动),对性能影响可忽略,依然满足 RTC 传输要求(RTC 传输一般视 13~30FPS 为正常)。
其他参数的表现大致与这组测试相同,起码在特殊场景的视频处理中 WebAssembly 的性能是远低于原生。当然可能是 OpenCV 对 WebAssembly 的支持还不够好,但是这组对比以及 WebAssembly 的权限支持已经让我们对其有些失望。
WebSocket 本地连接方案
这个方案并无系统的定义,其实现思路为以原生工程为 Server,前端通过 localhost 的端口与其交互,数据量小的可以用 HTTP(支持的浏览器更广),数据量大的可以用 WebSocket(IE10以上)。对于 RTC,如果发送在前端,则 WebSocket 可能需要承担每秒数M的数据传输来将视频帧从原生进程发送到前端,前端还需要通过 WebGL 进行渲染。
虽然是在本地通信,但溢出的采集帧率以及音视频在两个进程采集所可能导致的音画同步问题都让我们对其的表现表示担忧,所以并未有过多尝试。
虚拟摄像头方案
多个方案都行不通,让我们对 ActiveX 念念不忘。COM 在性能上有着其他方案所不具备的巨大优势,其他方案或是性能不如原生或是鼓吹性能接近原生,而 COM 则是实实在在的原生性能。
围绕 COM 进行一番调研,我们发现还有其他路径可以满足我们的需求,即 COM 组件结合 DirectShow 来将视频发送到模拟摄像头,从而在采集层面完成偷天换日!如果这个方案可行,那么最终的产品将不仅可以用于我们眼下的场景,所有使用 DirectShow 进行摄像头调用的应用都将可以使用我们封装的视频处理技术。
搭建 COM 工程,封装 AI 数字人形象的实现,调用 DirectShow 接口完成虚拟摄像头注册与视频流传递,编写批处理脚本将我们的 COM 注册到系统路径。完成一系列工作,使用诸多摄像头测试工具进行测试,效果出奇的好。
以下为使用 AR 面具处理后的虚拟摄像头接入网易会议的效果:
最终方案
经过大量的方案验证,我们决定以虚拟摄像头的方案作为我们最终的方案,无论从性能还是耦合性来说,这个方案都是无可挑剔。
方案结构
关键实现
1. 首先我们新建一个动态库工程命名 WebCamCOM,并使用 CoCreateInstance 与 RegisterFilter 等接口将我们的对象注册为 DirectShow Filter。
2. 借助 memoryapi.h 的接口来传递我们定义的数据,此处我们除了传递基本的视频数据之外还额外传递了视频长宽与时间戳的信息。
3. 通过使用 CreateMutex 来保证内存共享时的访问安全。
4. 新建另一个动态库工程命名 SharedImageWrapper,对外只定义一个接口。
5. 根据 shouldRotate 入参来决定是否需要做垂直方向的翻转(用于适配 Unity)。
6. 简单处理 data 之后同样通过 memoryapi.h 接口往我们定义好的 DirectShow Filter 传递视频数据。
7. 上层集成 SendImage 接口即可以将采集的 RGB 数据发送至 DirectShow。
8. 编写批处理脚本,使用 regsvr32 命令以管理员权限将 WebCamCom 注册至系统注册表。
问题
- Unity 对 Texture 的采集自下而上,直接使用其 data 会有上下颠倒的现象,故需要做一次垂直翻转。
- Unity 可选 OpenGL 渲染与 Direct3D 渲染,两种渲染方式的 Texture 句柄解析需要用两套接口。
OpenGL:
D3D:
展望
DirectShow 虽然是目前主流的操作摄像头的框架,但是 Media Foundation 框架的使用已经成为趋势,考虑未来将接口适配到 Media Foundation 框架(基于 USB 摄像头驱动开发也是一个可行的方案)。
目前视频处理所支持的能力主要还是围绕数字人形象、美颜、虚拟背景,基于现有的框架其实可以结合更多好玩的视频处理技术进来。
插件本身可以结合 WebSocket(HTTP)的方案来开放一些接口,诸如美颜参数、数字人形象的外形,这样前端可以默默完成对插件的配置。
插件可以整合出实用的设置界面,可通过拖拖拽拽查看预览效果。
总结
本文介绍了网易在 PC Web 端视频处理方案上的一些探究,从多个方面对比了一些可选方案的优劣,最终在虚拟摄像头方案上大致阐述了实现思路。也许您不从事音视频领域的开发,也许您对 PC 开发不以为意,希望本文可以给您一些不一样的角度去认识这些技术。受限于篇幅,未对核心的 COM 组件机制做详细介绍略有遗憾,大家如有兴趣也可逆潮流来玩转一下 PC 开发中的黑科技。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。