WebRTC-1V1音视频通话websocket 业务代码2(业务复现)

主要架构在这里插入图片描述

1 peerA 向着 服务端发送join信令

// 获取本地流 promise 的then

function openLocalStream(stream) {
    
    
    console.log('Open local stream');
    doJoin(roomId);
    localVideo.srcObject = stream;
    localStream = stream;
}

function doJoin(roomId) {
    
    
    var jsonMsg = {
    
    
        'cmd': 'join',
        'roomId': roomId,
        'uid': localUserId,
    };
    var message = JSON.stringify(jsonMsg);
    //通过封装好的 sendmessage发送
    zeroRTCEngine.sendMessage(message);
    console.info("doJoin message: " + message);
}

2 peerB 向着 服务端发送join信令

// 获取本地流 promise 的then

function openLocalStream(stream) {
    
    
    console.log('Open local stream');
    doJoin(roomId);
    localVideo.srcObject = stream;
    localStream = stream;
}

function doJoin(roomId) {
    
    
    var jsonMsg = {
    
    
        'cmd': 'join',
        'roomId': roomId,
        'uid': localUserId,
    };
    var message = JSON.stringify(jsonMsg);
    zeroRTCEngine.sendMessage(message);
    console.info("doJoin message: " + message);
}

3 - 4 信令服务器向着双方返回 new-peer 和resp-join 信令

服务器信令处理

// join 主动加入房间
// leave 主动离开房间
// new-peer 有人加入房间,通知已经在房间的人
// peer-leave 有人离开房间,通知已经在房间的人
// offer 发送offer给对端peer
// answer发送offer给对端peer
// candidate 发送candidate给对端peer
const SIGNAL_TYPE_JOIN = "join";
const SIGNAL_TYPE_RESP_JOIN = "resp-join";  // 告知加入者对方是谁
const SIGNAL_TYPE_LEAVE = "leave";
const SIGNAL_TYPE_NEW_PEER = "new-peer";
const SIGNAL_TYPE_PEER_LEAVE = "peer-leave";
const SIGNAL_TYPE_OFFER = "offer";
const SIGNAL_TYPE_ANSWER = "answer";
const SIGNAL_TYPE_CANDIDATE = "candidate";

服务器对于信令处理的整体

