鬥魚 H5 直播原理解析,它是如何省了 80% 的 CDN 流量?

2022-10-11 06:01:16

鬥魚直播相信大家都聽說過,開啟鬥魚官網就可以直接在瀏覽器中觀看直播。那麼鬥魚是如何實現瀏覽器視訊直播的呢?本篇文章就來解析鬥魚是如何實現直播的,以及它是如何節省 80% 的 CDN 流量,要知道視訊直播流量費並不便宜,鬥魚每個月光這些流量費都要支付幾個億,節省 CDN 流量就是省錢。

直播技術方案

在實際去鬥魚直播間偵錯視訊直播之前,我就猜它肯定是使用 HTTP-FLV 方案來實現視訊直播,因為國內幾乎所有直播平臺都是使用 HTTP-FLV 方案。

但是去鬥魚直播間並沒有找到 .flv 的網路請求,而是找到了 .xs 的網路請求,如下圖所示。

image.png

不過 .xs 網路請求的響應的 Content-Type 是 video/x-flv,原來只是字尾不同,看來我猜的果真沒錯,鬥魚就是用的 HTTP-FLV。

HTTP+P2P FLV 拉流

不過為什麼字尾是 .xs 而不是 .flv 呢?其實這裡是因為鬥魚預設並不完全使用 HTTP 去拉流,而是採用 CDN 和 P2P 兩種方式同時去拉流,.xs 並不是一個完整的 FLV 流,而是一個子 FLV 流。

進入鬥魚直播間,鬥魚首先會去請求一個完整的 FLV 流,等 P2P 連線好了再去切換成子流。這是因為 P2P 連線比較慢,如果走來就走 P2P,那麼視訊起播速度會非常慢。

上圖中第二個連線就是一個完整的 FLV 流,等 P2P 連線成功後會斷開連線去拉子流。

在 P2P 連線成功後,還可以在網路面板看到一個 WebSocket 連線,如下圖所示,它是鬥魚用來推播其他正在觀看當前流的使用者的,這樣播放器就可以直接從推播的使用者這裡拉流。

鬥魚 P2P 是基於 WebRTC 的 DataChannel,可以開啟 chrome 的 WebRTC 的偵錯頁面,可以看到有很多 WebRTC 連線,它可以接收其他使用者分享的視訊資料,自己也會共用當前下載到的視訊資料給其他使用者。

鬥魚將一個完整的直播流進行切片,分成一個個小的視訊分片並進行編號(這樣方便使用者之間共用)然後將這些小分片分為多個子流,通過 HTTP 從 CDN 拉一路子流,然後通過 P2P 去其他使用者那裡拉其他的子流。

但是通過 P2P 從其他使用者那裡拉流並不是很穩定,例如其他使用者可以能退出了直播間,或者網路出了問題,這樣就會導致接收它分享的使用者直播斷流。為了提升直播穩定性,如果在一定時間內沒有收到其他使用者分享的資料,鬥魚播放器就會立刻從 CDN 去拉對應的子流,並且 WebSocket 也會推薦新的使用者給播放器。

可以發現,加上 P2P 拉流,大大增加了直播的複雜度。但是它帶來的好處也非常的明顯,就是可以省錢,省到就是賺到!因為流量費非常的貴,鬥魚每個月光直播頻寬都得花好幾個億。利用 P2P 從其他使用者那裡拉流可以節省大量流量,例如一個直播流分為兩個子流,一個從 CDN 拉,一個從其他使用者那裡拉,這樣理論上就可以節省 50% 的流量,而鬥魚將一個直播流分成 6 個子流,一個從 CDN 拉,其餘 5 個全部從其他使用者那裡拉,理論上可以節省超過 80% 的直播流量!

當然 P2P 拉流也有一些缺點,例如直播延遲較高,不適用於低延遲直播場景,對使用者電腦和頻寬有一定消耗,因為除了從其他使用者那裡拉流,當前使用者自己還要上傳視訊資料給其他使用者。

如果你想關閉 P2P,也比較簡單,可以在網路面板遮蔽下圖中的地址即可。

遮蔽之後,鬥魚就只會從 CDN 拉流,不走 P2P,如下圖所示,可以發現流的地址變成正常的 .flv 字尾。

無論是隻使用 HTTP,還是使用 HTTP + P2P,它們的最終目的是獲取 FLV 視訊資料。

FLV 格式

FLV 視訊格式是由 Adobe 公司開發,在 2003 年釋出,用於視訊檔在網路上傳輸。在 Flash 時代幾乎所有串流媒體平臺都在使用 FLV 格式,但是隨著 Flash 技術的淘汰,FLV 也跟著沒落了,目前國外已經沒有串流媒體平臺在使用 FLV 了,但是在國內 FLV 卻廣泛用於網路直播場景。

不像 Flash,H5 的 video 元素是無法播放 FLV 視訊的,我們需要藉助 MSE 來自己控制視訊播放,具體原理是將 FLV 轉封裝成 FMP4 視訊格式,然後交給 MSE 播放即可。

MSE 全稱是 Media Source Extensions API,它是 Web 串流媒體的基礎,所有 Web 串流媒體平臺最終都會用到它,如果對它感興趣,歡迎檢視 串流媒體視訊基礎 MSE 入門 & FFmpeg 製作視訊預覽縮圖和 fmp4

目前有開源的 flv.js 來幫我們完成這件事,檢視鬥魚 dist 後程式碼,鬥魚也是使用的 flv.js,不過在之上加了很多自定義的程式碼,例如加上了 h265 編碼的支援,flv.js 是不支援 h265 編碼的,FLV 官方規範也不支援,但是業務又有這種需求,所以一般將 FLV 視訊編碼 ID 等於 12 當作 h265 的流。在鬥魚直播中如果發現直播流是 h265 編碼並且瀏覽器不支援 h265,鬥魚會利用 WASM 來軟解播放視訊。

直播時移

對於賽事直播鬥魚是支援直播時移的,如下圖所示。

但是這個播放器的進度條體驗不是很好,進度條的高度只有 3px,滑鼠非要精準的放上去,才能有 Hover 的效果,這是沒那麼容易做到的。這裡推薦個好用開源的播放器進度條 ppbar,你可以把它整合到任何播放器中去,非常的好用。

鬥魚直播時移是基於 HLS 的,如果點選一下進度條,鬥魚播放器會黑一下,將 FLV 切換成 HLS。

在剛開始進入直播間拉流的時候,鬥魚播放器可以獲取到伺服器返回的一個時間戳,單位是秒,當用戶點選進度條跳轉到前 10 分鐘時,就直接用當前時間減去 600 秒就得到了前 10 分鐘視訊的時間戳,然後會用這個時間戳去請求請求一個 getVodStream 介面獲取到 HLS 時移流地址,獲取到 HLS 過後,就和普通 HLS 直播一樣去播放即可。

和 FLV 一樣,要在瀏覽器中播放 HLS 流,同樣需要 MSE API 來播放,目前可以藉助開源的 hls.js 來在瀏覽器中播放 HLS 流。檢視鬥魚 dist 過後的程式碼,鬥魚應該沒有使用 hls.js,而是自己實現在瀏覽器中播放 HLS。

總結

這篇文章介紹了鬥魚 H5 直播技術的原理,鬥魚不僅使用國內常用的 HTTP-FLV 方案,還加入了 P2P 拉流,從而節省 CDN 流量。對於賽事直播,鬥魚還支援直播時移,直播時移是使用 HLS 來實現的,使用者在 seek 後會通過 seek 到的時間點去伺服器換取對應的時移 HLS 流地址,然後走 HLS 拉流即可。