WebRTC本地实现 - WebRTC通信实现(Web端)
发布日期:2021-05-10 00:57:59 浏览次数:29 分类:精选文章

本文共 9476 字,大约阅读时间需要 31 分钟。

操作系统准备

  • 需要准备好摄像头,由于本电脑没有摄像头,这里使用虚拟摄像头(VCam)来代替真实摄像头

效果展示

  • 本地台式机使用VCam虚拟摄像头来模拟视频流
  • 远程笔记本访问同一链接,从而进行通信

 

通信效果展示

代码提取

  • 链接:
  • 提取码: kyfu

WebRTC通信实现明细

  • 该文章代码是在 WebRTC本地实现 - Vue与Socket IO通信项目搭建与测试(https)基础上进行更改操作
  • 原文章修改的代码内容如下【可查看本文章: 2 - 流程代码】
    1. 删除原来的发送信息的按钮和test方法
    2. 增加方法 sendMessage

 

// 发送消息给 Socket IO 服务器sendMessage(msg) {    this.$socket.emit('message', sendMessage)}

代码编写流程

  • 客户Peer - A 和 Peer - B进行通信,有可能是A先发起 或者 B先发起,所以代码发起端跟接收端都得写
  1. Connect Signal Server:2端 连接到信令服务器,也就是之前与Socket IO进行通信过程
  • 流程图片

 

1 - Connect Signal Server

  • 流程说明
    A用户连接到Sinal Server(也就是Socket IO 服务器)
    B用户连接到Sinal Server(也就是Socket IO 服务器)
  1. getUserMedia:2端 获取本地媒体流
  • 流程图片

 

2 - getUserMedia

  • 流程说明

    A用户本地的媒体流,并设置localStream
    B用户本地的媒体流,并设置localStream

  • 流程代码(this.$lib.logInfo等可直接替换为console.log)

 

  • 效果展示(如果 VCam播放视频,这里显示的是视频的内容)

 

2 - 效果展示

  1. 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 + ' 已经满员了');}
  1. 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();    }}
  1. 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;        }    }}
  1. 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;        }    }}
  1. 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;        }    }}
  1. 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;        }    }}

 

上一篇:WebRTC入门与提高-WebRTC原理(STUN/TURN/SDP)
下一篇:h5 小游戏总结及踩坑记录

发表评论

最新留言

感谢大佬
[***.8.128.20]2025年03月31日 07时42分14秒