赞
踩
先会骑车,再研究为什么这么骑,才是我认为学习技术的思路,底部付了demo例子,根据例子上面的介绍即可运行。
看例子中代码,使用nodejs启动
后面再慢慢补吧,问题有点多
<html> <head> <title>Voice WebRTC demo</title> </head> <h1>WebRTC demo 1v1</h1> <div id="buttons"> <input id="zero-roomId" type="text" placeholder="请输入房间ID" maxlength="40"/> <button id="joinBtn" type="button">加入</button> <button id="leaveBtn" type="button">离开</button> </div> <div id="videos"> <video id="localVideo" autoplay muted playsinline>本地窗口</video> <video id="remoteVideo" autoplay playsinline>远端窗口</video> </div> <script src="js/main.js"></script> <!-- 可直接引入在线js:https://webrtc.github.io/adapter/adapter-latest.js --> <script src="js/adapter-latest.js"></script> </html>
'use strict'; // 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 localUserId = Math.random().toString(36).substr(2); // 本地uid var remoteUserId = -1; // 对端 var roomId = 0; var localVideo = document.querySelector('#localVideo'); var remoteVideo = document.querySelector('#remoteVideo'); var localStream = null; var remoteStream = null; var pc = null; var zeroRTCEngine; function handleIceCandidate(event) { console.info("handleIceCandidate"); if (event.candidate) { var candidateJson = { 'label': event.candidate.sdpMLineIndex, 'id': event.candidate.sdpMid, 'candidate': event.candidate.candidate }; var jsonMsg = { 'cmd': SIGNAL_TYPE_CANDIDATE, 'roomId': roomId, 'uid': localUserId, 'remoteUid':remoteUserId, 'msg': JSON.stringify(candidateJson) }; var message = JSON.stringify(jsonMsg); zeroRTCEngine.sendMessage(message); console.info("handleIceCandidate message: " + message); console.info("send candidate message"); } else { console.warn("End of candidates"); } } function handleRemoteStreamAdd(event) { console.info("handleRemoteStreamAdd"); remoteStream = event.streams[0]; // 视频轨道 // let videoTracks = remoteStream.getVideoTracks() // 音频轨道 // let audioTracks = remoteStream.getAudioTracks() remoteVideo.srcObject = remoteStream; } function handleConnectionStateChange() { if(pc != null) { console.info("ConnectionState -> " + pc.connectionState); } } function handleIceConnectionStateChange() { if(pc != null) { console.info("IceConnectionState -> " + pc.iceConnectionState); } } function createPeerConnection() { var defaultConfiguration = { bundlePolicy: "max-bundle", rtcpMuxPolicy: "require", iceTransportPolicy:"all",//relay 或者 all // 修改ice数组测试效果,需要进行封装 iceServers: [ { "urls": [ "turn:192.168.1.173:3478?transport=udp", "turn:192.168.1.173:3478?transport=tcp" // 可以插入多个进行备选 ], "username": "lqf", "credential": "123456" }, { "urls": [ "stun:192.168.1.173:3478" ] } ] }; pc = new RTCPeerConnection(defaultConfiguration); // 音视频通话的核心类 pc.onicecandidate = handleIceCandidate; pc.ontrack = handleRemoteStreamAdd; pc.onconnectionstatechange = handleConnectionStateChange; pc.oniceconnectionstatechange = handleIceConnectionStateChange localStream.getTracks().forEach((track) => pc.addTrack(track, localStream)); // 把本地流设置给RTCPeerConnection } 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); }); } function handleCreateOfferError(error) { console.error("handleCreateOfferError: " + error); } 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); }); } function handleCreateAnswerError(error) { console.error("handleCreateAnswerError: " + error); } var ZeroRTCEngine = function (wsUrl) { this.init(wsUrl); zeroRTCEngine = this; return this; } ZeroRTCEngine.prototype.init = function (wsUrl) { // 设置websocket url this.wsUrl = wsUrl; /** websocket对象 */ this.signaling = null; } ZeroRTCEngine.prototype.createWebsocket = function () { zeroRTCEngine = this; zeroRTCEngine.signaling = new WebSocket(this.wsUrl); zeroRTCEngine.signaling.onopen = function () { zeroRTCEngine.onOpen(); } zeroRTCEngine.signaling.onmessage = function (ev) { zeroRTCEngine.onMessage(ev); } zeroRTCEngine.signaling.onerror = function (ev) { zeroRTCEngine.onError(ev); } zeroRTCEngine.signaling.onclose = function (ev) { zeroRTCEngine.onClose(ev); } } ZeroRTCEngine.prototype.onOpen = function () { console.log("websocket打开"); } ZeroRTCEngine.prototype.onMessage = function (event) { console.log("websocket收到信息: " + event.data); var jsonMsg = null; try { jsonMsg = JSON.parse(event.data); } catch(e) { console.warn("onMessage parse Json failed:" + e); return; } switch (jsonMsg.cmd) { case SIGNAL_TYPE_NEW_PEER: handleRemoteNewPeer(jsonMsg); break; case SIGNAL_TYPE_RESP_JOIN: handleResponseJoin(jsonMsg); break; case SIGNAL_TYPE_PEER_LEAVE: handleRemotePeerLeave(jsonMsg); break; case SIGNAL_TYPE_OFFER: handleRemoteOffer(jsonMsg); break; case SIGNAL_TYPE_ANSWER: handleRemoteAnswer(jsonMsg); break; case SIGNAL_TYPE_CANDIDATE: handleRemoteCandidate(jsonMsg); break; } } ZeroRTCEngine.prototype.onError = function (event) { console.log("onError: " + event.data); } ZeroRTCEngine.prototype.onClose = function (event) { console.log("onClose -> code: " + event.code + ", reason:" + EventTarget.reason); } ZeroRTCEngine.prototype.sendMessage = function (message) { this.signaling.send(message); } function handleResponseJoin(message) { console.info("handleResponseJoin, remoteUid: " + message.remoteUid); remoteUserId = message.remoteUid; // doOffer(); } function handleRemotePeerLeave(message) { console.info("handleRemotePeerLeave, remoteUid: " + message.remoteUid); remoteVideo.srcObject = null; if(pc != null) { pc.close(); pc = null; } } function handleRemoteNewPeer(message) { console.info("处理远端新加入链接,并发送offer, remoteUid: " + message.remoteUid); remoteUserId = message.remoteUid; doOffer(); } function handleRemoteOffer(message) { console.info("handleRemoteOffer"); if(pc == null) { createPeerConnection(); } var desc = JSON.parse(message.msg); pc.setRemoteDescription(desc); doAnswer(); } function handleRemoteAnswer(message) { console.info("handleRemoteAnswer"); var desc = JSON.parse(message.msg); pc.setRemoteDescription(desc); } function handleRemoteCandidate(message) { console.info("handleRemoteCandidate"); var jsonMsg = message.msg; if(typeof message.msg === "string"){ jsonMsg = JSON.parse(message.msg); } var candidateMsg = { 'sdpMLineIndex': jsonMsg.label, 'sdpMid': jsonMsg.id, 'candidate': jsonMsg.candidate }; var candidate = new RTCIceCandidate(candidateMsg); pc.addIceCandidate(candidate).catch(e => { console.error("addIceCandidate failed:" + e.name); }); } function doOffer() { // 创建RTCPeerConnection if (pc == null) { createPeerConnection(); } // let options = {offerToReceiveVideo:true} // pc.createOffer(options).then(createOfferAndSendMessage).catch(handleCreateOfferError); pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError); } function doAnswer() { pc.createAnswer().then(createAnswerAndSendMessage).catch(handleCreateAnswerError); } function doJoin(roomId) { var jsonMsg = { 'cmd': 'join', 'roomId': roomId, 'uid': localUserId, }; var message = JSON.stringify(jsonMsg); zeroRTCEngine.sendMessage(message); console.info("doJoin message: " + message); } function doLeave() { var jsonMsg = { 'cmd': 'leave', 'roomId': roomId, 'uid': localUserId, }; var message = JSON.stringify(jsonMsg); zeroRTCEngine.sendMessage(message); console.info("doLeave message: " + message); hangup(); } function hangup() { localVideo.srcObject = null; // 0.关闭自己的本地显示 remoteVideo.srcObject = null; // 1.不显示对方 closeLocalStream(); // 2. 关闭本地流 if(pc != null) { pc.close(); // 3.关闭RTCPeerConnection pc = null; } } function closeLocalStream() { if(localStream != null) { localStream.getTracks().forEach((track) => { track.stop(); }); } } function openLocalStream(stream) { console.log('Open local stream'); doJoin(roomId); localVideo.srcObject = stream; // 显示画面 localStream = stream; // 保存本地流的句柄 } function initLocalStream() { navigator.mediaDevices.getUserMedia({ audio: true, video: true }) .then(openLocalStream) .catch(function (e) { alert("getUserMedia() error: " + e.name); }); } // zeroRTCEngine = new ZeroRTCEngine("wss://192.168.1.60:80/ws"); zeroRTCEngine = new ZeroRTCEngine("ws://192.168.1.60:9001/ws"); zeroRTCEngine.createWebsocket(); document.getElementById('joinBtn').onclick = function () { roomId = document.getElementById('zero-roomId').value; if (roomId == "" || roomId == "请输入房间ID") { alert("请输入房间ID"); return; } console.log("第一步:加入按钮被点击, roomId: " + roomId); // 初始化本地码流 initLocalStream(); } document.getElementById('leaveBtn').onclick = function () { console.log("离开按钮被点击"); doLeave(); }
var ws = require("nodejs-websocket") var prort = 9001; // 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"; /** ----- ZeroRTCMap ----- */ var ZeroRTCMap = function () { this._entrys = new Array(); this.put = function (key, value) { if (key == null || key == undefined) { return; } var index = this._getIndex(key); if (index == -1) { var entry = new Object(); entry.key = key; entry.value = value; this._entrys[this._entrys.length] = entry; } else { this._entrys[index].value = value; } }; this.get = function (key) { var index = this._getIndex(key); return (index != -1) ? this._entrys[index].value : null; }; this.remove = function (key) { var index = this._getIndex(key); if (index != -1) { this._entrys.splice(index, 1); } }; this.clear = function () { this._entrys.length = 0; }; this.contains = function (key) { var index = this._getIndex(key); return (index != -1) ? true : false; }; this.size = function () { return this._entrys.length; }; this.getEntrys = function () { return this._entrys; }; this._getIndex = function (key) { if (key == null || key == undefined) { return -1; } var _length = this._entrys.length; for (var i = 0; i < _length; i++) { var entry = this._entrys[i]; if (entry == null || entry == undefined) { continue; } if (entry.key === key) {// equal return i; } } return -1; }; } var roomTableMap = new ZeroRTCMap(); function Client(uid, conn, roomId) { this.uid = uid; // 用户所属的id this.conn = conn; // uid对应的websocket连接 this.roomId = roomId; } 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 + " 已经有两人存在,请使用其他房间"); // 加信令通知客户端,房间已满 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 = { 'cmd':SIGNAL_TYPE_RESP_JOIN, 'remoteUid': remoteUid }; msg = JSON.stringify(jsonMsg); console.info("resp-join: " + msg); conn.sendText(msg); } } } return client; } function handleLeave(message) { var roomId = message.roomId; var uid = message.uid; var roomMap = roomTableMap.get(roomId); if (roomMap == null) { console.error("handleLeave can't find then roomId " + roomId); return; } if (!roomMap.contains(uid)) { console.info("uid: " + uid +" have leave roomId " + roomId); return; } console.info("uid: " + uid + " 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); } } } } 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); } } } } 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); } } 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("处理Candidate 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); } } // 创建监听9001端口webSocket服务 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; } }); conn.on("close", function(code, reason) { console.info("连接关闭 code: " + code + ", reason: " + reason); if(conn.client != null) { // 强制让客户端从房间退出 handleForceLeave(conn.client); } }); conn.on("error", function(err) { console.info("监听到错误:" + err); }); }).listen(prort);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。