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); })
