0


一对一WebRTC视频通话系列(四)——offer、answer、candidate信令实现

本篇博客主要讲解offer、answer、candidate信令实现,涵盖了媒体协商和网络协商相关实现。
本系列博客主要记录一对一WebRTC视频通话实现过程中的一些重点,代码全部进行了注释,便于理解WebRTC整体实现。


一对一WebRTC视频通话系列往期博客

一对一WebRTC视频通话系列(一)—— 创建页面并显示摄像头画面
一对一WebRTC视频通话系列(二)——websocket和join信令实现
一对一WebRTC视频通话系列(三)——leave和peer-leave信令实现


offer、answer、candidate信令实现

整体实现思路

整体实现思路(红色部分为客户端,蓝色为服务端):
(1)收到new­peer (handleRemoteNewPeer处理),作为发起者创建RTCPeerConnection,绑定事件响应函数,加入本地流;
(2)创建offer sdp,设置本地sdp,并将offer sdp发送到服务器;
(3)服务器收到offer sdp 转发给指定的remoteClient;
(4)接收者收到offer,也创建RTCPeerConnection,绑定事件响应函数,加入本地流;
(5)接收者设置远程sdp,并创建answer sdp,然后设置本地sdp并将answer sdp发送到服务器;
(6)服务器收到answer sdp 转发给指定的remoteClient;
(7)发起者收到answer sdp,则设置远程sdp;
(8)发起者和接收者都收到ontrack回调事件,获取到对方码流的对象句柄;
(9)发起者和接收者都开始请求打洞,通过onIceCandidate获取到打洞信息(candidate)并发送给对方
(10)如果P2P能成功则进行P2P通话,如果P2P不成功则进行中继转发通话。

1. 客户端

(1)创建RTCPeerConnection,绑定事件响应函数,加入本地流
handleRemoteNewPeer->doOffer->ceratePeerConnection()

functiondoOffer(){//创建RTCPeerConnection对象if(pc ==null)ceratePeerConnection();
    pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError);}
functionceratePeerConnection(){//创建RTCPeerConnection对象
    pc =newRTCPeerConnection(null);
    pc.onicecandidate = handleIceCandidate;
    pc.ontrack = handleRemoteStreamAdd;
    localStream.getTracks().forEach(track=>{
        pc.addTrack(track, localStream);});}

(2)创建offer sdp,设置本地sdp,并将offer sdp发送到服务器

handleRemoteNewPeer

->

doOffer

->

pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError)

;

functioncreateOfferAndSendMessage(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);//将json对象转换为字符串
        zeroRTCEngine.sendMessage(message);//设计方法:用实现方法而不是直接用变量
        console.info("send offer message: "+ message);}).catch(function(error){
        console.error('offer setLocalDiscription failed: '+ error.toString());});}

(4)接收者收到offer,也创建RTCPeerConnection,绑定事件响应函数,加入本地流

ZeroRTCEngine.prototype.onmessage()

解析收到信息。
当信令为

SIGNAL_TYPE_OFFER

时,调用

handleRemoteOffer()

进行处理。

functionhandleRemoteOffer(message){
        console.info("handleRemoteOffer");if(pc ==null){ceratePeerConnection();}var desc =JSON.parse(message.msg);
        pc.setRemoteDescription(desc);doAnswer();}

(5)接收者设置远程sdp,并创建answer sdp,然后设置本地sdp并将answer sdp发送到服务器;
在(4)完成后,调用

doAnswer()

函数实现。

functiondoAnswer(){
    pc.createAnswer().then(createAnswerAndSendMessage).catch(handleCreateAnswerError);}
functioncreateAnswerAndSendMessage(session){
    pc.setLocalDescription(session).then(function(){var jsonMsg ={'cmd':'answer','roomId': roomId,'uid': localUserId,'remoteUid':remoteUserId,'msg':JSON.stringify(session)};var message =JSON.stringify(jsonMsg);//将json对象转换为字符串
        zeroRTCEngine.sendMessage(message);//设计方法:用实现方法而不是直接用变量
        console.info("send answer message: "+ message);}).catch(function(error){
        console.error('answer setLocalDiscription failed: '+ error.toString());});}

(7)发起者收到answer sdp,则设置远程sdp;
ZeroRTCEngine.prototype.onmessage()解析收到信息。
当信令为

SIGNAL_TYPE_ANSWER

时,调用

handleRemoteAnswer()

进行处理。

functionhandleRemoteAnswer(message){
    console.info("handleRemoteAnswer");var desc =JSON.parse(message.msg);
    pc.setRemoteDescription(desc);}

(8)发起者和接收者都收到ontrack回调事件,获取到对方码流的对象句柄; ???

(9)发起者和接收者都开始请求打洞,通过onIceCandidate获取到打洞信息(candidate)并发送给对方

