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

2022-08-07 06:01:34

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:https://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測試工具測試下
https://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 引數控制

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

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

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

參考資料:
github.com/googollee/go-socket.io
《WebRTC技術詳解:從0到1構建多人視訊會議系統》
《WebRTC音視訊實時互動技術:原理、實戰與原始碼分析》
《FFmpeg 音視訊開發基礎與實戰》