愛奇藝直播 WebAssembly 優化之路


WebAssembly 技術簡介

近幾年,WebAssembly 技術非常火,可以說是成為了 JavaScript 一個新的轉折點。JavaScript 自 1995 年誕生之日起,其性能問題就被大家詬病。直到 2008 年,很多瀏覽器加入了即時編譯器,JavaScript 也開始引入 JITs,再加上 Google 等廠商對其的大力優化,其性能提升了 10 倍不止。由此,JavaScript 也開始跳出了瀏覽器的范圍,在各個領域嶄露頭腳,比如后台使用的 Node.js 和桌面端使用的 Electron 等。

 

JIT 技術簡而言之是在 JavaScript 解釋執行時將常用的二進制代碼塊暫存下來,在下一次解釋執行相同的代碼塊的時候可以直接運行暫存的二進制代碼塊,節約了解釋的時間。那能不能將所有 JavaScript 代碼一次性都編譯成二進制,提升運行效率呢?WebAssembly 的出現回答了這個問題。

 

在 WebAssembly 出現之前,JavaScript 是瀏覽器里可以運行的唯一的編程語言。而 WebAssembly 技術使瀏覽器運行別的語言編寫的程序變成了可能。目前可以使用 C、C++、Rust、Go、Java、C# 編譯器(還有更多)來創建 wasm 模塊。瀏覽器在運行時將 wasm 模塊放在專有的虛擬機中運行。由於是二進制的文件,運行效率比解釋執行的 JavaScript 腳本要高很多,因此,很多前端開發者也把 WebAssembly 技術視作下一代的前端技術。

 

目前 WebAssembly 的兼容性如下圖所示:

 

 

可以看到,在新版本上,主流瀏覽器不管是在 PC 端還是移動端都支持了 WebAssembly,而且各大瀏覽器廠商還在持續支持此項技術,相信不久就會得到非常普遍的應用。

 

WebAssembly 和直播,不一樣的火花

一直以來,愛奇藝生產的直播流有 mp4 和 flv 兩種格式,但 Html5 的 video 標簽原生只支持 mp4 的播放,如何解決 flv 格式在網頁端播放的問題就擺在了所有人的面前。一般來說 flv 格式在網頁端播放有以下幾種解決方案:

 

1、使用 flash 播放器插件

 

不過因為性能和安全等各種問題,各大瀏覽器已經逐漸弱化了這種方式,Chrome 也將在 2020 年左右停止對 flash player 的支持,所以現在基本很少有人用了。

 

2、網頁對 flv 格式的視頻解碼

 

使用 canvas 渲染圖像,使用 audio 播放聲音,相當於網頁端做一個播放器,這也是可行的。但各大瀏覽器廠商對原生 video 控件會針對不同的平台做硬件加速渲染的優化,如果自己渲染的話,硬件加速這塊便也需要自己做,這樣會耗費極大的人力,並且效果也很難和瀏覽器原生的硬件加速相比。

 

3、在網頁端將 flv 格式轉成 mp4 格式然后使用原生播放器

 

這也是目前使用得最多的方案。這樣既可以播放 flv 的直播流,也可以將渲染丟給原生播放器去做,充分發揮原生播放器的優化能力。

 

愛奇藝直播使用的就是第三種方式,當 flv 的直播流到達前端時,使用 JavaScript 將 flv 轉換成 mp4,再交給原生播放器。但由於 JavaScript 運行效率較低,這部分的性能一直都令人不太滿意,所以決定引入 WebAssembly 技術,看看是否能帶來不一樣的提升。現在打開任意的愛奇藝直播間,在后面輸入__enablewasm__=true,就能打開 WebAssembly 轉碼模式,如下圖所示:

 

 

體驗上,兩種模式都能滿足流暢觀看直播的需求。由此可見,WebAssembly 模塊可以很完美地替換原來的 JavaScript 所寫的轉碼模塊。下面來看一下如何接入 WebAssembly。

 

接入 WebAssembly 的步驟

使用 WebAssembly 非常簡單,總的來說,分為以下幾步:

 

1、使用 c 編寫 flv 轉 mp4 的代碼

 

首先定義 WebAssembly 被 js 調用的接口文件:

 

 

如果想在被編譯成 wasm 文件后可以被 JavaScript 調用,就需要在可以被外部調用的函數前使用 EM_PORT_API 來標識,這樣在后面編譯的時候 WebAssembly 就會將此函數作為可被 JavaScript 調用的方法拋出。

 

然后還需定義一些 WebAssembly 調用 JavaScript 的接口,如下面所示:

 

 

主要是通知 js 轉換 mp4 流的頭部信息和已經轉好的部分流的緩存區地址等,實際調用的代碼需要使用 EM_ASM_()函數包起來,里面填上調用的 JavaScript 方法名和帶的參數。

 

定義好接口后就是轉碼實現的部分了,這里涉及到 flv 和 mp4 格式的相關知識(對這兩種格式不太了解的同學可以自行閱讀相關文檔)。整體轉碼采用 flv 和 mp4 雙緩存區的模式,流程如下圖所示:

 

 

a. JavaScript 獲取到直播流后將流存入 flv 緩存區;

 

b. JavaScript 通知 WebAssembly 緩存區首地址和進度;

 

c. WebAssembly 請求緩存區數據;

 

d. WebAssembly 進行轉碼;

 

e. 將轉好的 mp4 片段存入 mp4 緩存區;

 

f. WebAssembly 通知 JavaScript 轉碼進度;

 

最后由 JavaScript 通知原生播放器直接播放 mp4 緩存區的視頻流。

 