var server = ws.createServer(function(conn){
    
    
    console.log("创建一个新的连接--------")
    conn.client = null; // 对应的客户端信息
    // conn.sendText("我收到你的连接了....");
    conn.on("text", function(str) {
    
    
        // console.info("recv msg:" + str);
        var jsonMsg = JSON.parse(str);

        switch (jsonMsg.cmd) {
    
    
            case SIGNAL_TYPE_JOIN:
                conn.client = handleJoin(jsonMsg, conn);
                break;
            case SIGNAL_TYPE_LEAVE:
                handleLeave(jsonMsg);
                break;
            case SIGNAL_TYPE_OFFER:
                handleOffer(jsonMsg);
                break;
            case SIGNAL_TYPE_ANSWER:
                handleAnswer(jsonMsg);
                break;
            case SIGNAL_TYPE_CANDIDATE:
                handleCandidate(jsonMsg);
                break;
        }

    });
    

处理Join 信令

function handleJoin(message, conn) {
    
    
    var roomId = message.roomId;
    var uid = message.uid;

    console.info("uid: " + uid + "try to join room " + roomId);
	
    var roomMap = roomTableMap.get(roomId);
    if (roomMap == null) {
    
    
        roomMap = new  ZeroRTCMap();
        roomTableMap.put(roomId, roomMap);
    }
	
    if(roomMap.size() >= 2) {
    
    
        console.error("roomId:" + roomId + " 已经有两人存在,请使用其他房间");
        // 加信令通知客户端,房间已满
        也可以  设计一个full 信令比如:  
             var jsonMsg = {
    
    
                    'cmd': SIGNAL_TYPE_PEER_FULL,
                    'remoteUid': uid
                };
                msg = JSON.stringify(jsonMsg);
      然后发送   console.info("room_full: " + msg);
                conn.sendText(msg);
        return null;
    }

    var client = new Client(uid, conn, roomId);
    roomMap.put(uid, client);
    if(roomMap.size() > 1) {
    
    
        // 房间里面已经有人了,加上新进来的人,那就是>=2了,所以要通知对方
        var clients = roomMap.getEntrys();
        for(var i in clients) {
    
    
            var remoteUid = clients[i].key;
            if (remoteUid != uid) {
    
    
                var jsonMsg = {
    
    
                	//通知第一个进房间的人  有人进来了
                    'cmd': SIGNAL_TYPE_NEW_PEER,
                    'remoteUid': uid
                };
                var msg = JSON.stringify(jsonMsg);
                var remoteClient =roomMap.get(remoteUid);
                console.info("new-peer: " + msg);
                remoteClient.conn.sendText(msg);

                jsonMsg = {
    
    
                    	//返回 后进房间的这个人 已经进来房间人的id
                    'cmd':SIGNAL_TYPE_RESP_JOIN,
                    'remoteUid': remoteUid
                };
                msg = JSON.stringify(jsonMsg);
                console.info("resp-join: " + msg);
                conn.sendText(msg);
            }
        }
    }

    return client;
}

5 A收到new-peer 信令后 B 收到resp——join信令以后

B收到以后

function handleResponseJoin(message) {
    
    
    console.info("handleResponseJoin, remoteUid: " + message.remoteUid);
    remoteUserId = message.remoteUid;
   
}

A收到以后

function handleRemoteNewPeer(message) {
    
    
    console.info("handleRemoteNewPeer, remoteUid: " + message.remoteUid);
    保存远端id
    remoteUserId = message.remoteUid;
    为双方交换sdp做准备
    doOffer();
}

6 A -> offer->信令服务器

当A收到B加入房间后 就开始为了双方通讯做准备(具体函数doffer)

doffer

function doOffer() {
    
    
    // 创建RTCPeerConnection
    if (pc == null) {
    
    
        createPeerConnection();
    }
    pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError);
}

1 如果没有rtcPeerConnection创建 具体参数前面说了

function createPeerConnection() {
    
    
    var defaultConfiguration = {
    
    
        bundlePolicy: "max-bundle",
        rtcpMuxPolicy: "require",
        iceTransportPolicy:"relay",//relay 或者 all
        // 修改ice数组测试效果,需要进行封装
        iceServers: [
            {
    
    
                "urls": [
                    "turn:公网ip/域名?transport=udp",
                    "turn:公网ip/域名?transport=tcp"       // 可以插入多个进行备选
                ],
                "username": "test",  coturn 设置的
                "credential": "123456"
            },
            {
    
    
                "urls": [
                    "stun:公网ip/域名"
                ]
            }
        ]
    };
 
    pc = new RTCPeerConnection(defaultConfiguration);
    pc.onicecandidate = handleIceCandidate;  //回调Candidate 函数
    pc.ontrack = handleRemoteStreamAdd;		//回调处理远端流的函数
    pc.onconnectionstatechange = handleConnectionStateChange;
    pc.oniceconnectionstatechange = handleIceConnectionStateChange

    localStream.getTracks().forEach((track) => pc.addTrack(track, localStream)); // 给video本地的绑定本地流 中的对应轨  WebRtc不同于ffmpeg一流可以多轨  一轨== 一个音视频ffmpeg流那种
}

2 创建好了 就发送创建本地offer success然后调用 createOfferAndSendMessage 失败就是xxx 上面有

session 就是 pc.createOffer() 返回值了 
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);
            zeroRTCEngine.sendMessage(message);
            // console.info("send offer message: " + message);
            console.info("send offer message");
        })
        .catch(function (error) {
    
    
            console.error("offer setLocalDescription failed: " + error);
        });

}

7 服务器收到A的offer

服务直接转发了 所以信令的cmd 没变还是 offer

