WebRTC 開發實踐:如何實現 SFU 服務器

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

1 什麼是 SFU ?

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

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

1.jpg

如圖所示,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 服務器建立數據傳輸通道的 ?

2.jpg

如圖,我們先看看瀏覽器與 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 客戶端的數據,通知邏輯上的 Consumers

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


4 如何實現 one-to-many ?

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

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

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

3.jpg

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

4.jpg

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

5 數據傳輸協議

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

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

6 小結

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

weixin_jhuster.jpg


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章