文章目录
- 一对一视频通话
- New_peer信令处理
2024-9-19
梳理信令处理的函数嵌套及实现思路
一对一视频通话
New_peer信令处理
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() {// 创建RTCPeerConnectionif (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 并发送给远程对等体。