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

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


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

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


offer、answer、candidate信令实现

  • 整体实现思路
    • 1. 客户端
    • 2. 服务端

整体实现思路

整体实现思路(红色部分为客户端,蓝色为服务端):
(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()

function doOffer() {//创建RTCPeerConnection对象if(pc == null)ceratePeerConnection();pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError);
}
function ceratePeerConnection() {//创建RTCPeerConnection对象pc = new RTCPeerConnection(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);

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); //将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()进行处理。

	function handleRemoteOffer(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()函数实现。

function doAnswer() {pc.createAnswer().then(createAnswerAndSendMessage).catch(handleCreateAnswerError);
}
function createAnswerAndSendMessage(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()进行处理。

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

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

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

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("send candidate message");} else {console.warn("End of candidates");}
}
function handleRemoteCandidate(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;
应从消息监听函数入手,完成对offeranswercandidate这3种情况的处理。

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

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

  1. 获取房间ID和用户ID。
  2. 获取房间Map。
  3. 检查用户ID是否存在于房间Map中。
  4. 如果远程用户存在,将消息发送给远程用户。
  5. 如果不存在,输出错误信息。
function handleOffer(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);}
}

answercandidate信令处理函数逻辑与offer几乎一样,简单修改函数名称和打印信息即可:

function handleAnswer(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);}
}function handleCandidate(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);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/679400.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【小浩算法 BST与其验证】

BST与其验证 前言我的思路思路一 中序遍历判断数组无重复递增思路二 递归边界最大值最小值的传递 我的代码测试用例1测试用例2 前言 BST是二叉树一个经典应用,我们常常将其用于数据的查找以及构建平衡二叉树等。今天我所做的题目是验证一颗二叉树是否为二叉搜索树&…

TinyEngine 低代码引擎区块局域网部署方案全新上线!

本文由体验技术团队 TinyEngine 项目组成员创作~ 在 TinyEngine 开源后,对私有化部署存在诉求的用户越来越多,而当前 TinyEngine 多项内容都依托在公网中,当前官网提供的区块发布方案,为公网环境下的发布,不能完全满足…

什么是驱动数字签名?如何获取驱动数字签名?

Windows 驱动程序承载着计算机实现的各种内核和用户模式功能。如果驱动程序被黑客攻击,可能会产生很多问题。Windows通过数字签名来验证驱动程序包的完整性及发布者的身份。2020年10月的安全更新中,微软加强了对驱动软件的验证,如果Windows无…

文件IO-使用dup2实现错误日志功能及判断文件权限,并终端输出

1:使用 dup2 实现错误日志功能 使用 write 和 read 实现文件的拷贝功能,注意,代码中所有函数后面,紧跟perror输出错误信息,要求这些错误信息重定向到错误日志 err.txt 中去 代码: #incl…

去除图片水印软件-inpaint

一、普通使用教程 亲眼看看使用 Inpaint 从照片中删除不需要的元素是多么容易: 1.1加载图片 1.2 选择要纠正的问题区域 1.3 告别不需要的对象并保存 二、功能 1 修复旧照片 老并不总是意味着坏。我们拥有的一些旧照片对我们来说仍然很重要,因为它们仍…

mybatis:Spring junit 测试报错:Failed to load ApplicationContext

Spring junit 测试报错:Failed to load ApplicationContext 解决方法,修改mybatis版本,版本过高导致无法加载依赖

电脑屏幕监控软件有哪些?8款受欢迎的电脑屏幕监控软件

电脑屏幕监控软件有哪些?8款受欢迎的电脑屏幕监控软件 市场上有很多监控软件,因为太多,很多老板不知道怎么选,今天小编从它们各自的特点、优势、未来发展趋势几方面,介绍8款受欢迎的电脑屏幕监控软件。 第一是&#x…

xhs 旋转滑块流程分析

声明 本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 前言 本文首发于公众号…

网络知识点之—QoS

QoS(Quality of Service,服务质量)指一个网络能够利用各种基础技术,为指定的网络通信提供更好的服务能力,是网络的一种安全机制, 是用来解决网络延迟和阻塞等问题的一种技术。QoS的保证对于容量有限的网络来…

算法系列--多源BFS问题

💕"对相爱的人来说,对方的心意,才是最好的房子。"💕 作者:Lvzi 文章主要内容:算法系列–多源BFS问题 大家好,今天为大家带来的是算法系列--多源BFS问题 前言: 之前我们已经学习过单源的最短路问…

论文阅读】 ICCV-2021-3D Local Convolutional Neural Networks for Gait Recognition

motivation :现有方法方法无法准确定位身体部位,不同的身体部位可以出现在同一个条纹(如手臂和躯干),一个部分可以出现在不同帧(如手)的不同条纹上。其次,不同的身体部位具有不同的尺度,即使是不同帧中的同一部分也可以出现在不同…

每日一博 - 闲聊架构设计中的多级缓存设计

文章目录 方法论概述客户端缓存应用层缓存服务层缓存缓存设计的注意事项总结 思维导图戳这里 方法论概述 从客户端到服务层,缓存的应用广泛而重要。通过合理的缓存设计,能够有效地提高系统的性能并降低延迟。 客户端缓存 在客户端层面,浏览…