function handleOffer(message) {
    
    
    var roomId = message.roomId;
    var uid = message.uid;
    var remoteUid = message.remoteUid;

    console.info("handleOffer uid: " + uid + "transfer  offer  to remoteUid" + remoteUid);
	// 找房间
    var roomMap = roomTableMap.get(roomId);
    if (roomMap == null) {
    
    
        console.error("handleOffer can't find then roomId " + roomId);
        return;
    }
	// 找人
    if(roomMap.get(uid) == null) {
    
    
        console.error("handleOffer can't find then uid " + uid);
        return;
    }
	// 找到以后发送
    var remoteClient = roomMap.get(remoteUid);
    if(remoteClient) {
    
    
        var msg = JSON.stringify(message);
        remoteClient.conn.sendText(msg);
    } else {
    
    
        console.error("can't find remoteUid: " + remoteUid);
    }
}

8 信令服务器发送offer 给 B

逻辑同上 但是这里 先 pc.setRemoteDescription(desc); 罢了
然后就说doAnswer();


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

doAnswer() 逻辑同上doffer

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);
            zeroRTCEngine.sendMessage(message);
            // console.info("send answer message: " + message);
            console.info("send answer message");
        })
        .catch(function (error) {
    
    
            console.error("answer setLocalDescription failed: " + error);
        });

}

9-10 服务器接收answer 并且转发answer 逻辑同上offer

服务器接收answer

function handleAnswer(message) {
    
    
    var roomId = message.roomId;
    var uid = message.uid;
    var remoteUid = message.remoteUid;

    console.info("handleAnswer uid: " + uid + "transfer answer  to remoteUid" + remoteUid);

    var roomMap = roomTableMap.get(roomId);
    if (roomMap == null) {
    
    
        console.error("handleAnswer can't find then roomId " + roomId);
        return;
    }

    if(roomMap.get(uid) == null) {
    
    
        console.error("handleAnswer can't find then uid " + uid);
        return;
    }

    var remoteClient = roomMap.get(remoteUid);
    if(remoteClient) {
    
    
        var msg = JSON.stringify(message);
        remoteClient.conn.sendText(msg);
    } else {
    
    
        console.error("can't find remoteUid: " + remoteUid);
    }
}

A收到服端发送过的Answer

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

现在会触发创建RTCPeerConnection 时候绑定的回调函数(ontrack也触发了不过没有数据传输过来不显示罢了)

10 A 向服务器传递 condidate(通过初始化RTCPeerconnection 对于Conturn服务器配置 内部通过ice request 得到了自己的地址 ) 11 信令服务器转发向B

A 向服务器传递 condidate

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("handleIceCandidate message: " + message);
        console.info("send candidate message");
    } else {
    
    
        console.warn("End of candidates");
    }
}

信令服务器转发A的condidate向B 逻辑同offer

function handleAnswer(message) {
    
    
    var roomId = message.roomId;
    var uid = message.uid;
    var remoteUid = message.remoteUid;

    console.info("handleAnswer uid: " + uid + "transfer answer  to remoteUid" + remoteUid);

    var roomMap = roomTableMap.get(roomId);
    if (roomMap == null) {
    
    
        console.error("handleAnswer can't find then roomId " + roomId);
        return;
    }

    if(roomMap.get(uid) == null) {
    
    
        console.error("handleAnswer can't find then uid " + uid);
        return;
    }

    var remoteClient = roomMap.get(remoteUid);
    if(remoteClient) {
    
    
        var msg = JSON.stringify(message);
        remoteClient.conn.sendText(msg);
    } else {
    
    
        console.error("can't find remoteUid: " + remoteUid);
    }
}

