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

webrtc (Web Real-Time Communications) 是一個實時通訊技術,也是實時音視頻技術的標準和框架。
大白話講,webrtc是一個集大成的實時音視頻技術集,包含了各種客戶端api、音視頻編/解碼lib、流媒體傳輸協議、回聲消除、安全傳輸等。
對於開發者來說可以藉助webrtc非常方便的實現低延時視頻通話能力。
現在主流的直播系統、會議系統基本都是基於webrtc來實現。

一、webrtc 三種架構

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

webrtc_arch.png

【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

mesh_p2p.png

由於是私有證書問題,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數據轉發。

5Figure41.png

【SDP】
sdp是會話描述協議。
是媒體協商時使用,用於將本地支持的媒體(編解碼等)信息、candidate(連接候選者)信息打包發送到信令服務器。
sdp的交換是通過中間服務器(信令服務器)來完成的。

【ICE】
ICE是一個不斷嘗試連接的協議,不同的網絡情況下ICE大概會嘗試如下幾種方式來建立通訊通道。
host(peers都在內網)、 srflx(nat穿透)、prflx(nat穿透-Full Cone)、relay(中繼)

【服務端】
在整個連接生命週期中都是需要服務端參與。參與webrtc協作的服務端大概分爲這幾種類型。
stun/turn服務器(p2p穿透)、信令服務器、媒體服務器(媒體信息處理)、業務服務器(可選)

整體流程大致如下。

webrtc_connection.png

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

【部署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_ok.png

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

【部署OWT】

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

owt_empty_candidate.png

我們直接使用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

demo_sfu.png

OWT還配有管理後臺用於控制媒體服務器的相關參數。
owt_room.png
owt_room_detail.png

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

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

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