webrtc实现视频群聊系列文章终章之完成即时通讯+多人视频会议(开放源码)

引言

前面几篇文章讲了使用webrtc实现本地模拟通话视频聊天,现实网络1对1视频聊天以及屏幕分享和聊天随时切换的文章,接下来就下来实战怎么利用webrtc实现多人群视频通话,会议

思路

  1. 因为webrtc是点对点的,通过前面文章我们也实现了相应的效果,但是多对多的实现思路也是基于此的,条件是:一对一维护一个PeerConnection,多对多维护nPeerConnection
  2. 简单的讲就是每个客户端维护会议室中自己和其他会议室所有人的链接信息PeerConnection
  3. 当会议室中有人加入进来之后,加入的一方通知服务器自己加入,服务器发送通知给其他已经在会议室的人
  4. 等到初始化完成每个客户端的PeerConnection后,最后加入的用户向每个成员发送offer
  5. 每个成员监听到offer后响应answer
  6. 通过onicecandidate 实现多端ICE候选,最后检测每个客户端视频加入到本地video标签并以各个用户名称命名元素ID
  7. 流程基本不变,和点对点的一样,只不过在web端多了一些操作,以前是一个发送一个接受响应,现在是一个发送,多个监听响应

演示

源码地址

  • 后端: https://github.com/wangsrGit119/suc-chat-bandend
  • 前段: https://github.com/wangsrGit119/suc-love-chat
  • 演示地址就不放在文章了,想要的可以直接留言,部署自己的或者遇到问题都可以问我哦

具体代码实现

  • 初始化各个客户端的peerConnect,这个操作是每个用户主动去创建会议或者接受邀请的时候才会去触发的

//初始化 PeerConnection
initPeer(peerName,e){
    const that = this;
    console.log(peerName+"初始化PeerConnection")
    let peer_tep = new PeerConnection(this.iceServers);
    peer_tep.addStream(that.localStream);
    peer_tep.onicecandidate = function(event) {
        console.log("监听ice候选信息",event.candidate)
        if (event.candidate) {
            let candidate_data = {userId:that.userInfo.userId,username:that.userInfo.username,candidate:event.candidate}
            let params = {userId:that.userInfo.userId,targetId:e.userId,targetName:e.username,targetType:2,data:candidate_data}
            that.socket.emit("candidate",params)
        }else{
            console.log("ICE收集已经完成")
        }
    };
    peer_tep.onaddstream = (event) => {
        console.log("监听到视频加入 加入用户 ",e.username)
        that.createEleVideo(event.stream,e.username)
    };
    that.userPeerList[peerName] = peer_tep;
},

  • 每个刚加进来的用户进行创建offer并发送到其他客户端

//创建连接
async onCreateOffer() {
    const that = this;
    console.log("开始创建offer")
    for(const ele of that.groupUserList){
        let peerName = that.userInfo.username+"-"+ele.username;
        if(that.userInfo.username !== ele.username){
            //创建offer
            let offer = await that.userPeerList[peerName].createOffer(that.offerOption);
            //设置本地描述
            await that.userPeerList[peerName].setLocalDescription(offer)
            //远程发送到服务器 并转发到其他的客户端
            let data = {offer:offer,userId:that.userInfo.userId,username:that.userInfo.username,info:"发送offer"}
            let params = {userId:that.userInfo.userId,targetId:ele.userId,targetName:ele.username,targetType:2,data:data}
            that.socket.emit("offer",params)
        }
    }
},
  • 监听别的客户端的响应

//监听 Ice 候选
async onIceCandidate(data) {
    const that = this;
    let peerName = that.userInfo.username+"-"+data.data.username;
    await that.userPeerList[peerName].addIceCandidate(data.data.candidate)
},
//监听远端offer
async onOffer(data) {
    const that = this;
    let peerName = that.userInfo.username+"-"+data.data.username;
    await that.userPeerList[peerName].setRemoteDescription(data.data.offer)
    // 接收端创建 answer
    let answer = await that.userPeerList[peerName].createAnswer();
    // 接收端设置本地 answer 描述
    await that.userPeerList[peerName].setLocalDescription(answer);
    //发送到呼叫端 answer
    let answer_data = {userId:that.userInfo.userId,username:that.userInfo.username,answer:answer}
    let params = {userId:that.userInfo.userId,targetId:data.data.userId,targetName:data.data.username,targetType:data.targetType,data:answer_data}
    that.socket.emit("answer",params)
},
//监听远程响应
async onAnswer(data) {
    const that = this;
    let peerName = that.userInfo.username+"-"+data.data.username;
    // 发送端 设置远程 answer 描述
    await that.userPeerList[peerName].setRemoteDescription(data.data.answer);
},

  • 监听到媒体流的时候创建媒体显示,注意,监听到的媒体以用户名作为dom元素id,这样移除或者禁言都可以对应到具体用户
//追加视频
createEleVideo(stream,id){
    console.log("createEleVideo",stream);
    let ele = document.getElementById("ManyToManyVideo");
    let old = document.getElementById(id);

    if(old){
        old.srcObject = stream;
    }else{
        let video = document.createElement('video');
        video.controls = true;
        video.autoplay = true;
        video.width = 230;
        video.height = 200;
        video.volume = 0.1;
        video.id = id;
        video.srcObject = stream;
        ele.append(video);
    }
},

最后

  • 求个赞赞,有问题请留言
  • 文章来源公众号 苏克分享
    苏克分享