function handleCandidate(message) {
    
    
    var roomId = message.roomId;
    var uid = message.uid;
    var remoteUid = message.remoteUid;

    console.info("handleCandidate uid: " + uid + "transfer candidate  to remoteUid" + remoteUid);

    var roomMap = roomTableMap.get(roomId);
    if (roomMap == null) {
    
    
        console.error("handleCandidate can't find then roomId " + roomId);
        return;
    }

    if(roomMap.get(uid) == null) {
    
    
        console.error("handleCandidate can't find then uid " + uid);
        return;
    }

    var remoteClient = roomMap.get(remoteUid);
    if(remoteClient) {
    
    
        var msg = JSON.stringify(message);
        remoteClient.conn.sendText(msg);
    } else {
    
    
        console.error("can't find remoteUid: " + remoteUid);
    }
}

12 - 13 B接收服务器转发到A的condidate B向着信令服务器发送A的condidate

pc.addIceCandidate(candidate) 通过该函数保存对端 condidate 信息 然后触发handleIceCandidate() 发送数据向服务器

function handleRemoteCandidate(message) {
    
    
    console.info("handleRemoteCandidate");
    var candidate = JSON.parse(message.msg);
    pc.addIceCandidate(candidate).catch(e => {
    
    
        console.error("addIceCandidate failed:" + e.name);
    });
}

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("handleIceCandidate message: " + message);
        console.info("send candidate message");
    } else {
    
    
        console.warn("End of candidates");
    }
}

14 -15 A收到B的condidate 后续处理都是一样的 然后就condidate配对 一般3对 成功一个就开始音视频通话了(如果P2P能成功则进行P2P通话,如果P2P不成功则进行中继转发通话)

16 leave处理

客户端

function handleRemotePeerLeave(message) {
    
    
    console.info("handleRemotePeerLeave, remoteUid: " + message.remoteUid);
    remoteVideo.srcObject = null;
    if(pc != null) {
    
    
        pc.close();
        pc = null;
    }
}

服务端

function handleLeave(message) {
    
    
    var roomId = message.roomId;
    var uid = message.uid;

    console.info("uid: " + uid + "leave room " + roomId);

    var roomMap = roomTableMap.get(roomId);
    if (roomMap == null) {
    
    
        console.error("handleLeave can't find then roomId " + roomId);
        return;
    }
    roomMap.remove(uid);        // 删除发送者
    if(roomMap.size() >= 1) {
    
    
        var clients = roomMap.getEntrys();
        for(var i in clients) {
    
    
            var jsonMsg = {
    
    
                'cmd': 'peer-leave',
                'remoteUid': uid // 谁离开就填写谁
            };
            var msg = JSON.stringify(jsonMsg);
            var remoteUid = clients[i].key;
            var remoteClient = roomMap.get(remoteUid);
            if(remoteClient) {
    
    
                console.info("notify peer:" + remoteClient.uid + ", uid:" + uid + " leave");
                remoteClient.conn.sendText(msg);
            }
        }
    }
}

function handleForceLeave(client) {
    
    
    var roomId = client.roomId;
    var uid = client.uid;

    // 1. 先查找房间号
    var roomMap = roomTableMap.get(roomId);
    if (roomMap == null) {
    
    
        console.warn("handleForceLeave can't find then roomId " + roomId);
        return;
    }

    // 2. 判别uid是否在房间
    if (!roomMap.contains(uid)) {
    
    
        console.info("uid: " + uid +" have leave roomId " + roomId);
        return;
    }

    // 3.走到这一步,说明客户端没有正常离开,所以我们要执行离开程序
    console.info("uid: " + uid + " force leave room " + roomId);

    roomMap.remove(uid);        // 删除发送者
    if(roomMap.size() >= 1) {
    
    
        var clients = roomMap.getEntrys();
        for(var i in clients) {
    
    
            var jsonMsg = {
    
    
                'cmd': 'peer-leave',
                'remoteUid': uid // 谁离开就填写谁
            };
            var msg = JSON.stringify(jsonMsg);
            var remoteUid = clients[i].key;
            var remoteClient = roomMap.get(remoteUid);
            if(remoteClient) {
    
    
                console.info("notify peer:" + remoteClient.uid + ", uid:" + uid + " leave");
                remoteClient.conn.sendText(msg);
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_33329316/article/details/124775768