RTCPeerConnection API
是每個瀏覽器之間點對點連接的核心,RTCPeerConnection
是WebRTC
組件,用於處理對等體之間流數據的穩定和有效通信。
RTCPeerConnection
可以保護Web開發人員免受潛伏在其中的無數復雜性的影響。WebRTC
使用的編解碼器和協議可以進行大量工作,即使在不可靠的網絡上也可以進行實時通信:
- 丟包隱藏
- 回聲消除
- 帶寬適應性
- 動態抖動緩沖
- 自動增益控制
- 降噪和抑制
- 圖像’清潔’。
// 創建實例 let pc = RTCPeerConnection(serverConfig);
根據你是發起者還是被發起對象,在連接的每一邊會使用稍微不同的方式使用RtcPeerConnection
對象。
serverConfigconfig
配置參數中包含iceServers
參數。它是包含有關STUN
和TURN
服務器的信息的URL
對象數組,在查找ICE
候選時使用。可以在code.google.com
找到可用的公共STUN
服務器的列表。
現實中,無論你的應用如何見到那,webRTC
都需要服務器,因為:
- 通信用戶發現彼此並交換自己的“真實世界”的詳細信息;
webRTC
客戶端(對等方)交換網絡信息;peers
交換有關每天的數據,如視頻格式和分辨率webRTC
客戶端遍歷NAT
網關和防火牆
換句話說,WebRTC
需要四種類型的服務器端功能:
- 發現用戶並溝通。
- 使用
STUN
服務器連接用戶信號。 - 使用
NAT
/防火牆遍歷。 - 在對等通信失敗的情況下,使用中繼服務器。
ICE
是用於連接對等體的框架,例如兩個視頻聊天客戶端。最初,ICE
嘗試通過UDP
直接連接對等端,以盡可能低的延遲。在此過程中,STUN
服務器只有一個任務:使NAT
后面的對等體能夠找到其公共地址和端口。
下面是調用流程:
1.獲取本地媒體設備成功之后,創建一個新的RTCPeerConnection
對象,初始化將本地音視頻軌道加入到RTCPeerConnection
function createConn(stream) { localStream = stream // 顯示本地視頻流 localVideo.srcObject = stream; //谷歌公共stun服務器 let serverConfig = { "iceServers": [ { "urls": ["turn:192.168.1.133:3478"], "username": "webrtc", "credential": "webrtc" } ] }; // 呼叫者 let localPeer = new RTCPeerConnection(serverConfig) // 被呼叫者 let remotePeer = new RTCPeerConnection(serverConfig) // 設置媒體流監聽,將本地流添加到RTCPeerConnection對象 localStream.getTracks().forEach((track) => { localPeer.addTrack(track, localStream); }); localPeer.addStream(stream) }
2.注冊onicecandidate
處理程序,並監聽獲取自己的ICE
協商信息,它將任何ICE
候選發送給其他對等方
function createConn(stream) { ... // 當獲得到自己的公網地址后,發送給其它客戶端 localPeer.onicecandidate = function(event) { console.log('I got my icecandidate info') if (event.candidate) { console.log(event.candidate.candidate) } socket.emit('onicecandidate', event.candidate); } // 如果監測到本地媒體流連接到本地,將其綁定到一個video標簽上輸出 localPeer.ontrack = function(e) { // 因為媒體流是一個數組 if (remoteVideo.srcObject !== e.streams[0]) { remoteVideo.srcObject = e.streams[0]; console.log('received remote stream'); } }; }
3.提前注冊消息處理程序。信令服務器還應該有一個處理來自遠程計算機的消息處理程序。如果消息包含RTCSessionDescription
對象,則應該使用RTCSessionDescription()
方法將其添加到RTCPeerConnection
對象。如果消息包含RTCIceCandidate
對象,則應該使用addIceCandidate()
方法將其添加到RTCPeerConnection
對象。
消息處理程序會根據誰是呼叫方和被呼叫方被調用。
//呼叫方收到對方回復的SDP時調用的消息處理程序 localPeer.setRemoteDescription(new RTCSessionDescription(answer)); //被呼叫方收到對方發送的SOP時調用的消息處理程序 localPeer.addIceCandidate(new RTCIceCandidate(candidate));
4.撥通對方,發送自己的SDP
信息,開始提供/回答協商過程,這是呼叫者的流量不同於被呼叫者的唯一步驟。呼叫者使用createoffer( )
方法開始協商,並注冊一個收到RTCSessiondescription
對象的回調。然后這個回調應該使用setlocaldescription( )
將這個rtcsessiondescription
對象添加到rtcpeerconnection
對象中。最后,調用者應該使用信令服務器將這個rtcsessiondescription
發送到遠程計算機。另一方面,被呼叫者,在createanswer()
方法中注冊相同的回調。請注意,只有在從調用者收到通知后,才會啟動流。
//當本地開始撥打對方的時候,發送自己的SDP信息 const offerOptions = { offerToReceiveAudio: 1, offerToReceiveVideo: 1 }; function call() { console.log('Starting call'); try { console.log('localPeerConnection createOffer start'); const offer = await pc1.createOffer(offerOptions); console.log(offer); localPeer.setLocalDescription(offer) socket.emit('offer', offer); } catch (e) { console.log(`Failed to create session description: ${e.toString()}`); } } // 當本地收到對方的撥號通知時 收到對方的SDP信息,然后生成回復SDP信息 function handleOffer(offer, name) { connectedUser = name; console.log("I got offer: "); localPeer.setRemoteDescription(new RTCSessionDescription(offer)); //create an answer to an offer localPeer.createAnswer(function(answer) { localPeer.setLocalDescription(answer); console.log("I will reply a answer") send({ type: "answer", answer: answer }); }, function(error) { alert("Error when creating an answer"); }); }; // 當撥號方收到對方回復的SDP后,設置到連接中,調用消息處理程序 socket.on('answer', (desc) => { console.log("I got answer: ", desc.sdp); localPeerConnection.setRemoteDescription(desc); })