functioncreatePeerConnection(){
    pc =newRTCPeerConnection(null);
    pc.onicecandidate = handleIceCandidate;
    pc.ontrack = handleRemoteStreamAdd;

    localStream.getTracks().forEach((track)=> pc.addTrack(track, localStream));}
functionhandleIceCandidate(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("send candidate message");}else{
        console.warn("End of candidates");}}
functionhandleRemoteCandidate(message){
    console.info("handleRemoteCandidate");var candidate =JSON.parse(message.msg);
    pc.addIceCandidate(candidate).catch(e=>{
        console.error("addIceCandidate failed:"+ e.name);});}

在这里插入图片描述
在这里插入图片描述

2. 服务端

主要完成以下两点:
(3)服务器收到offer sdp 转发给指定的remoteClient;
(6)服务器收到answer sdp 转发给指定的remoteClient;
应从消息监听函数入手,完成对

offer

answer

candidate

这3种情况的处理。

// 监听客户端发送的消息
conn.on("text",function(str){
    console.info("Received msg:"+str);var jsonMsg =JSON.parse(str);switch(jsonMsg.cmd){caseSIGNAL_TYPE_JOIN:handleJoin(jsonMsg, conn);break;caseSIGNAL_TYPE_LEAVE:handleLeave(jsonMsg);break;caseSIGNAL_TYPE_OFFER://新添1handleOffer(jsonMsg);break;caseSIGNAL_TYPE_ANSWER://新添2handleAnswer(jsonMsg);break;caseSIGNAL_TYPE_CANDIDATE://新添3handleCandidate(jsonMsg);break;}});

首先完成

offer

信令处理函数:
当收到视频流 offer 消息时,它会提取房间ID、用户ID和远程用户ID,然后检查房间Map中是否存在该用户ID。如果存在用户ID,它会将消息发送给远程用户。
实现原理如下:

  1. 获取房间ID和用户ID。
  2. 获取房间Map。
  3. 检查用户ID是否存在于房间Map中。
  4. 如果远程用户存在,将消息发送给远程用户。
  5. 如果不存在,输出错误信息。
functionhandleOffer(message){// 获取房间ID和用户IDvar roomId = message.roomId;var uid = message.uid;var remoteUid = message.remoteUid;
    console.info("handleOffer uid:"+ uid +" send offer to remoteUid: "+ remoteUid);// 获取房间Mapvar roomMap = roomTableMap.get(roomId);if(roomMap ==null){
        console.error("roomId:"+ roomId +" is not exist");return;}if(roomMap.get(uid)==null){
        console.error("uid:"+ uid +" is not exist in roomId:"+ roomId);return;}var remoteClient = roomMap.get(remoteUid);if(remoteClient){var msg =JSON.stringify(message);
        remoteClient.conn.sendText(msg);}else{
        console.error("remoteUid:"+ remoteUid +" is not exist in roomId:"+ roomId);}}
answer

candidate

信令处理函数逻辑与

offer

几乎一样,简单修改函数名称和打印信息即可:

functionhandleAnswer(message){// 获取房间ID和用户IDvar roomId = message.roomId;var uid = message.uid;var remoteUid = message.remoteUid;
    console.info("handleAnswer uid:"+ uid +" send answer to remoteUid: "+ remoteUid);// 获取房间Mapvar roomMap = roomTableMap.get(roomId);if(roomMap ==null){
        console.error("roomId:"+ roomId +" is not exist");return;}if(roomMap.get(uid)==null){
        console.error("uid:"+ uid +" is not exist in roomId:"+ roomId);return;}var remoteClient = roomMap.get(remoteUid);if(remoteClient){var msg =JSON.stringify(message);
        remoteClient.conn.sendText(msg);}else{
        console.error("remoteUid:"+ remoteUid +" is not exist in roomId:"+ roomId);}}functionhandleCandidate(message){// 获取房间ID和用户IDvar roomId = message.roomId;var uid = message.uid;var remoteUid = message.remoteUid;
    console.info("handleCandidate uid:"+ uid +" send Candidate to remoteUid: "+ remoteUid);// 获取房间Mapvar roomMap = roomTableMap.get(roomId);if(roomMap ==null){
        console.error("roomId:"+ roomId +" is not exist");return;}if(roomMap.get(uid)==null){
        console.error("uid:"+ uid +" is not exist in roomId:"+ roomId);return;}var remoteClient = roomMap.get(remoteUid);if(remoteClient){var msg =JSON.stringify(message);
        remoteClient.conn.sendText(msg);}else{
        console.error("remoteUid:"+ remoteUid +" is not exist in roomId:"+ roomId);}}
标签: webrtc 音视频

本文转载自: https://blog.csdn.net/qq_45087381/article/details/138339572
版权归原作者 君莫笑lucky 所有, 如有侵权,请联系我们删除。

“一对一WebRTC视频通话系列(四)——offer、answer、candidate信令实现”的评论:

还没有评论