Web端實現RTC視頻特效的解決方案

語言: CN / TW / HK

需求

隨着 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 註冊至系統註冊表。

 

問題 

 

1. Unity 對 Texture 的採集自下而上,直接使用其 data 會有上下顛倒的現象,故需要做一次垂直翻轉。

圖片

 

2. Unity 可選 OpenGL 渲染與 Direct3D 渲染,兩種渲染方式的 Texture 句柄解析需要用兩套接口。

 

OpenGL:

圖片

 

D3D:

圖片

展望

 

  • DirectShow 雖然是目前主流的操作攝像頭的框架,但是 Media Foundation 框架的使用已經成為趨勢,考慮未來將接口適配到 Media Foundation 框架(基於 USB 攝像頭驅動開發也是一個可行的方案)。

 

  • 目前視頻處理所支持的能力主要還是圍繞數字人形象、美顏、虛擬背景,基於現有的框架其實可以結合更多好玩的視頻處理技術進來。

 

  • 插件本身可以結合 WebSocket(HTTP)的方案來開放一些接口,諸如美顏參數、數字人形象的外形,這樣前端可以默默完成對插件的配置。

 

  • 插件可以整合出實用的設置界面,可通過拖拖拽拽查看預覽效果。

 

總結

 

本文介紹了網易在 PC Web 端視頻處理方案上的一些探究,從多個方面對比了一些可選方案的優劣,最終在虛擬攝像頭方案上大致闡述了實現思路。也許您不從事音視頻領域的開發,也許您對 PC 開發不以為意,希望本文可以給您一些不一樣的角度去認識這些技術。受限於篇幅,未對核心的 COM 組件機制做詳細介紹略有遺憾,大家如有興趣也可逆潮流來玩轉一下 PC 開發中的黑科技。

作者:

 
金傑,網易雲信客户端開發工程師,從事音視頻能力及IM能力場景化開發五年有餘,偏愛移動端開發。