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