
WebRTC本地实现 - WebRTC通信实现(Web端)
发布日期:2021-05-10 00:57:59
浏览次数:29
分类:精选文章
本文共 9476 字,大约阅读时间需要 31 分钟。
操作系统准备
- 需要准备好摄像头,由于本电脑没有摄像头,这里使用虚拟摄像头(VCam)来代替真实摄像头
效果展示
- 本地台式机使用VCam虚拟摄像头来模拟视频流
- 远程笔记本访问同一链接,从而进行通信
通信效果展示
代码提取
- 链接:
- 提取码: kyfu
WebRTC通信实现明细
- 该文章代码是在 WebRTC本地实现 - Vue与Socket IO通信项目搭建与测试(https)基础上进行更改操作
- 原文章修改的代码内容如下【可查看本文章: 2 - 流程代码】
- 删除原来的发送信息的按钮和test方法
- 增加方法 sendMessage
// 发送消息给 Socket IO 服务器sendMessage(msg) { this.$socket.emit('message', sendMessage)}
代码编写流程
- 客户Peer - A 和 Peer - B进行通信,有可能是A先发起 或者 B先发起,所以代码发起端跟接收端都得写
- Connect Signal Server:2端 连接到信令服务器,也就是之前与Socket IO进行通信过程
- 流程图片
1 - Connect Signal Server
- 流程说明 A用户连接到Sinal Server(也就是Socket IO 服务器) B用户连接到Sinal Server(也就是Socket IO 服务器)
- getUserMedia:2端 获取本地媒体流
- 流程图片
2 - getUserMedia
-
流程说明
A用户本地的媒体流,并设置localStream B用户本地的媒体流,并设置localStream -
流程代码(this.$lib.logInfo等可直接替换为console.log)
本地视频流
远程视频流
- 效果展示(如果 VCam播放视频,这里显示的是视频的内容)
2 - 效果展示
- 1端 create,1端 join:
- 流程图片
image.png
-
流程说明
A为创建者,B为加入者(2个扮演的角色可调换) A用户创建房间(自定义房间名称:room_webrtc) 同时Signal Server(Socket IO服务器)通知A用户,你是创建者 B用户加入到room_webrtc 同时Signal Server(Socket IO服务器)通知A用户有新的用户加入,通知B用户加入成功 -
流程代码
data() return 中增加变量
// 房间名称roomName: 'room_webrtc',// 通道是否准备就绪isChannelReady: false
methods:中增加方法:创建加入房间
// 试图创建或加入Socket IO的房间createOrJoinRoom() { this.$lib.logInfo('试图创建或加入Socket IO 的房间:' + this.roomName); this.$socket.emit('create or join', this.roomName);}
sockets:增加监听多个方法
通知用户(A)为创建者(created)
通知房间其他用户(A)有新伙伴(B)准备加入(join) 通知用户(B)加入成功(joined) 通知房间其他用户(A)新伙伴(B)加入成功(ready) 房间满了(full)
created(room) { this.$lib.logInfo('您是房间:' + room + ' 的创建者'); // 赋值创建者标志 this.isInitiator = true;},join(room) { this.$lib.logInfo('房间:' + room + ' 有新的用户准备加入');},joined(room) { this.$lib.logInfo('房间:' + room + ' 已经加入成功'); // 加入者,设定通道准备就绪 this.isChannelReady = true;},ready(){ this.$lib.logInfo('房间:' + room + ' 新的用户已经加入成功'); // 创建者,设定通道准备就绪 this.isChannelReady = true;},full(){ this.$lib.logInfo('房间:' + room + ' 已经满员了');}
- create 端(创建者端,发送offer)
- 流程图片
创建 RTCPeerConnection
-
流程说明
当远程伙伴加入成功后,创建端需开始创建 RTCPeerConnection(本地端机器与远端机器的一条对等连接) 创建Offer 并 设置本地描述(SetLocalDescription) -
流程代码
data() return 中增加变量
// RTCPeerConnection,本地端机器与远端机器的一条对等连接pc: null,// coturn配置供 RTCPeerConnection 使用,(如果没有coturn,可参照demo的step-05中的代码获取)pcConfiguration: { iceServers: [{ urls: 'turn:wzeros.cn', username: 'wzeros', credential: '123456' }]},// 远程媒体流remoteStream: null,
methods:中增加方法:创建RTCPeerConnection
// 创建本地端机器与远端机器的一条对等连接createPeerConnection() { try { this.$lib.logInfo('创建 RTCPeerConnection'); this.pc = new RTCPeerConnection(this.pcConfiguration); this.pc.onicecandidate = this.handleIceCandidate; this.pc.onaddstream = this.handleRemoteStreamAdded; this.pc.onremovestream = this.handleRemoteStreamRemoved; this.$lib.logInfo('增加本地媒体流'); this.pc.addStream(this.localStream); } catch (e) { this.$lib.logError('创建 RTCPeerConnection 出错,错误原因如下'); this.$lib.logObj(e.message); return; }},// 事件触发器// 只要本地代理ICE 需要通过信令服务器传递信息给其他对等端时就会触发// 功能说明地址:https://developer.mozilla.org/zh-CN/docs/Web/API/RTCPeerConnection/onicecandidatehandleIceCandidate(event) { this.$lib.logInfo('触发 icecandidate 事件,事件内容如下'); this.$lib.logInfo(event); if (event.candidate) { this.sendMessage({ type: 'candidate', label: event.candidate.sdpMLineIndex, id: event.candidate.sdpMid, candidate: event.candidate.candidate }); } else { this.$lib.logInfo('End of candidates'); }},// 处理远程流添加handleRemoteStreamAdded(event) { this.$lib.logInfo('处理远程媒体流添加'); // 赋值远程媒体流 this.remoteStream = event.stream; // 展示远程媒体流 this.$refs.remoteVideo.srcObject = this.remoteStream;},// 处理远程流撤掉handleRemoteStreamRemoved(event) { this.$lib.logInfo('Remote stream removed. Event: ', event);},// 创建 offercreateOffer() { this.$lib.logInfo('发送offer给远程的伙伴'); this.pc.createOffer((sessionDescription) => { this.$lib.logInfo('设置本地描述,描述内容如下:'); this.$lib.logInfo(sessionDescription); this.pc.setLocalDescription(sessionDescription); // 通过Signal Server 发送(offer) sessionDescription 到远程伙伴 this.sendMessage(sessionDescription); }, (event) => { this.$lib.logError('createOffer()出错,错误原因如下:'); this.$lib.logError(event); });}
sockets:ready中增加调用createPeerConnection
ready(room) { this.$lib.logInfo('房间:' + room + ' 新的用户已经加入成功'); // 创建者 | 加入者,设定通道准备就绪 this.isChannelReady = true; // 创建者 if (this.isInitiator) { // 创建本地端机器与远端机器的一条对等连接 this.createPeerConnection(); // 创建 offer this.createOffer(); }}
- join端(加入者端,接收offer)
- 流程图片
接收offer
-
流程说明
创建者端通过Signal Server(Socket IO服务器)发送出了offer 加入者端在监听(message)消息后,需要初始化RTCPeerConnection等操作 再发送一个回应信息(answer)给create端 并 设置本地描述(SetLocalDescription) -
流程代码
methods:中增加方法:创建回复
// 创建 answercreateAnswer() { this.$lib.logInfo('发送answer给远程的创建者'); this.pc.createAnswer().then((sessionDescription) => { this.$lib.logInfo('设置会话描述,描述内容如下:'); this.$lib.logObj(sessionDescription); this.pc.setLocalDescription(sessionDescription); // 通过Signal Server 发送(answer) sessionDescription 到远程的创建者 this.sendMessage(sessionDescription); }, (error) => { this.$lib.logError('创建会话描述出错,错误原因如下:'); this.$lib.logObj(error); });}
sockets:修改message监听方法
message(msg) { this.$lib.logInfo('接收到服务端的消息,消息如下:'); this.$lib.logObj(msg); if (msg.type) { switch (msg.type) { // 加入者接收到创建者的offer case 'offer': // 创建本地端机器与远端机器的一条对等连接 this.createPeerConnection(); // 设置远程回话描述 this.pc.setRemoteDescription(new RTCSessionDescription(msg)); // 给创建者回复 this.createAnswer(); break; default: break; } }}
- create端(创建者端,接收answer)
- 流程图片
接收answer
-
流程说明
加入者接收到offer后,返回answer,创建者端接收到answer后设定远程会话描述 设置完会话描述后,此时在创建RTCPeerConnection使用了coturn服务配置的话,将会触发onicecandidate事件。如果建立连接成功后将会触发onaddstream(添加远程媒体流) 在onicecandidate事件中做了一件事情发送type:'candidate'的内容到Signal Server服务器,继而推送到2端 -
流程代码
sockets:修改message监听方法
message(msg) { this.$lib.logInfo('接收到服务端的消息,消息如下:'); this.$lib.logObj(msg); if (msg.type) { switch (msg.type) { case 'offer': // 加入者接收到创建者的offer // 创建本地端机器与远端机器的一条对等连接 this.createPeerConnection(); // 设置远程会话描述 this.pc.setRemoteDescription(new RTCSessionDescription(msg)); // 给创建者回复 this.createAnswer(); break; case 'answer': // 创建者接收到加入者的answer // 设置远程会话描述 this.pc.setRemoteDescription(new RTCSessionDescription(msg)); break; default: break; } }}
- 2端candidate处理
- 流程图片
candidate处理
-
流程说明
双方只需要做的一件事情就是对RTCPeerConnection添加一个Ice代理后,之后自动触发添加远程媒体流,双方就能够进行通信了 -
流程代码
sockets:修改message监听方法
message(msg) { this.$lib.logInfo('接收到服务端的消息,消息如下:'); this.$lib.logObj(msg); if (msg.type) { switch (msg.type) { case 'offer': // 加入者接收到创建者的offer // 创建本地端机器与远端机器的一条对等连接 this.createPeerConnection(); // 设置远程会话描述 this.pc.setRemoteDescription(new RTCSessionDescription(msg)); // 给创建者回复 this.createAnswer(); break; case 'answer': // 创建者接收到加入者的answer // 设置远程会话描述 this.pc.setRemoteDescription(new RTCSessionDescription(msg)); break; case 'candidate': // candidate处理 this.pc.addIceCandidate(new RTCIceCandidate({ sdpMLineIndex: msg.label, candidate: msg.candidate })); break; default: break; } }}
- 2端退出通信处理
- 流程图片
退出通信处理
-
流程说明
只要有一方退出系统,发送离开消息到Signal Server服务器,信令服务器通知双方结束通信 关闭RTCPeerConnection,暂停本地视频流和远程视频流展示 -
流程代码
methods:中增加方法:离开通信
leave() { if(this.remoteStream !== null) { this.$refs.remoteVideo.srcObject = null; this.remoteStream = null; } if(this.localStream !== null) { this.$refs.localVideo.srcObject = null; this.localStream = null; } if (this.pc !== null) { this.pc.close(); this.pc = null; }}
export default :中增加beforeDestroy监听
beforeDestroy() { this.sendMessage('leave');}
sockets:修改message监听方法
message(msg) { this.$lib.logInfo('接收到服务端的消息,消息如下:'); this.$lib.logObj(msg); if (msg.type) { switch (msg.type) { case 'offer': // 加入者接收到创建者的offer // 创建本地端机器与远端机器的一条对等连接 this.createPeerConnection(); // 设置远程会话描述 this.pc.setRemoteDescription(new RTCSessionDescription(msg)); // 给创建者回复 this.createAnswer(); break; case 'answer': // 创建者接收到加入者的answer // 设置远程会话描述 this.pc.setRemoteDescription(new RTCSessionDescription(msg)); break; case 'candidate': // candidate处理 this.pc.addIceCandidate(new RTCIceCandidate({ sdpMLineIndex: msg.label, candidate: msg.candidate })); break; case 'leave': // 离开通信 this.leave(); break; default: break; } }}
发表评论
最新留言
感谢大佬
[***.8.128.20]2025年03月31日 07时42分14秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
PNFT邮票数字资产化,科技、美学与价值的完美融合
2021-05-10
5G赋能新文旅 巡天遥看一千河
2021-05-10
Leetcode第557题---翻转字符串中的单词
2021-05-10
IDEA:修改编辑区背景色
2021-05-10
C# WinForm 圆角button
2021-05-10
Problem G. The Stones Game【取石子博弈 & 思维】
2021-05-10
HRBUST—1891 A + B Problem VII
2021-05-10
装饰模式
2021-05-10
框架综合实践(3)-业务逻辑businessView的封装
2021-05-10
Robot Framework 新建资源文件-用户关键字
2021-05-10
HDU - 4289 Control 拆点最大流,领悟拆点的真谛吧!
2021-05-10
HDU - 2732 Leapin‘ Lizards 拆点最大流+BFS 学好英语QAQ
2021-05-10
导弹拦截 NOIP1999 dilworth定理裸题
2021-05-10
java数组初始化及内存分析
2021-05-10
Unable to execute dex: Multiple dex files
2021-05-10
3分钟搞懂js的冒泡和捕获?
2021-05-10
Mac电脑调用自带的命令行窗口
2021-05-10
终端查看本机ip地址
2021-05-10
vue前端导出多级表头的excel表
2021-05-10