上一篇文章《WebRTC 開發實踐:為什麼你需要 SFU 伺服器》我們瞭解了 WebRTC SFU 伺服器的基本原理和必要性,解決了 What 和 Why,本文則更近一步,探究一下實現 SFU 伺服器的關鍵技術點有哪些 ?重點解決一下 How

1 什麼是 SFU ?

首先,我們再看一次 SFU 伺服器的定義,什麼是 SFU ?

SFU 的全稱是:Selective Forwarding Unit,是一種路由和轉發 WebRTC 客戶端音視頻數據流的服務端程序。

如圖所示,SFU 伺服器最核心的功能就是與每一個 WebRTC Peer 客戶端建立鏈接,分別接收來自他們的音視頻數據,並實現 one-to-many 的能力(即把一個客戶端的流轉發到其他 WebRTC Peer 客戶端),那麼,如果我們要實現這樣一臺 SFU 伺服器,有哪些需要解決和處理的問題呢 ?

我們可以想像一下 Web 伺服器和直播伺服器的工作原理,瀏覽器/直播客戶端,要想完成與伺服器之間的數據交換,通常離不開如下幾個步驟:

1. 通過 DNS 解析,拿到伺服器的 IP 地址;然後通過 「約定」 的埠(如:80 或者 1935)連接到伺服器

2. 客戶端使用 「約定」 的信令協議(如:HTTP,RTMP),發送請求給伺服器,實現數據交換的準備工作3. 客戶端開始上行數據給伺服器,或者伺服器開始下發數據到客戶端,結束後,通過信令關閉連接4. 靜態的資源型的數據(文件、網頁),通常服務端是讀取磁碟上的數據拷貝一份給需要的客戶端5. 非靜態的實時數據(如:直播流),伺服器則通過在 「內存」 中拷貝並轉發給需要的客戶端

同樣,WebRTC 客戶端與 SFU 伺服器之間的交互,也是離不開這些步驟的,特別是 4/5,其實就是所謂的 one-to-many 能力。

2 信令和傳輸通道的建立

首先我們解決第一個問題,即 WebRTC 客戶端是如何跟 SFU 伺服器建立數據傳輸通道的 ?

如圖,我們先看看瀏覽器與 Web 伺服器的建聯過程:瀏覽器通過 DNS 解析 URL 中的域名,拿到 IP 後通過 80 埠連接上伺服器(後續的數據傳輸均復用這條 TCP 鏈路)。

WebRTC 其實也是類似的,但是與標準的 HTTP 服務或者 RTMP 直播服務相比,還是有些區別的,如下:

1. 信令和數據通道是 「分離」 的,信令目前沒有統一的實現方案,可以使用任何方案(如:HTTP、TCP 自定義協議、SIP 等等),但是數據並不走這條信令鏈路,而是走單獨的 UDP 埠

2. 數據通道使用的 UDP 協議,不像 TCP 有 「連接」 的概念,客戶端僅僅知道伺服器的 UDP 埠,但不 「連接」 是無法預判傳輸通道是否真的 OK(主要是部分 NAT 網關類型的限制,導致並不是所有 UDP 傳輸都能通),因此需要藉助一些框架和協議來判斷 UDP 通道的可用性(即 ICE 協議)

上述內容分析完了,我們就可以看看如何實現 SFU 的信令和傳輸通道了:

1. 實現 HTTP Web Server 服務(或者 SIP 或者基於 TCP 自定義協議),用於提供 「信令」 的支持(如:推流命令、拉流命令等)

2. 通過 libnice 庫或者自己 coding 的方式,實現 ICE 協議,用於提供 UDP 「數據通道」 的檢測和建聯3. 實現 UDP 數據監聽和發送,用於接收客戶端的數據,轉發其他客戶端的數據

3 需要實現哪些 「信令」 ?

對於 HTTP 協議,實現的 「信令」 包括:GET,POST,DELETE 等等,定義了瀏覽器期望進行的行為。同理,對於 SFU,我們也要定義一系列必要的信令,以約定客戶端和伺服器對應的行為,那具體有哪些呢 ?

其實 WebRTC 客戶端,與 SFU 伺服器需要協商的事情,無外乎就是如下幾點:

1. ICE 建聯:交換 ICE 信息(用戶名、密碼、IP 地址、UDP 埠等)

2. 發布流/取消發布流:客戶端通知伺服器準備好接收數據

3. 訂閱流/取消訂閱流:客戶端通知伺服器準備好轉發數據

因此,SFU 伺服器通過任意一種方式(HTTP/TCP 等),提供 ICE Connection,Publish,Subscribe 信令即可,SFU 在信令背後需要實現的邏輯分別如下:

1. ICE Connection:添加一路 UDP 通道

2. Publish:添加一個邏輯上的數據 Producer,通過 UDP 通道 recv 客戶端的數據,通知邏輯上的 Consumers3. Subscribe:添加一個邏輯上的數據 Consumer,收到 Producer 通知後,通過 UDP 通道 send 給客戶端

4 如何實現 one-to-many ?

這是 SFU 最核心的功能,其實也不是 WebRTC SFU 特有,如前面所述,凡是非靜態資源型的服務(數據實時產生實時消費)均需要在服務端實現 one-to-many,比較典型的例子就是 RTMP 直播流伺服器,需要將客戶端推流上來的數據,實時轉發給多個拉流的客戶端。

實現 one-to-many ,最重要的一點是需要把數據的生產者(Publisher)和數據的消費者(Subscriber)關聯起來,怎麼關聯呢 ?

WebRTC 傳輸的音視頻數據,實際上是封裝在 RTP 包裡面,RTP 包頭有個很重要的欄位,叫做 SSRC(同步源標識),就是這路流的唯一標識,如圖:

數據的生產者(Publisher)和數據的消費者(Subscriber)即可通過 SSRC 來關聯,實現 one-to-many 的核心代碼邏輯抽象如下:

即:當 SFU 接收到 Publisher 發送上來的數據後,輪詢一下所有的 Subscribers,如果 SSRC 匹配成功,則將數據轉發給這個客戶端。

5 數據傳輸協議

WebRTC 採用的是標準的 RTP/RTCP 協議進行數據的封包和網路狀態反饋,因此,SFU 伺服器也需要支持 RTP/RTCP 的封包和解包,從而能夠 「理解」 客戶端的 UDP 數據包的含義,如:提取出 SSRC 或者 timestamp 等必要的信息,也能及時地向客戶端反饋網路狀態(RTCP)。

關於 RTP/RTCP 傳輸協議,已經發展多年,是比較成熟的多媒體傳輸協議了,也有很多不錯的開源庫,這裡就不再贅述了。

6 小結

以上就是關於如何實現 SFU 伺服器最核心的知識點了,暫且就分享到這裡了,如有疑問的小夥伴歡迎來信 [email protected] 交流。另外,也歡迎大家關注我的新浪微博 @盧_俊 或者 微信公眾號@Jhuster 獲取最新的文章和資訊。

推薦閱讀:

相關文章