開始正文之前,先思考1個問題:2個處於不同網絡環境的(具備攝像頭/麥克風多媒體設備的)瀏覽器,要實現點對點的實時視頻/語音通訊,難點在哪?
至少得先搞定下面2個問題:
1、彼此要了解對方支持的媒體格式、支持的最大分辨率等媒體信息
比如:peerA端可支持MPEG-1/2、H264多種編碼格式,而peerB端支持MPEG-4、H264,要保證二端都正確的編解碼,最簡單的辦法就是取它們的交集H264
就象2個不同國家的人交流,1個只會講英文、中文,另1個只會講德語、英文,他倆肯定要能相互正常溝通,肯定會用雙方都懂的英文來交流一樣。
注:有一個專門的協議 ,稱為Session Description Protocol (SDP),可用於描述上述這類信息,在webrtc中,參與視頻通訊的雙方必須先交換SDP信息,這樣雙方才能知根知底,而交換SDP的過程,也稱為"媒體協商"。
2、彼此要了解對方的網絡情況,這樣才有可能找到一條相互通訊的鏈路
類似的道理,在復雜的網絡環境中,要建立二個端的連接,得有一條雙方都能訪問的鏈路。
如上圖,peerA端具體有公網ip以及192網段的內網環境,而peerB沒有公網,只有192、198二個內網地址,從圖中可知,它倆可以使用公用的192網段來通訊。webrtc通訊過程中,這些網絡相關的信息,也得相互交換,找出共同的交集,這個過程也稱為“網絡協商”。
順着這個思路再琢磨一下,剛開始前,這2個端還沒建立連接,既然連都沒連上,又如何交換“媒體信息”、“網絡信息”?
這時候就該所謂的信令服務器signal server出場了:
如上圖,2個瀏覽器端的上層,可以抽象出一層信令服務器(可以是1台或多台,看實際應用的情況,如果2端的瀏覽器都能訪問某個公共的網絡環境,比如公網,可以讓它們都連到這台公用的信令服務器上;如果沒有公共的網絡環境,可以在2端各搭一組服務器,即signal serverA、signal serverB,但是這二組信令服務器之間要能互通),借助信令服務器,就可以實現上面提到的SDP信息及網絡信息交換。
交換SDP的過程,大致如上圖:
1、Amy(1個假想的人名),把自身的SDP信息,通過setLocalDescription方法保存起來,然后通過offer方法,發給信令服務器。
2、信息服務器把Amy的SDP向前傳遞到另1端的Bob(另1個假想的人名),Bob會先調用setRemoteDescripition把Amy的SDP保存下來。
3、然后Bob調用setLocalDescription方法保存自己的SDP,然后再通過answer方法,把自己的SDP通過信令服務器發給Amy
4、Amy收到Bob的SDP后,調用setRemoteDescription保存起來,這樣雙方就完成了SDP交換,然后找出其中的交集,如果能達成一致,就可以建立p2p連接,開始通訊了。
但是現實往往是殘酷的,在中國的網絡環境中,據一些統計數據顯示,至少1半的網絡是無法直接穿透打通的(我個人認為根本原因是:IP4地址資源在互聯網發展早期絕大多數都被國外占用了,輪到中國這些發展中國家使用時IP地址嚴重不足,所以大多數電腦都不具備公網ip,只能通過路由器、交換機做NAT轉換,而相當一部分NAT是對稱型的,基本上沒法空透),這種情況下只能借助上一節講到的turn服務器中轉。
另外,在視頻對話框中,通常會有房間(或群)的概念,用於做一些業務上的隔離,這部分邏輯也是在signal server中實現的,綜合考慮peer端、信令服務器、stun/turn服務器后,整個1對1實時視頻通訊的時序圖如下:
主要過程如下:
1、雙方先調用getUserMedia打開本機攝像頭
2、向信令服務器發送加入房間apply_join請求
3、信令服務器通知本人加入成功(joined),同時向其它人廣播加入消息(other_joined)
4、二端開始創建peerConnection連接
5、peerB端創建offer,同時將SDP保存到本機(setLocalDescription),並通過信令服務器傳遞到peerA
6、peerB在setLocalDescription后,會異步觸發“候選網絡鏈路”收集,大致是通過Stun確定自己所有的NAT映射出口,如果Stun返回NAT是“對稱型”的,基本上就沒法穿透了,會再通過Turn拿到中繼reply地址,並通過信令服務器,將網絡候選鏈路信息發到peerA(即:開始網絡協商)
7、peerA收到的peerB的SDP后,開始回應(createAnswer),仍然通過信令服務器,將SDP發送到peerB
8、同時peerA也會開始網絡候選鏈路的收集,並將自己的網絡信息,通過信令服務器,發到peerB(即:網絡協商)
這樣peerA,peerB就相互交換了媒體信息及網絡信息,如果能達到一致(即:找到交集),就可以開始通訊了
基於以上原理,做了一個demo示例程序,見:https://github.com/yjmyzz/webrtc-samples
參考資料:
https://rtcdeveloper.com/t/topic/13742
https://www.html5rocks.com/en/tutorials/webrtc/basics/
https://hpbn.co/webrtc/#standards-and-development-of-webrtc
https://blog.csdn.net/momo0853/article/details/85157775
https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Connectivity
https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setLocalDescription