Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • sweng-group-15/liowebrtc
1 result
Show changes
Commits on Source (6)
...@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", { ...@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
value: true value: true
}); });
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _wildemitter = require('wildemitter'); var _wildemitter = require('wildemitter');
...@@ -104,25 +106,31 @@ var LioWebRTC = function (_WildEmitter) { ...@@ -104,25 +106,31 @@ var LioWebRTC = function (_WildEmitter) {
var peers = self.webrtc.getPeers(message.from, message.roomType); var peers = self.webrtc.getPeers(message.from, message.roomType);
var totalPeers = self.webrtc.getPeers().length; var totalPeers = self.webrtc.getPeers().length;
var peer = void 0; var peer = void 0;
if (message.type === 'offer') { if (message.type === 'offer') {
if (peers.length) { if (peers.length) {
peers.forEach(function (p) { peers.forEach(function (p) {
if (p.sid === message.sid) peer = p; if (p.sid === message.sid) peer = p;
}); });
// if (!peer) peer = peers[0]; // fallback for old protocol versions peers.forEach(function (p) {
if (p !== peer) {
p.end(false);
totalPeers--;
}
});
} }
if (_this.config.dataOnly && _this.config.constraints.maxPeers > 0 && totalPeers >= _this.config.constraints.maxPeers) { if (_this.config.dataOnly && _this.config.constraints.maxPeers > 0 && totalPeers >= _this.config.constraints.maxPeers) {
return; return;
} }
if (!peer) { if (!peer) {
console.log('Received offer from', message.from);
peer = self.webrtc.createPeer({ peer = self.webrtc.createPeer({
id: message.from, id: message.from,
sid: message.sid, sid: message.sid,
type: message.roomType, type: message.roomType,
enableDataChannels: self.config.enableDataChannels, enableDataChannels: self.config.enableDataChannels,
sharemyscreen: message.roomType === 'screen' && !message.broadcaster, sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null,
iceServers: self.webrtc.config.peerConnectionConfig.iceServers.concat(message.turnservers)
}); });
if (_this.config.dataOnly && _this.config.constraints.maxPeers > 0) { if (_this.config.dataOnly && _this.config.constraints.maxPeers > 0) {
_this.sendPing(peer, peer.id, true); _this.sendPing(peer, peer.id, true);
...@@ -130,10 +138,10 @@ var LioWebRTC = function (_WildEmitter) { ...@@ -130,10 +138,10 @@ var LioWebRTC = function (_WildEmitter) {
peer.start(); peer.start();
_this.emit('createdPeer', peer); _this.emit('createdPeer', peer);
} }
peer.handleMessage(message);
} else { } else {
return; return;
} }
peer.handleMessage(message);
} else if (peers.length) { } else if (peers.length) {
peers.forEach(function (p) { peers.forEach(function (p) {
p.handleMessage(message); p.handleMessage(message);
...@@ -188,19 +196,17 @@ var LioWebRTC = function (_WildEmitter) { ...@@ -188,19 +196,17 @@ var LioWebRTC = function (_WildEmitter) {
self.webrtc.config.peerConnectionConfig.iceServers = args; self.webrtc.config.peerConnectionConfig.iceServers = args;
self.emit('stunservers', args); self.emit('stunservers', args);
}); });
connection.on('turnservers', function (args) { /*connection.on('turnservers', (args) => {
// appends to the config // appends to the config
self.webrtc.config.peerConnectionConfig.iceServers = self.webrtc.config.peerConnectionConfig.iceServers.concat(args); self.webrtc.config.peerConnectionConfig.iceServers = self.webrtc.config.peerConnectionConfig.iceServers.concat(args);
self.emit('turnservers', args); self.emit('turnservers', args);
}); });*/
/* /*this.webrtc.on('iceFailed', (peer) => {
this.webrtc.on('iceFailed', (peer) => {
// local ice failure // local ice failure
}); });
this.webrtc.on('connectivityError', (peer) => { this.webrtc.on('connectivityError', (peer) => {
// remote ice failure // remote ice failure
}); });*/
*/
// sending mute/unmute to all peers // sending mute/unmute to all peers
_this.webrtc.on('audioOn', function () { _this.webrtc.on('audioOn', function () {
...@@ -350,6 +356,8 @@ var LioWebRTC = function (_WildEmitter) { ...@@ -350,6 +356,8 @@ var LioWebRTC = function (_WildEmitter) {
value: function connectToRandomPeer() { value: function connectToRandomPeer() {
var _this2 = this; var _this2 = this;
if (!this.connection) return;
this.getClients(function (err, clients) { this.getClients(function (err, clients) {
var ids = Object.keys(clients).filter(function (c) { var ids = Object.keys(clients).filter(function (c) {
return !(_this2.unconnectivePeers[c] === true || c === _this2.id || (0, _PeerOptimizer.isNeighbor)(_this2.id, c)); return !(_this2.unconnectivePeers[c] === true || c === _this2.id || (0, _PeerOptimizer.isNeighbor)(_this2.id, c));
...@@ -413,6 +421,8 @@ var LioWebRTC = function (_WildEmitter) { ...@@ -413,6 +421,8 @@ var LioWebRTC = function (_WildEmitter) {
}, { }, {
key: 'disconnect', key: 'disconnect',
value: function disconnect() { value: function disconnect() {
if (!this.connection) return;
this.connection.disconnect(); this.connection.disconnect();
delete this.connection; delete this.connection;
} }
...@@ -490,7 +500,6 @@ var LioWebRTC = function (_WildEmitter) { ...@@ -490,7 +500,6 @@ var LioWebRTC = function (_WildEmitter) {
var peer = void 0; var peer = void 0;
_this5.roomCount = Object.keys(roomDescription.clients).length; _this5.roomCount = Object.keys(roomDescription.clients).length;
// console.log(roomDescription);
_this5.id = roomDescription.you; _this5.id = roomDescription.you;
(0, _PeerOptimizer.addNode)(_this5.id); (0, _PeerOptimizer.addNode)(_this5.id);
_this5.unconnectivePeers[_this5.id] = true; _this5.unconnectivePeers[_this5.id] = true;
...@@ -506,26 +515,50 @@ var LioWebRTC = function (_WildEmitter) { ...@@ -506,26 +515,50 @@ var LioWebRTC = function (_WildEmitter) {
client = roomDescription.clients[id]; client = roomDescription.clients[id];
for (type in client) { for (type in client) {
if (client[type]) { if (type !== 'turnservers' && client[type]) {
var peerCount = _this5.webrtc.getPeers().length; var _ret = function () {
if (_this5.config.dataOnly && _this5.config.constraints.maxPeers > 0 && (peerCount >= _this5.config.constraints.minPeers || peerCount >= _this5.config.constraints.maxPeers)) { console.log('Received peer info about', id, 'on room join');
break; var peerCount = _this5.webrtc.getPeers().length;
} var peers = self.webrtc.getPeers(id, type);
peer = self.webrtc.createPeer({ var backoff = false;
id: id, peers.forEach(function (p) {
type: type, console.log('Peer with same ID in state', p.pc.pc.iceConnectionState, 'found');
enableDataChannels: self.config.enableDataChannels && type !== 'screen', if (p.pc.pc.iceConnectionState == 'new' || p.pc.pc.iceConnectionState == 'checking') {
receiveMedia: { backoff = true;
offerToReceiveAudio: type !== 'screen' && !_this5.config.dataOnly && _this5.config.receiveMedia.offerToReceiveAudio ? 1 : 0, return;
offerToReceiveVideo: !_this5.config.dataOnly && self.config.receiveMedia.offerToReceiveVideo ? 1 : 0 }
p.end(false);
peerCount--;
});
if (backoff) {
return {
v: void 0
};
}
if (_this5.config.dataOnly && _this5.config.constraints.maxPeers > 0 && (peerCount >= _this5.config.constraints.minPeers || peerCount >= _this5.config.constraints.maxPeers)) {
return {
v: void 0
};
}
peer = self.webrtc.createPeer({
id: id,
type: type,
enableDataChannels: self.config.enableDataChannels && type !== 'screen',
receiveMedia: {
offerToReceiveAudio: type !== 'screen' && !_this5.config.dataOnly && _this5.config.receiveMedia.offerToReceiveAudio ? 1 : 0,
offerToReceiveVideo: !_this5.config.dataOnly && self.config.receiveMedia.offerToReceiveVideo ? 1 : 0
},
iceServers: self.webrtc.config.peerConnectionConfig.iceServers.concat(client.turnservers)
});
if (_this5.config.dataOnly && _this5.config.constraints.maxPeers > 0) {
_this5.sendPing(peer, peer.id, true);
} else {
peer.start();
_this5.emit('createdPeer', peer);
} }
}); }();
if (_this5.config.dataOnly && _this5.config.constraints.maxPeers > 0) {
_this5.sendPing(peer, peer.id, true); if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
} else {
peer.start();
_this5.emit('createdPeer', peer);
}
} }
} }
} }
...@@ -605,29 +638,55 @@ var LioWebRTC = function (_WildEmitter) { ...@@ -605,29 +638,55 @@ var LioWebRTC = function (_WildEmitter) {
}, { }, {
key: 'connectToPeer', key: 'connectToPeer',
value: function connectToPeer(peerId, client) { value: function connectToPeer(peerId, client) {
var _this7 = this;
var type = void 0; var type = void 0;
var peer = void 0; var peer = void 0;
for (type in client) { for (type in client) {
if (client[type]) { if (type !== 'turnservers' && client[type]) {
var peerCount = this.webrtc.getPeers().length; var _ret2 = function () {
if (this.config.constraints.maxPeers > 0 && peerCount >= this.config.constraints.maxPeers) { console.log('Connecting to peer', peerId);
break; var peerCount = _this7.webrtc.getPeers().length;
} var peers = _this7.webrtc.getPeers(peerId, type);
peer = this.webrtc.createPeer({ var backoff = false;
id: peerId, peers.forEach(function (p) {
type: type, console.log('Peer with same ID in state', p.pc.pc.iceConnectionState, 'found');
enableDataChannels: this.config.enableDataChannels && type !== 'screen', if (p.pc.pc.iceConnectionState == 'new' || p.pc.pc.iceConnectionState == 'checking') {
receiveMedia: { backoff = true;
offerToReceiveAudio: type !== 'screen' && !this.config.dataOnly && this.config.receiveMedia.offerToReceiveAudio ? 1 : 0, return;
offerToReceiveVideo: !this.config.dataOnly && this.config.receiveMedia.offerToReceiveVideo ? 1 : 0 }
p.end(false);
peerCount--;
});
if (backoff) {
return {
v: void 0
};
} }
}); if (_this7.config.constraints.maxPeers > 0 && peerCount >= _this7.config.constraints.maxPeers) {
if (this.config.dataOnly && this.config.constraints.maxPeers > 0) { return {
this.sendPing(peer, peerId, true); v: void 0
} else { };
peer.start(); }
this.emit('createdPeer', peer); peer = _this7.webrtc.createPeer({
} id: peerId,
type: type,
enableDataChannels: _this7.config.enableDataChannels && type !== 'screen',
receiveMedia: {
offerToReceiveAudio: type !== 'screen' && !_this7.config.dataOnly && _this7.config.receiveMedia.offerToReceiveAudio ? 1 : 0,
offerToReceiveVideo: !_this7.config.dataOnly && _this7.config.receiveMedia.offerToReceiveVideo ? 1 : 0
},
iceServers: _this7.webrtc.config.peerConnectionConfig.iceServers.concat(client.turnservers)
});
if (_this7.config.dataOnly && _this7.config.constraints.maxPeers > 0) {
_this7.sendPing(peer, peerId, true);
} else {
peer.start();
_this7.emit('createdPeer', peer);
}
}();
if ((typeof _ret2 === 'undefined' ? 'undefined' : _typeof(_ret2)) === "object") return _ret2.v;
} }
} }
} }
......
...@@ -22,6 +22,10 @@ var _webrtcsupport = require('./webrtcsupport'); ...@@ -22,6 +22,10 @@ var _webrtcsupport = require('./webrtcsupport');
var _webrtcsupport2 = _interopRequireDefault(_webrtcsupport); var _webrtcsupport2 = _interopRequireDefault(_webrtcsupport);
var _whatThePack = require('what-the-pack');
var _whatThePack2 = _interopRequireDefault(_whatThePack);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
...@@ -30,6 +34,10 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen ...@@ -30,6 +34,10 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _MessagePack$initiali = _whatThePack2.default.initialize(Math.pow(2, 22)),
encode = _MessagePack$initiali.encode,
decode = _MessagePack$initiali.decode;
function isAllTracksEnded(stream) { function isAllTracksEnded(stream) {
var isAllTracksEnded = true; var isAllTracksEnded = true;
stream.getTracks().forEach(function (t) { stream.getTracks().forEach(function (t) {
...@@ -38,6 +46,11 @@ function isAllTracksEnded(stream) { ...@@ -38,6 +46,11 @@ function isAllTracksEnded(stream) {
return isAllTracksEnded; return isAllTracksEnded;
} }
var protoSend = RTCDataChannel.prototype.send;
RTCDataChannel.prototype.send = function (data) {
protoSend.apply(this, [encode(data)]);
};
var Peer = function (_WildEmitter) { var Peer = function (_WildEmitter) {
_inherits(Peer, _WildEmitter); _inherits(Peer, _WildEmitter);
...@@ -59,7 +72,7 @@ var Peer = function (_WildEmitter) { ...@@ -59,7 +72,7 @@ var Peer = function (_WildEmitter) {
_this.channels = {}; _this.channels = {};
_this.sid = options.sid || Date.now().toString(); _this.sid = options.sid || Date.now().toString();
// Create an RTCPeerConnection via the polyfill // Create an RTCPeerConnection via the polyfill
_this.pc = new _rtcpeerconnection2.default(_this.parent.config.peerConnectionConfig, _this.parent.config.peerConnectionConstraints); _this.pc = new _rtcpeerconnection2.default(Object.assign({}, _this.parent.config.peerConnectionConfig, { iceServers: options.iceServers || _this.parent.config.peerConnectionConfig.iceServers }), _this.parent.config.peerConnectionConstraints);
_this.pc.on('ice', _this.onIceCandidate.bind(_this)); _this.pc.on('ice', _this.onIceCandidate.bind(_this));
_this.pc.on('endOfCandidates', function (event) { _this.pc.on('endOfCandidates', function (event) {
self.send('endOfCandidates', event); self.send('endOfCandidates', event);
...@@ -203,7 +216,7 @@ var Peer = function (_WildEmitter) { ...@@ -203,7 +216,7 @@ var Peer = function (_WildEmitter) {
this.logger.log('sending via datachannel', channel, messageType, message); this.logger.log('sending via datachannel', channel, messageType, message);
var dc = this.getDataChannel(channel); var dc = this.getDataChannel(channel);
if (dc.readyState !== 'open') return false; if (dc.readyState !== 'open') return false;
dc.send(JSON.stringify(message)); dc.send(message);
return true; return true;
} }
...@@ -213,10 +226,11 @@ var Peer = function (_WildEmitter) { ...@@ -213,10 +226,11 @@ var Peer = function (_WildEmitter) {
key: '_observeDataChannel', key: '_observeDataChannel',
value: function _observeDataChannel(channel, peer) { value: function _observeDataChannel(channel, peer) {
var self = this; var self = this;
channel.binaryType = 'arraybuffer';
channel.onclose = this.emit.bind(this, 'channelClose', channel, peer); channel.onclose = this.emit.bind(this, 'channelClose', channel, peer);
channel.onerror = this.emit.bind(this, 'channelError', channel, peer); channel.onerror = this.emit.bind(this, 'channelError', channel, peer);
channel.onmessage = function (event) { channel.onmessage = function (event) {
self.emit('channelMessage', self, channel.label, JSON.parse(event.data), channel, event); self.emit('channelMessage', self, channel.label, decode(_whatThePack2.default.Buffer.from(event.data)), channel, event);
}; };
channel.onopen = this.emit.bind(this, 'channelOpen', channel, peer); channel.onopen = this.emit.bind(this, 'channelOpen', channel, peer);
} }
...@@ -282,11 +296,11 @@ var Peer = function (_WildEmitter) { ...@@ -282,11 +296,11 @@ var Peer = function (_WildEmitter) {
var emitRemoval = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; var emitRemoval = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
if (this.closed) return; if (this.closed) return;
this.pc.close();
this.handleStreamRemoved(emitRemoval);
if (emitRemoval) { if (emitRemoval) {
this.parent.emit('removedPeer', this); this.parent.emit('removedPeer', this);
} }
this.pc.close();
this.handleStreamRemoved(emitRemoval);
} }
}, { }, {
key: 'handleRemoteStreamAdded', key: 'handleRemoteStreamAdded',
...@@ -335,10 +349,10 @@ var Peer = function (_WildEmitter) { ...@@ -335,10 +349,10 @@ var Peer = function (_WildEmitter) {
}); });
// override onopen // override onopen
dc.onopen = function () { dc.onopen = function () {
dc.send(JSON.stringify({ dc.send({
size: file.size, size: file.size,
name: file.name name: file.name
})); });
sender.send(file, dc); sender.send(file, dc);
}; };
// override onclose // override onclose
...@@ -348,6 +362,12 @@ var Peer = function (_WildEmitter) { ...@@ -348,6 +362,12 @@ var Peer = function (_WildEmitter) {
}; };
return sender; return sender;
} }
}, {
key: 'getStats',
value: function getStats(selector) {
// TODO: Use adapter.js to patch this across browsers
return this.pc.pc.getStats(selector);
}
}]); }]);
return Peer; return Peer;
......
...@@ -47,7 +47,7 @@ var WebRTC = function (_LocalMedia) { ...@@ -47,7 +47,7 @@ var WebRTC = function (_LocalMedia) {
var config = _this.config = { var config = _this.config = {
debug: false, debug: false,
peerConnectionConfig: { peerConnectionConfig: {
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] iceServers: []
}, },
peerConnectionConstraints: { peerConnectionConstraints: {
optional: [] optional: []
...@@ -96,7 +96,7 @@ var WebRTC = function (_LocalMedia) { ...@@ -96,7 +96,7 @@ var WebRTC = function (_LocalMedia) {
if (peer.enableDataChannels) { if (peer.enableDataChannels) {
var dc = peer.getDataChannel('liowebrtc'); var dc = peer.getDataChannel('liowebrtc');
if (dc.readyState !== 'open') return; if (dc.readyState !== 'open') return;
dc.sendDirectlyToAll(JSON.stringify({ type: 'speaking' })); dc.sendDirectlyToAll({ type: 'speaking' });
} }
}); });
} }
...@@ -107,7 +107,7 @@ var WebRTC = function (_LocalMedia) { ...@@ -107,7 +107,7 @@ var WebRTC = function (_LocalMedia) {
if (peer.enableDataChannels) { if (peer.enableDataChannels) {
var dc = peer.getDataChannel('liowebrtc'); var dc = peer.getDataChannel('liowebrtc');
if (dc.readyState !== 'open') return; if (dc.readyState !== 'open') return;
dc.sendDirectlyToAll(JSON.stringify({ type: 'stoppedSpeaking' })); dc.sendDirectlyToAll({ type: 'stoppedSpeaking' });
} }
}); });
} }
...@@ -118,7 +118,7 @@ var WebRTC = function (_LocalMedia) { ...@@ -118,7 +118,7 @@ var WebRTC = function (_LocalMedia) {
if (peer.enableDataChannels) { if (peer.enableDataChannels) {
var dc = peer.getDataChannel('liowebrtc'); var dc = peer.getDataChannel('liowebrtc');
if (dc.readyState !== 'open') return; if (dc.readyState !== 'open') return;
dc.sendDirectlyToAll(JSON.stringify({ type: 'payload', volume: volume })); dc.sendDirectlyToAll({ type: 'payload', volume: volume });
} }
}); });
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
"rtcpeerconnection": "file:../rtcpeerconnection", "rtcpeerconnection": "file:../rtcpeerconnection",
"socket.io-client": "^2.3.0", "socket.io-client": "^2.3.0",
"webrtc-adapter": "^7.3.0", "webrtc-adapter": "^7.3.0",
"what-the-pack": "^2.0.3",
"wildemitter": "^1.2.0" "wildemitter": "^1.2.0"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -60,20 +60,25 @@ class LioWebRTC extends WildEmitter { ...@@ -60,20 +60,25 @@ class LioWebRTC extends WildEmitter {
connection.on('message', (message) => { connection.on('message', (message) => {
const peers = self.webrtc.getPeers(message.from, message.roomType); const peers = self.webrtc.getPeers(message.from, message.roomType);
const totalPeers = self.webrtc.getPeers().length; let totalPeers = self.webrtc.getPeers().length;
let peer; let peer;
if (message.type === 'offer') { if (message.type === 'offer') {
if (peers.length) { if (peers.length) {
peers.forEach((p) => { peers.forEach((p) => {
if (p.sid === message.sid) peer = p; if (p.sid === message.sid) peer = p;
}); });
// if (!peer) peer = peers[0]; // fallback for old protocol versions peers.forEach((p) => {
if (p !== peer) {
p.end(false);
totalPeers--;
}
});
} }
if (this.config.dataOnly && this.config.constraints.maxPeers > 0 && totalPeers >= this.config.constraints.maxPeers) { if (this.config.dataOnly && this.config.constraints.maxPeers > 0 && totalPeers >= this.config.constraints.maxPeers) {
return; return;
} }
if (!peer) { if (!peer) {
console.log('Received offer from', message.from)
peer = self.webrtc.createPeer({ peer = self.webrtc.createPeer({
id: message.from, id: message.from,
sid: message.sid, sid: message.sid,
...@@ -81,6 +86,7 @@ class LioWebRTC extends WildEmitter { ...@@ -81,6 +86,7 @@ class LioWebRTC extends WildEmitter {
enableDataChannels: self.config.enableDataChannels, enableDataChannels: self.config.enableDataChannels,
sharemyscreen: message.roomType === 'screen' && !message.broadcaster, sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null, broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null,
iceServers: self.webrtc.config.peerConnectionConfig.iceServers.concat(message.turnservers),
}); });
if (this.config.dataOnly && this.config.constraints.maxPeers > 0) { if (this.config.dataOnly && this.config.constraints.maxPeers > 0) {
this.sendPing(peer, peer.id, true); this.sendPing(peer, peer.id, true);
...@@ -88,10 +94,10 @@ class LioWebRTC extends WildEmitter { ...@@ -88,10 +94,10 @@ class LioWebRTC extends WildEmitter {
peer.start(); peer.start();
this.emit('createdPeer', peer); this.emit('createdPeer', peer);
} }
peer.handleMessage(message);
} else { } else {
return; return;
} }
peer.handleMessage(message);
} else if (peers.length) { } else if (peers.length) {
peers.forEach((p) => { peers.forEach((p) => {
p.handleMessage(message); p.handleMessage(message);
...@@ -145,19 +151,17 @@ class LioWebRTC extends WildEmitter { ...@@ -145,19 +151,17 @@ class LioWebRTC extends WildEmitter {
self.webrtc.config.peerConnectionConfig.iceServers = args; self.webrtc.config.peerConnectionConfig.iceServers = args;
self.emit('stunservers', args); self.emit('stunservers', args);
}); });
connection.on('turnservers', (args) => { /*connection.on('turnservers', (args) => {
// appends to the config // appends to the config
self.webrtc.config.peerConnectionConfig.iceServers = self.webrtc.config.peerConnectionConfig.iceServers.concat(args); self.webrtc.config.peerConnectionConfig.iceServers = self.webrtc.config.peerConnectionConfig.iceServers.concat(args);
self.emit('turnservers', args); self.emit('turnservers', args);
}); });*/
/* /*this.webrtc.on('iceFailed', (peer) => {
this.webrtc.on('iceFailed', (peer) => {
// local ice failure // local ice failure
}); });
this.webrtc.on('connectivityError', (peer) => { this.webrtc.on('connectivityError', (peer) => {
// remote ice failure // remote ice failure
}); });*/
*/
// sending mute/unmute to all peers // sending mute/unmute to all peers
this.webrtc.on('audioOn', () => { this.webrtc.on('audioOn', () => {
...@@ -291,6 +295,9 @@ class LioWebRTC extends WildEmitter { ...@@ -291,6 +295,9 @@ class LioWebRTC extends WildEmitter {
} }
connectToRandomPeer() { connectToRandomPeer() {
if (!this.connection)
return;
this.getClients((err, clients) => { this.getClients((err, clients) => {
const ids = Object.keys(clients).filter(c => !(this.unconnectivePeers[c] === true || c === this.id || isNeighbor(this.id, c))); const ids = Object.keys(clients).filter(c => !(this.unconnectivePeers[c] === true || c === this.id || isNeighbor(this.id, c)));
if (ids.length) { if (ids.length) {
...@@ -340,6 +347,9 @@ class LioWebRTC extends WildEmitter { ...@@ -340,6 +347,9 @@ class LioWebRTC extends WildEmitter {
} }
disconnect() { disconnect() {
if (!this.connection)
return;
this.connection.disconnect(); this.connection.disconnect();
delete this.connection; delete this.connection;
} }
...@@ -404,17 +414,31 @@ class LioWebRTC extends WildEmitter { ...@@ -404,17 +414,31 @@ class LioWebRTC extends WildEmitter {
let peer; let peer;
this.roomCount = Object.keys(roomDescription.clients).length; this.roomCount = Object.keys(roomDescription.clients).length;
// console.log(roomDescription);
this.id = roomDescription.you; this.id = roomDescription.you;
addNode(this.id); addNode(this.id);
this.unconnectivePeers[this.id] = true; this.unconnectivePeers[this.id] = true;
for (id of Object.keys(roomDescription.clients).reverse().filter(item => item !== this.id)) { for (id of Object.keys(roomDescription.clients).reverse().filter(item => item !== this.id)) {
client = roomDescription.clients[id]; client = roomDescription.clients[id];
for (type in client) { for (type in client) {
if (client[type]) { if (type !== 'turnservers' && client[type]) {
const peerCount = this.webrtc.getPeers().length; console.log('Received peer info about', id, 'on room join')
let peerCount = this.webrtc.getPeers().length;
const peers = self.webrtc.getPeers(id, type);
let backoff = false
peers.forEach((p) => {
console.log('Peer with same ID in state', p.pc.pc.iceConnectionState, 'found')
if (p.pc.pc.iceConnectionState == 'new' || p.pc.pc.iceConnectionState == 'checking') {
backoff = true;
return;
}
p.end(false);
peerCount--;
});
if (backoff) {
return;
}
if (this.config.dataOnly && this.config.constraints.maxPeers > 0 && (peerCount >= this.config.constraints.minPeers || peerCount >= this.config.constraints.maxPeers)) { if (this.config.dataOnly && this.config.constraints.maxPeers > 0 && (peerCount >= this.config.constraints.minPeers || peerCount >= this.config.constraints.maxPeers)) {
break; return;
} }
peer = self.webrtc.createPeer({ peer = self.webrtc.createPeer({
id, id,
...@@ -424,6 +448,7 @@ class LioWebRTC extends WildEmitter { ...@@ -424,6 +448,7 @@ class LioWebRTC extends WildEmitter {
offerToReceiveAudio: type !== 'screen' && !this.config.dataOnly && this.config.receiveMedia.offerToReceiveAudio ? 1 : 0, offerToReceiveAudio: type !== 'screen' && !this.config.dataOnly && this.config.receiveMedia.offerToReceiveAudio ? 1 : 0,
offerToReceiveVideo: !this.config.dataOnly && self.config.receiveMedia.offerToReceiveVideo ? 1 : 0, offerToReceiveVideo: !this.config.dataOnly && self.config.receiveMedia.offerToReceiveVideo ? 1 : 0,
}, },
iceServers: self.webrtc.config.peerConnectionConfig.iceServers.concat(client.turnservers),
}); });
if (this.config.dataOnly && this.config.constraints.maxPeers > 0) { if (this.config.dataOnly && this.config.constraints.maxPeers > 0) {
this.sendPing(peer, peer.id, true); this.sendPing(peer, peer.id, true);
...@@ -489,10 +514,25 @@ class LioWebRTC extends WildEmitter { ...@@ -489,10 +514,25 @@ class LioWebRTC extends WildEmitter {
let type; let type;
let peer; let peer;
for (type in client) { for (type in client) {
if (client[type]) { if (type !== 'turnservers' && client[type]) {
const peerCount = this.webrtc.getPeers().length; console.log('Connecting to peer', peerId)
let peerCount = this.webrtc.getPeers().length;
const peers = this.webrtc.getPeers(peerId, type);
let backoff = false
peers.forEach((p) => {
console.log('Peer with same ID in state', p.pc.pc.iceConnectionState, 'found')
if (p.pc.pc.iceConnectionState == 'new' || p.pc.pc.iceConnectionState == 'checking') {
backoff = true;
return;
}
p.end(false);
peerCount--;
});
if (backoff) {
return;
}
if (this.config.constraints.maxPeers > 0 && peerCount >= this.config.constraints.maxPeers) { if (this.config.constraints.maxPeers > 0 && peerCount >= this.config.constraints.maxPeers) {
break; return;
} }
peer = this.webrtc.createPeer({ peer = this.webrtc.createPeer({
id: peerId, id: peerId,
...@@ -502,6 +542,7 @@ class LioWebRTC extends WildEmitter { ...@@ -502,6 +542,7 @@ class LioWebRTC extends WildEmitter {
offerToReceiveAudio: type !== 'screen' && !this.config.dataOnly && this.config.receiveMedia.offerToReceiveAudio ? 1 : 0, offerToReceiveAudio: type !== 'screen' && !this.config.dataOnly && this.config.receiveMedia.offerToReceiveAudio ? 1 : 0,
offerToReceiveVideo: !this.config.dataOnly && this.config.receiveMedia.offerToReceiveVideo ? 1 : 0, offerToReceiveVideo: !this.config.dataOnly && this.config.receiveMedia.offerToReceiveVideo ? 1 : 0,
}, },
iceServers: this.webrtc.config.peerConnectionConfig.iceServers.concat(client.turnservers),
}); });
if (this.config.dataOnly && this.config.constraints.maxPeers > 0) { if (this.config.dataOnly && this.config.constraints.maxPeers > 0) {
this.sendPing(peer, peerId, true); this.sendPing(peer, peerId, true);
......
...@@ -2,6 +2,9 @@ import PeerConnection from 'rtcpeerconnection'; ...@@ -2,6 +2,9 @@ import PeerConnection from 'rtcpeerconnection';
import WildEmitter from 'wildemitter'; import WildEmitter from 'wildemitter';
import FileTransfer from 'filetransfer'; import FileTransfer from 'filetransfer';
import webrtcSupport from './webrtcsupport'; import webrtcSupport from './webrtcsupport';
import MessagePack from 'what-the-pack';
const { encode, decode } = MessagePack.initialize(2**22);
function isAllTracksEnded(stream) { function isAllTracksEnded(stream) {
let isAllTracksEnded = true; let isAllTracksEnded = true;
...@@ -11,6 +14,11 @@ function isAllTracksEnded(stream) { ...@@ -11,6 +14,11 @@ function isAllTracksEnded(stream) {
return isAllTracksEnded; return isAllTracksEnded;
} }
const protoSend = RTCDataChannel.prototype.send;
RTCDataChannel.prototype.send = function (data) {
protoSend.apply(this, [encode(data)])
};
class Peer extends WildEmitter { class Peer extends WildEmitter {
constructor(options) { constructor(options) {
super(); super();
...@@ -27,7 +35,9 @@ class Peer extends WildEmitter { ...@@ -27,7 +35,9 @@ class Peer extends WildEmitter {
this.channels = {}; this.channels = {};
this.sid = options.sid || Date.now().toString(); this.sid = options.sid || Date.now().toString();
// Create an RTCPeerConnection via the polyfill // Create an RTCPeerConnection via the polyfill
this.pc = new PeerConnection(this.parent.config.peerConnectionConfig, this.parent.config.peerConnectionConstraints); this.pc = new PeerConnection(Object.assign({}, this.parent.config.peerConnectionConfig,
{ iceServers: options.iceServers || this.parent.config.peerConnectionConfig.iceServers }),
this.parent.config.peerConnectionConstraints);
this.pc.on('ice', this.onIceCandidate.bind(this)); this.pc.on('ice', this.onIceCandidate.bind(this));
this.pc.on('endOfCandidates', (event) => { this.pc.on('endOfCandidates', (event) => {
self.send('endOfCandidates', event); self.send('endOfCandidates', event);
...@@ -157,17 +167,18 @@ class Peer extends WildEmitter { ...@@ -157,17 +167,18 @@ class Peer extends WildEmitter {
this.logger.log('sending via datachannel', channel, messageType, message); this.logger.log('sending via datachannel', channel, messageType, message);
const dc = this.getDataChannel(channel); const dc = this.getDataChannel(channel);
if (dc.readyState !== 'open') return false; if (dc.readyState !== 'open') return false;
dc.send(JSON.stringify(message)); dc.send(message);
return true; return true;
} }
// Internal method registering handlers for a data channel and emitting events on the peer // Internal method registering handlers for a data channel and emitting events on the peer
_observeDataChannel(channel, peer) { _observeDataChannel(channel, peer) {
const self = this; const self = this;
channel.binaryType = 'arraybuffer';
channel.onclose = this.emit.bind(this, 'channelClose', channel, peer); channel.onclose = this.emit.bind(this, 'channelClose', channel, peer);
channel.onerror = this.emit.bind(this, 'channelError', channel, peer); channel.onerror = this.emit.bind(this, 'channelError', channel, peer);
channel.onmessage = (event) => { channel.onmessage = (event) => {
self.emit('channelMessage', self, channel.label, JSON.parse(event.data), channel, event); self.emit('channelMessage', self, channel.label, decode(MessagePack.Buffer.from(event.data)), channel, event);
}; };
channel.onopen = this.emit.bind(this, 'channelOpen', channel, peer); channel.onopen = this.emit.bind(this, 'channelOpen', channel, peer);
} }
...@@ -223,11 +234,11 @@ class Peer extends WildEmitter { ...@@ -223,11 +234,11 @@ class Peer extends WildEmitter {
end(emitRemoval = true) { end(emitRemoval = true) {
if (this.closed) return; if (this.closed) return;
this.pc.close();
this.handleStreamRemoved(emitRemoval);
if (emitRemoval) { if (emitRemoval) {
this.parent.emit('removedPeer', this); this.parent.emit('removedPeer', this);
} }
this.pc.close();
this.handleStreamRemoved(emitRemoval);
} }
handleRemoteStreamAdded(event) { handleRemoteStreamAdded(event) {
...@@ -270,10 +281,10 @@ class Peer extends WildEmitter { ...@@ -270,10 +281,10 @@ class Peer extends WildEmitter {
}); });
// override onopen // override onopen
dc.onopen = () => { dc.onopen = () => {
dc.send(JSON.stringify({ dc.send({
size: file.size, size: file.size,
name: file.name, name: file.name,
})); });
sender.send(file, dc); sender.send(file, dc);
}; };
// override onclose // override onclose
...@@ -283,6 +294,11 @@ class Peer extends WildEmitter { ...@@ -283,6 +294,11 @@ class Peer extends WildEmitter {
}; };
return sender; return sender;
} }
getStats(selector) {
// TODO: Use adapter.js to patch this across browsers
return this.pc.pc.getStats(selector);
}
} }
export default Peer; export default Peer;
...@@ -12,7 +12,7 @@ class WebRTC extends LocalMedia { ...@@ -12,7 +12,7 @@ class WebRTC extends LocalMedia {
const config = this.config = { const config = this.config = {
debug: false, debug: false,
peerConnectionConfig: { peerConnectionConfig: {
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }], iceServers: [],
}, },
peerConnectionConstraints: { peerConnectionConstraints: {
optional: [], optional: [],
...@@ -61,7 +61,7 @@ class WebRTC extends LocalMedia { ...@@ -61,7 +61,7 @@ class WebRTC extends LocalMedia {
if (peer.enableDataChannels) { if (peer.enableDataChannels) {
const dc = peer.getDataChannel('liowebrtc'); const dc = peer.getDataChannel('liowebrtc');
if (dc.readyState !== 'open') return; if (dc.readyState !== 'open') return;
dc.sendDirectlyToAll(JSON.stringify({ type: 'speaking' })); dc.sendDirectlyToAll({ type: 'speaking' });
} }
}); });
} }
...@@ -72,7 +72,7 @@ class WebRTC extends LocalMedia { ...@@ -72,7 +72,7 @@ class WebRTC extends LocalMedia {
if (peer.enableDataChannels) { if (peer.enableDataChannels) {
const dc = peer.getDataChannel('liowebrtc'); const dc = peer.getDataChannel('liowebrtc');
if (dc.readyState !== 'open') return; if (dc.readyState !== 'open') return;
dc.sendDirectlyToAll(JSON.stringify({ type: 'stoppedSpeaking' })); dc.sendDirectlyToAll({ type: 'stoppedSpeaking' });
} }
}); });
} }
...@@ -83,7 +83,7 @@ class WebRTC extends LocalMedia { ...@@ -83,7 +83,7 @@ class WebRTC extends LocalMedia {
if (peer.enableDataChannels) { if (peer.enableDataChannels) {
const dc = peer.getDataChannel('liowebrtc'); const dc = peer.getDataChannel('liowebrtc');
if (dc.readyState !== 'open') return; if (dc.readyState !== 'open') return;
dc.sendDirectlyToAll(JSON.stringify({ type: 'payload', volume })); dc.sendDirectlyToAll({ type: 'payload', volume });
} }
}); });
} }
......