Здравствуйте, я пытаюсь сделать многопользовательский чат с использованием WebRTC. Все должно работать примерно так:
- Новый пользователь, используя WebSocket, отправляет широковещательное сообщение, так самым как бы анонсируя себя.
- Другие пользователи должны отправить новому SDP offer.
- Новый пользователь должен отправить SDP answer тем, от кого он получил offer.
Когда два пользователя подключены друг к другу, проблем не возникает. Когда же третий пользователь пытается подключиться, он отправляет анонс, два предыдущих пользователя отправляют ему offer, он отправляет им answer, но канал почему-то открывается только с одним из них. Помогите, пожалуйста, понять, что я делаю не так.
HTML + JS:
<html>
<head>
<title>WebRTC Chat Demo</title>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<textarea id="message"></textarea><button onclick="connect();">Connect</button><button onclick="sendMessage();">Send</button>
<div id="chatlog"></div>
<script>
var PeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
var SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
var IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
function id () {
return (Math.random() * 10000 + 10000 | 0).toString();
}
var ROOM = location.hash.substr(1);
// no room number specified, so create one
// which makes us the offerer
if (!ROOM) {
ROOM = id();
document.write("<a href='#"+ROOM+"'>Send link to other peer</a>");
}
var ME = id();
var socket = io.connect('', {port: 3000});
socket.on('webrtc', socketReceived);
socket.on('new', socketNewPeer);
socket.emit('room', JSON.stringify({id: ME, room: ROOM}));
// get references to the document tags
var chatlog = document.getElementById("chatlog");
var message = document.getElementById("message");
// options for the PeerConnection
var server = {
iceServers: [
{url: "stun:23.21.150.121"},
{url: "stun:stun.l.google.com:19302"},
{url: "turn:numb.viagenie.ca", credential: "webrtcdemo", username: "******"}
]
};
var options = {
optional: [
{DtlsSrtpKeyAgreement: true},
{RtpDataChannels: true}
]
}
var peers = {};
function bindEvents (channel) {
channel.onopen = function () { console.log("Channel Open"); }
channel.onmessage = function (e) {
console.log(e);
chatlog.innerHTML += "<div>Peer says: " + e.data + "</div>";
};
}
function sendMessage () {
var msg = message.value;
for (var peer in peers) {
if (peers.hasOwnProperty(peer)) {
if (peers[peer].channel !== undefined) {
peers[peer].channel.send(msg);
}
}
}
message.value = "";
}
function sendViaSocket(type, message, to) {
socket.emit("webrtc", JSON.stringify({id: ME, to: to, type: type, data: message}));
}
function socketNewPeer(data) {
peers[data] = {
candidateCache: []
};
var pc = new PeerConnection(server, options);
pc.onicecandidate = function(event) {
if (event.candidate) {
peers[data].candidateCache.push(event.candidate);
}
if (!event.candidate) {
sendViaSocket('offer', pc.localDescription, data);
for (var i = 0; i < peers[data].candidateCache.length; i++) {
sendViaSocket('candidate', peers[data].candidateCache[i], data);
}
}
};
peers[data].connection = pc;
var channel = pc.createDataChannel('mychannel', {});
channel.owner = data;
peers[data].channel = channel;
bindEvents(channel);
pc.createOffer(function(offer) {
pc.setLocalDescription(offer);
});
}
function socketReceived(data) {
var json = JSON.parse(data);
switch (json.type) {
case "candidate":
remoteCandidateReceived(json.id, json.data);
break;
case "offer":
remoteOfferReceived(json.id, json.data);
break;
case "answer":
remoteAnswerReceived(json.id, json.data);
break;
}
}
function createConnection(id) {
if (peers[id] === undefined) {
peers[id] = {
candidateCache: []
};
pc = new PeerConnection(server, options);
pc.onicecandidate = function(event) {
if (event.candidate) {
peers[id].candidateCache.push(event.candidate);
}
if (!event.candidate) {
sendViaSocket('answer', pc.localDescription, id);
for (var i = 0; i < peers[id].candidateCache.length; i++) {
sendViaSocket('candidate', peers[id].candidateCache[i], id);
}
}
};
peers[id].connection = pc;
pc.ondatachannel = function(e) {
peers[id].channel = e.channel;
peers[id].channel.owner = id;
bindEvents(peers[id].channel);
}
}
}
function remoteCandidateReceived(id, data) {
createConnection(id);
var pc = peers[id].connection;
pc.addIceCandidate(new IceCandidate(data));
console.log('CANDIDATE RECEIVED');
}
function remoteOfferReceived(id, data) {
createConnection(id);
var pc = peers[id].connection;
pc.setRemoteDescription(new SessionDescription(data));
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer);
});
console.log('OFFER RECEIVED');
}
function remoteAnswerReceived(id, data) {
createConnection(id);
var pc = peers[id].connection;
pc.setRemoteDescription(new SessionDescription(data));
console.log('ANSWER RECEIVED');
}
</script>
</body>
</html>
Сигнальный сервер:
var users = {};
var io = socket_io(server);
io.on('connection', function(socket) {
socket.on('room', function(message) {
console.log(message);
var json = JSON.parse(message);
users[json.id] = socket;
if (socket.room !== undefined) {
socket.leave(socket.room);
}
socket.room = json.room;
socket.join(socket.room);
socket.broadcast.to(socket.room).emit('new', json.id);
});
socket.on('webrtc', function(message) {
console.log(message);
var json = JSON.parse(message);
if (json.to !== undefined && users[json.to] !== undefined) {
users[json.to].emit('webrtc', message);
} else {
socket.broadcast.to(socket.room).emit('webrtc', message);
}
});
});