2、使用 emcc 編譯出 flv2Mp4.js 和 flv2Mp4.wasm

 

首先需要安裝 emscripten 環境,安裝和配置的具體步驟可以參考 emscripten 的官網

 

安裝完成后就可以使用 emcc 命令編譯 c 文件了,使用命令 emcc main.c -s TOTAL_MEMORY=268435456 -g -o flvToMp4.js,最終可以得到兩個文件,flvToMp4.js 和 flvToMp4.wasm。

 

其中 flvToMp4.wasm 是實際轉碼的 code,flvToMp4.js 相當於接口文件,播放器可以通過引入 flvToMp4.js 來加載 wasm 文件和調用 wasm 文件中的二進制代碼。

 

通過閱讀 flvToMp4.js,我們可以發現自動生成初始化 WebAssembly 的相關代碼,獲取 WebAssembly 的二進制文件后,調用了 WebAssembly.instantiate(),初始化了 WebAssembly,並且在獲取或加載 wasm 文件失敗后還能再次重試。

 

 

通過查看 getBinaryPromise()方法也可以看到下載的過程。

 

 

使用自動生成的代碼,就基本可以不用管加載 wasm 文件等問題,非常方便。

 

3、對接編譯好的 wasm 文件

 

因為轉碼是高 cpu 的工作,所以將其放入 web_worker 中運行,這樣不會阻礙主線程的渲染。

 

 

worker 創建后將事件和主線程對應綁定,也即綁定前面定義的 wasm 調用 JavaScript 的幾個方法:

 

 

上面就是 WebAssembly 通知 JavaScript 的相關消息接口的定義,到此已經完成了整個轉碼過程的全部接口定義,全部流程就如下面的時序圖所展示。

 

 

由時序圖可以看到播放器在收到流時就會初始化 WebAssembly 模塊,初始化完成后進入轉碼階段,通知 WebAssembly 進行轉碼並存入緩存區,再通知播放器播放。

 

使用 WebAssembly 實際性能對比

體驗上能保持一致,那實際性能上有多少提升呢?還是要用數據說話。愛奇藝直播團隊先后使用代碼打點和瀏覽器自帶的性能監測工具實時監測數據的方式來測試 WebAssembly 的實際使用性能。

 

1、直播流轉碼效率情況

 

首先,測試使用 WebAssembly 實際轉碼的速度。分別使用原來 JavaScript 所開發的轉碼模塊和使用 WebAssembly 的轉碼模塊進行轉換,在實際直播間轉換 flv 流數據包的前后進行打點計時,最終得到的數據如下所示:

 

 

前后各挑取了 30 包 flv 數據,表中第二列是每一包轉碼耗時,第三列是包的大小,在表的最后統計了總包的大小和總耗時,由此計算出未開啟 WebAssembly 和開啟 WebAssembly 的平均傳輸速率分別為 35305.6 字節/s 和 46608.1 字節/s。可以看出,WebAssembly 開啟后轉碼速度的提升還是非常明顯的。

 

2、運行時瀏覽器資源消耗情況

 

WebAssembly 實際應用在直播間中,能給直播間帶來什么樣的提升呢?最明顯的是 cpu 占用率的下降。這一點可以通過使用 Chrome 瀏覽器自帶的 Performance monitor 對使用 WebAssembly 前后的資源消耗做對比來證明。

 

 

如上圖所示,可以在開發者工具 More tools 中找到 Performance monitor。通過這個工具,可以大概得到平時運行時的 cpu 占用率。下面兩張圖分別顯示穩定播放時未開啟 WebAssembly 和開啟 WebAssembly 的 cpu 占用率情況:

 

  • 未開啟 WebAssembly

 

 

  • 開啟 WebAssembly

 

 

從圖中可以大致看到,未開啟 WebAssembly 時,cpu 占用率基本穩定在 7%左右,而開啟 WebAssembly 之后,cpu 占用率能穩定在 5%上下,由此可以估算出大約有 10%-20%的提升(注:使用測試機型為 macbookpro 2018 款,cpu i7 2.2 GHz,不同機器測試出的性能可能有差別,波動狀況也不完全一致,但在不同平台開啟 WebAssembly 基本都能獲得不同程度性能的提升)。

 

WebAssembly 未來的更多可能

使用 WebAssembly 轉碼還只是 WebAssembly 的一個非常小的用處,愛奇藝直播團隊還將使用 WebAssembly 技術實現更多有趣、有價值的功能,比如:

 

  • c++項目的移植。現在很多圖像相關的項目、算法都是由 c++編寫的,如果想在瀏覽器上運行,以前就只能使用 JavaScript 重寫一遍;而現在,通過 WebAssembly,只需要極小的改動,就使其能在瀏覽器上跑起來。

  • 算法的加密。由於 WebAssembly 編譯成的 wasm 是二進制文件,反編譯的成本很高,部分保密性比較強的算法會使用 WebAssembly 技術。

  • H.265 編碼格式的支持。H.265 編碼方式憑借其出色的壓縮比,被越來越多的產品所應用,但目前各主流瀏覽器原生還不支持 H.265 的硬解。但是也可以根據同樣的思路,使用 WebAssembly 將 H.265 的流轉化為 H.264 的流,然后再使用原生播放器播放,最終達到 Web 端播放 H.265 流的效果,這樣可以極大地降低帶寬成本。

 

得益於性能上的提升,WebAssembly 開始在各個領域嶄露頭腳,今后,愛奇藝直播團隊也將嘗試使用 WebAsssembly 實現更多的功能來優化愛奇藝的直播體驗。

 

原文鏈接https://mp.weixin.qq.com/s/LRGNOuFwHXALs_lhPyN3Zw


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM