前面我們通過兩篇文章詳細介紹了 Medooze 的實現邏輯,我相信你現在已經對 Medooze 有了非常深刻的認知。通過對 Medooze 實現原理和架構的了解,我們可以知道 Medooze 支持的功能非常多,如錄制回放、推流、SFU 等,其中最主要的功能是 SFU 的實現。
實際上,我們要實現的多方音視頻會議就是由 SFU 來實現的,或者你可以認為 Medooz 實現的 SFU 就是一個最簡單的音視頻會議系統。Medooze 提供的 SFU Demo 的地址為:https://github.com/medooze/sfu 。
由于 SFU 是一個 Node.js 項目,所以在環境部署、源碼分析的時候,需要你有 JavaScript 基礎,也需要掌握 NPM 和 Node 的常用命令,比如 npm install 等。接下來,我們主要從以下幾方面向你介紹一下多方音視頻會議(SFU)項目:
* 搭建多方音視頻會議系統,讓你感受一下多方通信;
* 分析多方音視頻會議實現的架構;
* 分析 Medooze API 的具體使用;
* 分析多方音視頻會議的入會流程。
## 多方音視頻會議環境的搭建
多方音視頻會議(SFU)項目是純 Node.js 實現的,可以說在目前所有的操作系統上都可以運行。然而,由于該系統依賴 medooze-media-server 項目,而該項目僅支持 macOS X 和 Linux 兩種操作系統,所以 SFU 項目也只能在 macOS X 和 Linux 這兩種操作系統下運行了。
本文我們所搭建的這個 Demo 的實驗環境是在 Ubuntu 18.04 系統上,所以接下來的實驗步驟和實驗結論都是基于 Ubuntu 18.04 得出的,如果你在其他操作系統下搭建可能結果會略有不同,但具體步驟都是一模一樣的。
下面我們就來分析下這個環境搭建的具體步驟。
第一步,打開 Linux 控制終端,下載 SFU 代碼,命令如下:
```
git clone https://github.com/medooze/sfu.git
```
第二步,安裝 SFU 依賴的所有包:
```
cd sfu
npm install
```
第三步,在 sfu 目錄下生成自簽名證書文件:
```
openssl req -sha256 -days 3650 -newkey rsa:1024 -nodes -new -x509 -keyout server.key -out server.cert
```
第四步,在 sfu 目錄下啟動服務:
```
node index.js IP
```
上面命令中的 IP 是測試機器的 IP 地址,如果用內網測試,你可以將它設置為 127.0.0.1。如果你想用外網訪問,則可以指定你的云主機的外網 IP 地址。另外需要注意的是,該服務啟動后默認監聽的端口是 8084,**所以你在訪問該服務時要指定端口為 8084。**
通過上面的步驟,我們就將 Medooze 的 SFU 服務器搭建好了,下面我們來測試一下。
打開瀏覽器,在地址欄中輸入以下地址就可以打開 Medooze SFU Demo 的測試界面了:
```
https://IP:8084/index.html
```
在這個地址中需要特別注意的是,必須使用 HTTPS 協議,否則瀏覽器無法訪問本地音視頻設備。此外,這個地址中的 IP 指的是 SFU 服務的主機的 IP 地址,可以是內網地址,也可以是外網地址。
當該地址執行后,會彈出登錄界面,如下所示:
Medooze SFU 登錄界面圖這是一個多人視頻會議登錄界面。關于登錄界面,這里我們做一個簡單的說明。
* 界面中的 Room Id 和 Name 可以隨機生成,也可以自己指定。如果需要隨機生成,點擊后面的 RANDOM 按鈕即可。
* 幾個參會人若想要進入同一個房間,那么他們的 Room Id 必須相同。
* 在 Audio Device 列出了所有麥克風,可供參會人選擇。
* Audio Device 下面,有一個音量進度條,它可以測試麥克風是否正常工作。
* 一切準備就緒后,點擊右下角的“READY!”按鈕,就可以加入房間了。此時,在瀏覽器左下角顯示了自己的本地視頻。
* 由于這是基于瀏覽器的音視頻應用,具有很好的跨平臺性,因此你可選擇 PC 瀏覽器、手機瀏覽器一起測試。
上面我們就將 Medooze 的音視頻會議系統的 Demo 環境搭建好了,并且你可以進行多人的音視頻互動了。接下來我們就分析一下這個音視頻會議系統(SFU) Demo 是如何實現的吧。
## SFU 目錄結構
首先我們來分析看一下 Medooze 的 SFU Demo 的目錄結構,以及每個子目錄中代碼的作用,為我們后面的分析做好準備。
如上圖所示,在 SFU Demo 目錄下的文件非常少,我們來看一下每個文件或目錄的具體作用吧!

