文章目录
text
2024-9-19
梳理信令处理的函数嵌套及实现思路
一对一视频通话
New_peer信令处理
cpp
ZeroRTCEngine.prototype.onMessage = function (event) {
console.log("onMessage: " + event.data);
var jsonMsg = null;
try {
jsonMsg = JSON.parse(event.data);//解序列化
} catch(e) {
console.warn("onMessage parse Json failed:" + e);
return;
}
switch (jsonMsg.cmd) {//解析信令(图中橙色框)
case SIGNAL_TYPE_NEW_PEER:
handleRemoteNewPeer(jsonMsg);
break;
case SIGNAL_TYPE_RESP_JOIN:
handleResponseJoin(jsonMsg);
break;
case SIGNAL_TYPE_PEER_LEAVE:
handleRemotePeerLeave(jsonMsg);
break;
case SIGNAL_TYPE_OFFER:
handleRemoteOffer(jsonMsg);
break;
case SIGNAL_TYPE_ANSWER:
handleRemoteAnswer(jsonMsg);
break;
case SIGNAL_TYPE_CANDIDATE:
handleRemoteCandidate(jsonMsg);
break;
}
}
function handleRemoteNewPeer(message) {
console.info("handleRemoteNewPeer, remoteUid: " + message.remoteUid);
remoteUserId = message.remoteUid;
doOffer();
}
function doOffer() {
// 创建RTCPeerConnection
if (pc == null) {
createPeerConnection();
}
pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError);
}
function createPeerConnection() {
pc = new RTCPeerConnection(null);
pc.onicecandidate = handleIceCandidate;
pc.ontrack = handleRemoteStreamAdd;
localStream.getTracks().forEach((track) => pc.addTrack(track, localStream));
function handleIceCandidate(event) {
console.info("handleIceCandidate");
if (event.candidate) {
var jsonMsg = {
'cmd': 'candidate',
'roomId': roomId,
'uid': localUserId,
'remoteUid': remoteUserId,
'msg': JSON.stringify(event.candidate)
};
var message = JSON.stringify(jsonMsg);
zeroRTCEngine.sendMessage(message);
// console.info("handleIceCandidate message: " + message);
console.info("send candidate message");
} else {
console.warn("End of candidates");
}
}
function handleRemoteStreamAdd(event) {
console.info("handleRemoteStreamAdd");
remoteStream = event.streams[0];
remoteVideo.srcObject = remoteStream;
}
function createOfferAndSendMessage(session) {
pc.setLocalDescription(session)
.then(function () {
var jsonMsg = {
'cmd': 'offer',
'roomId': roomId,
'uid': localUserId,
'remoteUid': remoteUserId,
'msg': JSON.stringify(session)
};
var message = JSON.stringify(jsonMsg);
zeroRTCEngine.sendMessage(message);
// console.info("send offer message: " + message);
console.info("send offer message");
})
.catch(function (error) {
console.error("offer setLocalDescription failed: " + error);
});
}
function handleCreateOfferError(error) {
console.error("handleCreateOfferError: " + error);
}
当执行信令 handleRemoteNewPeer
时,表示有一个新的远程对等体加入了房间,并且这个信息已经通过 WebSocket 接收并被解析为 JSON 对象 message
。
handleRemoteNewPeer
信令的详细执行流程:
-
记录日志:
console.info("handleRemoteNewPeer, remoteUid: " + message.remoteUid);
:在控制台输出一条信息,表明一个新的远程对等体已经加入,同时显示这个对等体的用户 ID。
-
保存远程用户 ID:
remoteUserId = message.remoteUid;
:将消息中的remoteUid
保存到全局变量remoteUserId
中。这个 ID 用于标识远程对等体,后续的 WebRTC 操作(如发送 offer)会使用这个 ID 来指定目标用户。
-
创建 WebRTC 连接:
doOffer();
:调用doOffer
函数,开始创建 WebRTC 连接并发送 offer。这个函数的作用是初始化RTCPeerConnection
对象(如果尚未创建),并创建一个 offer 来开始协商过程。
-
创建
RTCPeerConnection
对象:- 如果
pc
(RTCPeerConnection
对象)为null
,则调用createPeerConnection
函数来创建一个新的RTCPeerConnection
对象。这个对象负责管理 WebRTC 连接的协商和维护。
- 如果
-
设置 ICE 候选者和远程媒体流事件处理:
- 在
createPeerConnection
函数中,为RTCPeerConnection
对象设置了onicecandidate
和ontrack
事件处理函数。onicecandidate
处理本地 ICE 候选者的生成,而ontrack
处理远程媒体流的添加。
在 WebRTC 中,ICE(Interactive Connectivity Establishment)协议用于在对等体之间建立连接。这个过程需要通过一系列的步骤来发现并验证不同的网络路径,以找到可以用于媒体传输的最佳路径。以下是设置 ICE 候选者和远程媒体流事件处理的详细解析:
- 在
-
ICE 候选者(ICE Candidates):
- ICE 候选者是网络接口的描述,可以用来在对等体之间传输媒体流。每个候选者包括一个网络地址和一个端口号,以及可能的其他元数据,如传输协议类型(如 UDP 或 TCP)。
-
收集 ICE 候选者:
RTCPeerConnection
对象在创建时会开始 ICE 候选者的收集过程。这个过程是异步的,会在不同的时间点生成多个候选者。
-
设置
onicecandidate
事件处理函数:pc.onicecandidate = handleIceCandidate;
:为RTCPeerConnection
对象设置onicecandidate
事件处理函数。每当有新的 ICE 候选者生成时,这个事件处理函数会被调用。
-
处理 ICE 候选者事件:
handleIceCandidate(event)
:这个函数会在onicecandidate
事件触发时执行。它检查事件对象event
是否包含candidate
属性。
-
发送 ICE 候选者:
- 如果
event.candidate
存在,表示有一个可用的 ICE 候选者。这个候选者会被序列化为 JSON 格式,并封装在一个信令消息中,然后通过 WebSocket 发送给远程对等体。 var jsonMsg = { 'cmd': 'candidate', ... };
:创建一个包含信令类型、房间 ID、用户 ID 和候选者数据的 JSON 对象。var message = JSON.stringify(jsonMsg);
:将 JSON 对象转换为字符串,以便通过网络发送。zeroRTCEngine.sendMessage(message);
:调用ZeroRTCEngine
实例的sendMessage
方法,将消息发送给远程对等体。
- 如果
-
结束候选者收集:
- 如果
event.candidate
不存在,表示候选者收集过程已经结束。这时,可以发送一个特殊的信令消息,告知远程对等体没有更多的候选者。
- 如果
-
远程媒体流事件处理:
pc.ontrack = handleRemoteStreamAdd;
:为RTCPeerConnection
对象设置ontrack
事件处理函数。每当远程媒体流的第一帧数据到达时,这个事件处理函数会被调用。
-
处理远程媒体流:
handleRemoteStreamAdd(event)
:这个函数会在ontrack
事件触发时执行。它获取事件对象event
中的远程媒体流,并将其绑定到远程视频元素上,以便在网页上显示远程视频。
-
显示远程视频:
remoteVideo.srcObject = remoteStream;
:将远程媒体流remoteStream
绑定到远程视频元素remoteVideo
上。这样,远程视频流就会在视频元素中播放。
-
添加本地媒体流到
RTCPeerConnection
:localStream.getTracks().forEach((track) => pc.addTrack(track, localStream));
:将本地媒体流localStream
的轨道添加到RTCPeerConnection
对象中。这样,当连接建立后,本地媒体流可以被远程对等体接收。
-
创建并发送 offer:
pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError);
:调用RTCPeerConnection
对象的createOffer
方法来创建一个新的 offer。创建成功后,通过createOfferAndSendMessage
函数将 offer 封装为 JSON 消息并通过 WebSocket 发送给远程对等体。
-
处理创建 offer 错误:
- 如果在创建 offer 过程中出现错误,
catch
块会捕获这个错误,并调用handleCreateOfferError
函数来记录错误信息。
- 如果在创建 offer 过程中出现错误,
总结来说,当执行 handleRemoteNewPeer
信令时,应用程序会为新加入的远程对等体创建一个 WebRTC 连接,并开始协商过程,以便进行音频和视频通信。这个过程包括创建 RTCPeerConnection
对象、添加本地媒体流、创建 offer 并发送给远程对等体。