一文帶你瞭解webrtc基本原理(動手實現1v1視訊通話)

語言: CN / TW / HK

webrtc (Web Real-Time Communications) 是一個實時通訊技術,也是實時音視訊技術的標準和框架。

大白話講,webrtc是一個集大成的實時音視訊技術集,包含了各種客戶端api、音視訊編/解碼lib、流媒體傳輸協議、回聲消除、安全傳輸等。

對於開發者來說可以藉助webrtc非常方便的實現低延時視訊通話能力。

現在主流的直播系統、會議系統基本都是基於webrtc來實現。

一、webrtc 三種架構

我們先大概瞭解下webrtc的幾種架構及各自適用場景。

【Mesh】

Mesh架構,需要所有參與連線的peer建立與所有其他peer的媒體連線。

該架構需要n-1個上下行,以此帶來的頻寬消耗(流量)、編/解碼消耗(手機效能)成線性增長。

該架構只能適用3-4個人的小型會議場景。

【MCU】

所有本房間的peer將本地媒體流推到遠端媒體伺服器,由媒體伺服器進行混流,然後再推到所有連線的peer端。

該架構的優點就是隻需要1路上下行,隨著peer人數不斷增加,依然不會對使用者造成頻寬、手機效能影響。

該架構將壓力轉嫁到服務端,由專用媒體伺服器來完成混流,轉推等功能。

【SFU】

相對於MCU來說SFU只做轉發,媒體伺服器壓力有限。與mesh架構相比,只需要n-1個下行,1個上行。

在大規模的場合該架構具有伸縮性。

二、實現 1v1 視訊通話

廢話不多說,動手實踐下。

(麻雀雖小,五臟俱全。通過實現1v1的功能,來整體瞭解下webrtc協議的原理。)

github: http://github.com/Plen-wang/webrtc-demo-1v1

由於是私有證書問題,chrome會有安全提示。(demo地址暫時還能用 -_- )

有兩個方法可以試下。

第一個方法,手動設定一個類似不安全白名單列表,然後重啟瀏覽器。

chrome://flags/#unsafely-treat-insecure-origin-as-secure

如果不行,我們試下第二個方法肯定可以。

點選空白頁輸入 thisisunsafe 字元。

動手之前,我們先簡單瞭解下webrtc的連線的大致流程和涉及的相關技術點。

【WebRTC P2P】

【NAT穿透】

peer基本都在內網,需要通過nat穿透技術來與peer建立連線。

根據nat的拓撲情況大致分為如下幾種:完全錐形、IP錐形、埠錐形、對稱形。

stun\turn協議:stun協議用來拿到peer公網ip,turn用來做relay資料轉發。

【SDP】

sdp是會話描述協議。

是媒體協商時使用,用於將本地支援的媒體(編解碼等)資訊、candidate(連線候選者)資訊打包傳送到信令伺服器。

sdp的交換是通過中間伺服器(信令伺服器)來完成的。

【ICE】

ICE是一個不斷嘗試連線的協議,不同的網路情況下ICE大概會嘗試如下幾種方式來建立通訊通道。

host(peers都在內網)、 srflx(nat穿透)、prflx(nat穿透-Full Cone)、relay(中繼)

【服務端】

在整個連線生命週期中都是需要服務端參與。參與webrtc協作的服務端大概分為這幾種型別。

stun/turn伺服器(p2p穿透)、信令伺服器、媒體伺服器(媒體資訊處理)、業務伺服器(可選)

整體流程大致如下。

(上述技術點較多,感興趣可以自行查詢相關資料)

【部署STUN\TURN伺服器】

為了支援1v1公網訪問,我們需要搭建一個stun/turn伺服器。

這裡我們使用 Coturn 開源元件,coturn的映象有很多,可自行選擇。

(注意準備coturn配置檔案時,記得設定使用者名稱和密碼。)

docker run -d  --rm --name turn-server --network=host   \
               -v ${pwd}/turnserver.conf:/etc/coturn/turnserver.conf \
           instrumentisto/coturn

部署好之後可以通過ICE測試工具測試下

http://webrtc.github.io/samples/src/content/peerconnection/trickle-ice
stun:1.15.11.173:3478?transport=tcp
    turn:1.15.11.173:3478?transport=tcp:user:pwd

如果正常返回了ICE嘗試的連線型別,說明部署沒有問題。