了解了 SFU Demo 的目錄結構及其作用后,我們再來看一下 SFU Demo 的架構是什么樣子。
### SFU 架構
下圖就是 Medooze SFU Demo 的架構圖,你可以參考下:

:-: SFU 架構圖
從圖中你可以看出,SFU 架構包括了瀏覽器客戶端和 SFU 服務端兩部分。
而 SFU 服務端又包括兩部分功能:**WWW 服務、WebSocket 信令及業務管理。**
接下來我們就來分析一下**服務端的處理邏輯。**
*****
Node.js 中的 Web 服務器(WWW)是在 index.js 文件中實現的。當我們執行node index.js IP命令時,該命令啟動了一個 HTTPS 服務,監聽的端口是 8084。當 HTTPS 啟動后,我們就可以從它的 WWW 服務獲取到客戶端代碼了。需要說明的是,HTTPS 服務依賴 server.key 和 server.crt 文件,這兩個文件需要我們提前生成好(生成證書的命令在上面 SFU 環境搭建一節已經向你做了說明)。
*****
至于 WebSocket 服務及業務管理部分,邏輯會稍微復雜些。服務端啟動 WebSocket 服務后,可以通過它來接收客戶端發來的信令,同時也可以通過它向客戶端發送通知消息;除了對信令的處理外,SFU 的業務邏輯也是在這部分實現的,如 Room 和 Participator 實例的創建、參會人進入 / 離開房間等。
*****
. 這里需要你注意的是,WebSocket 服務和業務管理的代碼也是在 index.js 中實現的。換句話說,在服務端的 index.js 中,實現了 WWW 服務、WebSocket 服務和 SFU 業務管理三塊邏輯,這樣“大雜燴”的代碼實現也只是在 Demo 中才能這么寫了。
*****
另外,index.js 的實現依賴了 websocket 和 transaction-manager 兩個包。
*****
..分析完服務端的處理邏輯后,接下來我們再來看看客戶端。瀏覽器首先從 Medooze SFU 的 WWW 服務上獲得 sfu.js 代碼,在瀏覽器上運行 sfu.js 就可以與 Medooze SFU 建立連接,并進行多方音視頻通信了。在 sfu.js 中,主要實現了房間的登錄、音頻設備選擇、采集音視頻數據、共享音視頻數據等功能。實際上,客戶端的實現邏輯就是使用咱們專欄第一模塊所介紹的知識,如 RTCPeerConnection 的建立、獲取音視頻流、媒體協商等。
*****
此外,Medooze SFU 還實現了幾個信令,如 join 用于加入會議、update 用于通知客戶端重新進行媒體協商等等,這幾個信令還是非常簡單的,不過卻非常重要。尤其是在閱讀 Medooze SFU 代碼時,可以按照信令的流轉來梳理 SFU 的代碼邏輯,這樣會大大提高你的工作效率。
以上就是 Medooze SFU 的基本架構,接下來我們看一下 SFU 的邏輯處理。
### SFU 邏輯處理
SFU 的實現很簡單。在服務端有兩個重要的類,即 Room 和 Participator。它們分別抽象了多人會議中的房間和參會人。為便于你理解,我畫了一個簡單類圖,如下所示:

:-: SFU 類圖
從圖中可以看到,index.js 中定義了一個全局對象 Rooms,保存多個房間(Room)實例。
**Room 類是對房間的抽象:**
* Endpoint 屬性是 Medooze Endpoint 類型;
* Participators 屬性是一個 Map 類型,保存本房間中所有 Participator 實例;*
該類還有兩個重要方法,createParticipator 用于創建參會人實例,getStreams 獲取所有參會人共享的媒體流。
**Participator 類是對參會人的抽象:**
* IncomingStreams 屬性是 Map 類型,保存自己共享的媒體流,元素屬性類型是 Medooze::IncomingStream;
* OutgoingStreams 屬性是 Map 類型,保存自己觀看的媒體流,元素屬性類型是 Medooze::OutgoingStream;
* transport 屬性是 Medooze::Transport 類型,表示 DTLS 傳輸;
* init() 方法,初始化 transport 實例等;
* publishStream() 方法用于發布自己共享的流,創建 IncomingStream;
* addStream() 方法用于觀看其他參會人共享的流,創建 OutgoingStream 實例,并且 attachTo 到共享人的 IncomingStream 中。
## 入會流程
這里我們可以結合客戶端和服務器的邏輯,分析一下參會人入會的大致流程,如下圖所示:

