WebRTC 端对端连接的基本流程解析。
A:客户端 A,发起端
B:客户端 B,被呼叫端
Signal:信令服务器
sturn/turn:STUN/TURN 服务器
传输步骤:媒体协商、链路连接、媒体数据传输。
首先 A、B 需要和信令服务器建立连接。
发送端 A 创建一个 RTCPeerConnection。
1
pc1 = new RTCPeerConnection();
发送端 A 拿到本地的流 Add Stream 到连接中去。
1
2
3localStream.getTracks().forEach((track) => {
pc1.addTrack(track, localStream);
});发送端 A 创建一个 Offer,并设置到 setLocalDescription 中,setLocalDescription 调用后会向 sturn/turn 服务器发送一个 bind request,这个时候就开始收集所有能和对方连接的候选者了。
1
2
3
4
5
6
7
8
9
10
11
12
13var offerOptions = {
offerToReceiveVideo: 1,
offerToReceiveAudio: 0,
};
pc1.createOffer(offerOptions)
.then(getOffer)
.catch(handelCreateOfferError);
function getOffer(desc) {
pc1.setLocalDescription(desc);
// 发送 SDP Offer 给信令服务器:send desc.sdp;
// 信令服务器会把这个 SDP Offer 发给能连接的接收端 B
}接收端 B 收到信令服务器发来的 A 的 SDP Offer 后,创建一个 PeerConnection,并 setRemoteDescription 将收到的 sdp 设置进去。
1
2pc2 = new RTCPeerConnection();
pc2.setRemoteDescription(desc);然后接收端 B 需要给一个应答,创建一个 Answer 并设置到 B 的 LocalDescription,这个时候会向 sturn/turn 服务发送一个 bind request,然后接收端 B 再将自己的 SDP Answer 发送给 Signal 服务器。
1
2
3
4
5
6
7
8
9pc2.setLocalDescription(desc);
pc2.createAnswer()
.then(getAnswer)
.catch(handleAnsError);
function getAnswer(desc) {
pc2.setLocalDescription(desc);
// 发送 pc2 的 sdp answer 给 signal 服务器
}Signal 服务器收到接收端 B 的 SDP Answer 后会将这个 answer 发给端 A,端 A 收到 B 的 answer 后续设置 RemoteDescription。至此,A 和 B 的媒体协商算是完成了。
1
2// recieve SDP Answer from Signal Server
pc1.setRemoteDescription(desc);发送端 A 会收到一个 sturn/turn 服务器发来的 Candidate,这个 Candidate 就是服务器收集的能和对方建立连接的候选者了。然后 A 将这个 Candidate 发送给 Signal 服务器,通过信令服务器转给 B 端,B 收到之后续将这个 Candidate 添加进去。
同样的道理,当 B 端收到 sturn/turn 服务器发来的 Candidate,会将这个 Candidate 通过 Signal 转发给 A,A 将收到的 B 的候选者列表 Add 进去。
1
2
3
4
5
6
7pc1.onicecandidate = (e) => {
pc2.addIceCandidate(e.candidate);
};
pc2.onicecandidate = (e) => {
pc1.addIceCandidate(e.candidate);
};此时就 A、B 双方都已经收到了 Candidate,底层开始 pair,做排序和连接检测。
当找到最优的一个线路后,A、B 开始通信。
A 将数据流发送给 B,B 收到后将数据 AddStream,才能把音视频数据向上抛出进行渲染。
1
2
3
4
5pc2.ontrack = getRemoteStream;
function getRemoteStream(e) {
remoteVideo.srcObject = e.streams[0];
}
EOF