【實現信令伺服器與客戶端程式碼】

我們採用golang來實現一個簡單的信令伺服器,使用開源元件go-socket。

同時還需要實現一個web客戶端。

demo程式碼就不貼到文章裡了,放在github上。整體程式碼比較簡單,感興趣可以看下。

[email protected]:Plen-wang/webrtc-demo-1v1.git

【部署信令伺服器】

當在本地debug的差不多了,我們把信令伺服器打個映象發到雲主機上。

(如果部署本demo,可以直接使用此映象。)

docker push wangqingpei/rtc-signal-server:latest
    docker run --name signal-server -d -p:8080:8080 wangqingpei/rtc-signal-server

【部署web伺服器】

部署好信令伺服器之後,我們把靜態檔案放到web伺服器裡,直接使用nginx映象部署非常簡單。

docker run -d -p 80:80 -p 443:443  --rm --name webrtc-nginx   \
    -v /data/rtc-nginx.conf:/etc/nginx/nginx.conf  \
    -v /data/pem/server.key:/etc/nginx/server.key  \
    -v /data/pem/server.pem:/etc/nginx/server.pem  \
    -v /data/rtc-static-file:/usr/share/nginx/html  nginx

部署前,記得修改js裡的stun伺服器地址。

//建立RTCPeerConnection物件
function createRTCPeerConnection() {
    try {
	    
	    const configuration = {'iceServers': [{'urls': 'stun:1.15.11.173:3478?transport=tcp'}]}
	    
        rtcConnObject = new RTCPeerConnection(configuration);
        rtcConnObject.onicecandidate = handleRtcICECandidate;//ice 互動
        rtcConnObject.onaddstream = handleRtcAddStream;//遠端stream加入
        rtcConnObject.onremovestream = handleRtcRemoveStream;//遠端stream移除
        rtcConnObject.addStream(localStreamObject);//新增本地stream
        console.log("create local RTCPeerConnection object ok.");
    } catch (e) {
        console.error("create RTCPeerConnection err.", e);
    }
}

兩邊peer就可以藉助stun伺服器拿到公網ip實現nat穿透。

三、實現MCU/SFU 多人通話

MCU/SFU架構需要 專用媒體伺服器 參與。

【媒體伺服器選擇】

專用媒體伺服器有 OWT(open webrtc toolkit)、TWS(Kurento Media Server)等重量級的開源產品。

這兩款開源框架都支援MCU、SFU架構功能。

我們選擇OWT搗鼓下。

先看下部署起來的效果,預設MCU模式。

紅框部分是服務端混流之後的效果。

【部署OWT】

注意,owt-server-4.3映象與最新版chrome有相容性問題,會報錯 Empty candidate 錯誤。

我們直接使用5.0的映象部署。

docker run -d --name owt-demo --network host lmshao/owt-server

由於該映象是使用預設配置打的,啟動後手動進入容器修改下相關配置,換成你雲主機的公網ip,然後重啟服務。

配置檔案路徑

vi dist/webrtc_agent/agent.toml

配置項,這裡修改成你的公網ip

network_interfaces = [{name = "eth0", replaced_ip_address = "1.116.175.232"}]  # default: []

stun伺服器可選

stunport = 3478 #default: 0
stunserver = "1.15.11.173" #default: ""

然後修改下portal.toml檔案,檔案路徑。

vi dist/portal/portal.toml

修改成公網ip

ip_address = "1.116.175.232" #default: ""

重啟下相關服務

./bin.restart-all.sh

注意啟動日誌裡有一個id、key,這是用來進入管理頁面用的。(沒錯,owt提供了後臺管理頁面 -_-)

superServiceId: xxx
superServiceKey: xxx

sampleServiceId: xxx
sampleServiceKey: xxx

預設3004埠下是mcu模式,連線的人多了就會明顯示卡頓(看伺服器配置)。

我們切到SFU模式試下流暢度和伺服器負載情況。

通過 ?forward=true 引數控制

http://1.116.175.232:3004/?forward=true

OWT還配有管理後臺用於控制媒體伺服器的相關引數。

OWT還是比較強大的,有興趣可以研究研究。

參考資料:

github.com/googollee/go-socket.io

《WebRTC技術詳解:從0到1構建多人視訊會議系統》

《WebRTC音視訊實時互動技術:原理、實戰與原始碼分析》

《FFmpeg 音視訊開發基礎與實戰》