SFU 時序圖圖中深粉部分是客戶端實現,在 www/js/sfu.js 中;綠色部分是服務器實現,在 index.js 中;橘色是在 medooze 中實現。那具體的流程是怎樣的呢?
.
首先,瀏覽器從 Node.js 服務器獲取到 www/index.html 頁面及引用的 JavaScript 腳本,然后執行 connect 方法。在 connect 方法中使用 WebSocket 與 SFU 服務(index.js)建立連接。
.
服務端收到 WebSocket 連接請求后,從 URL 中解析到 roomid 參數。然后,根據該參數判斷房間是否在之前已經創建過。
.
如果該房間已創建,則什么都不做;
.
如果沒創建,則嘗試創建 Room 實例。
*****
對于客戶端來說,WebSocket 連接建立成功后,客戶端會發送 join 信令到服務端。而服務端收到 join 信令后,創建 Participator(參與人)實例,并且調用它的 init 方法進行初始化。
之后,Participator 獲取 Room 中所有已共享的媒體流,并調用它的 addStream 方法,這樣它就可以訂閱 Room 中所有的媒體流了。訂閱成功后,媒體流的數據就被源源不斷地發送給 Participator 對應的終端了。
上面的邏輯完成之后,服務端會通知客戶端 join 已經成功。此時,客戶端也可以共享它的音視頻流給服務器了。
.
此外,在客戶端將自己的媒體流推送給服務端之前,服務器會調用 publishStream 方法在服務端創建 IncomingStream 實例,這樣當客戶端的數據到達服務器端后,就可以將數據交給 IncomingStream 暫存起來,以備后面分發給其他終端。
.
通過上面的步驟,新加入的客戶端就可以看到會議中其他人分享的音視頻流了,同時還可以將自己的音視頻推送到服務器了。但此時,其他參會人還看不到新加入用戶的音視頻流,因為還有關鍵的一步沒有完成。
.
這**關鍵的一步**是,當新參會人一切準備就緒后,服務端要發送廣播消息(update) 通知所有已經在會中的其他人,有新用戶進來了,需要重新進行媒體協商。**只有重新媒體協商之后,其他參會人才能看到新入會人的音視頻流。
.
以上步驟就是一個新參會者加入會議的大致過程,同時也介紹了這個過程中其他已經在會中的參與人需要如何處理。小結本文我們首先介紹了通過 Medooze SFU 如何搭建最簡單的音視頻會議系統,該系統搭建好后就可以進行多人音視頻互動的“實驗”了。
.
然后,我們對 Medooze 的 SFU 項目的邏輯做了詳細介紹,分析了 Medooze SFU 的基本架構、邏輯處理、入會流程等。相信通過本文的學習,你對 Medooze 的使用和實現原理都會有更深刻的認識。
當然,如果你要想在自己的產品中更好地應用 Medooze,還需要掌握更多的細節,只有這樣才能做到心中有數。比如:如何能夠承擔百萬用戶級的負載?如何為不同的的地區、不同的運營商的用戶提供優質的服務?這些都是你需要進一步研究和學習的。
## 思考時間
今天留給你的思考題:一個多人存在的會議中,又有一個人加入進來,其他參會人是如何看到后加入人的視頻流的呢?你清楚其中的原理嗎?歡迎在留言區與我分享你的想法,也歡迎你在留言區記錄你的思考過程。
感謝閱讀,如果你覺得這篇文章對你有幫助的話,也歡迎把它分享給更多的朋友。
- 序言
- 編解碼
- H264
- HEVC碼流解析
- H264編碼原理
- 多媒體封裝
- MP4
- 學好 MP4,讓直播更給力
- AAC
- FLV
- 流媒體協議
- RTSP
- RTCP
- RTP
- H265 RTP封包筆記
- SDP
- RTMP
- RTMP URL
- rtmp url基礎
- webrtc
- 編譯
- 最簡單的編譯webrtc方案
- Webrtc音視頻會議之Webrtc“不求甚解”
- Webrtc音視頻會議之Mesh/MCU/SFU三種架構
- 音頻傳輸之Jitter Buffer設計與實現
- Janus
- Webrtc音視頻會議之Janus編譯
- Webrtc音視頻會議之Janus源碼架構設計
- webrtc服務器-janus房間管理
- 源碼分析
- WebRTC視頻JitterBuffer詳解
- 走讀Webrtc 中的視頻JitterBuffer(一)
- 走讀webrtc 中的視頻JitterBuffer(二)
- webrtc視頻幀率控制算法機制
- 目標碼率丟幀-1
- 目標幀率丟幀-2
- 29 如何使用Medooze 實現多方視頻會議
- FFmpeg
- FFmpeg編譯
- Window10下編譯最新版FFmpeg的方法步驟
- FFMPEG靜態庫編譯
- ffmpeg實現畫中畫
- FFmpeg推流器
- ffmpeg-aac
- OpenCV
- OpenCV學習筆記——視頻的邊緣檢測
- 圖像特征點匹配(視頻質量診斷、畫面抖動檢測)
- 圖像質量診斷