UNPKG

rtcmulticonnection

Version:

RTCMultiConnection is a WebRTC JavaScript wrapper library runs top over RTCPeerConnection API to support all possible peer-to-peer features.

267 lines (220 loc) 8.92 kB
// Muaz Khan - www.MuazKhan.com // MIT License - www.WebRTC-Experiment.com/licence // Documentation - github.com/muaz-khan/RTCMultiConnection // pushLogs is used to write error logs into logs.json var pushLogs = function(name, error) { console.log(name, error); }; try { pushLogs = require('./pushLogs.js'); } catch (e) { console.log('Unable to read pushLogs.js', e); } var users = {}; module.exports = exports = function(socket, maxRelayLimitPerUser) { try { maxRelayLimitPerUser = parseInt(maxRelayLimitPerUser) || 2; } catch (e) { maxRelayLimitPerUser = 2; } socket.on('join-broadcast', function(user) { try { if (!users[user.userid]) { socket.userid = user.userid; socket.isScalableBroadcastSocket = true; users[user.userid] = { userid: user.userid, broadcastId: user.broadcastId, isBroadcastInitiator: false, maxRelayLimitPerUser: maxRelayLimitPerUser, relayReceivers: [], receivingFrom: null, canRelay: false, typeOfStreams: user.typeOfStreams || { audio: true, video: true }, socket: socket }; notifyBroadcasterAboutNumberOfViewers(user.broadcastId); } var relayUser = getFirstAvailableBroadcaster(user.broadcastId, maxRelayLimitPerUser); if (relayUser === 'ask-him-rejoin') { socket.emit('rejoin-broadcast', user.broadcastId); return; } if (relayUser && user.userid !== user.broadcastId) { var hintsToJoinBroadcast = { typeOfStreams: relayUser.typeOfStreams, userid: relayUser.userid, broadcastId: relayUser.broadcastId }; users[user.userid].receivingFrom = relayUser.userid; users[relayUser.userid].relayReceivers.push( users[user.userid] ); users[user.broadcastId].lastRelayuserid = relayUser.userid; socket.emit('join-broadcaster', hintsToJoinBroadcast); // logs for current socket socket.emit('logs', 'You <' + user.userid + '> are getting data/stream from <' + relayUser.userid + '>'); // logs for target relaying user relayUser.socket.emit('logs', 'You <' + relayUser.userid + '>' + ' are now relaying/forwarding data/stream to <' + user.userid + '>'); } else { users[user.userid].isBroadcastInitiator = true; socket.emit('start-broadcasting', users[user.userid].typeOfStreams); // logs to tell he is now broadcast initiator socket.emit('logs', 'You <' + user.userid + '> are now serving the broadcast.'); } } catch (e) { pushLogs('join-broadcast', e); } }); socket.on('scalable-broadcast-message', function(message) { socket.broadcast.emit('scalable-broadcast-message', message); }); socket.on('can-relay-broadcast', function() { if (users[socket.userid]) { users[socket.userid].canRelay = true; } }); socket.on('can-not-relay-broadcast', function() { if (users[socket.userid]) { users[socket.userid].canRelay = false; } }); socket.on('check-broadcast-presence', function(userid, callback) { // we can pass number of viewers as well try { callback(!!users[userid] && users[userid].isBroadcastInitiator === true); } catch (e) { pushLogs('check-broadcast-presence', e); } }); socket.on('get-number-of-users-in-specific-broadcast', function(broadcastId, callback) { try { if (!broadcastId || !callback) return; if (!users[broadcastId]) { callback(0); return; } callback(getNumberOfBroadcastViewers(broadcastId)); } catch (e) {} }); function getNumberOfBroadcastViewers(broadcastId) { try { var numberOfUsers = 0; Object.keys(users).forEach(function(uid) { var user = users[uid]; if (user.broadcastId === broadcastId) { numberOfUsers++; } }); return numberOfUsers - 1; } catch (e) { return 0; } } function notifyBroadcasterAboutNumberOfViewers(broadcastId, userLeft) { try { if (!broadcastId || !users[broadcastId] || !users[broadcastId].socket) return; var numberOfBroadcastViewers = getNumberOfBroadcastViewers(broadcastId); if (userLeft === true) { numberOfBroadcastViewers--; } users[broadcastId].socket.emit('number-of-broadcast-viewers-updated', { numberOfBroadcastViewers: numberOfBroadcastViewers, broadcastId: broadcastId }); } catch (e) {} } // this even is called from "signaling-server.js" socket.ondisconnect = function() { try { if (!socket.isScalableBroadcastSocket) return; var user = users[socket.userid]; if (!user) return; if (user.isBroadcastInitiator === false) { notifyBroadcasterAboutNumberOfViewers(user.broadcastId, true); } if (user.isBroadcastInitiator === true) { // need to stop entire broadcast? for (var n in users) { var _user = users[n]; if (_user.broadcastId === user.broadcastId) { _user.socket.emit('broadcast-stopped', user.broadcastId); } } delete users[socket.userid]; return; } if (user.receivingFrom || user.isBroadcastInitiator === true) { var parentUser = users[user.receivingFrom]; if (parentUser) { var newArray = []; parentUser.relayReceivers.forEach(function(n) { if (n.userid !== user.userid) { newArray.push(n); } }); users[user.receivingFrom].relayReceivers = newArray; } } if (user.relayReceivers.length && user.isBroadcastInitiator === false) { askNestedUsersToRejoin(user.relayReceivers); } delete users[socket.userid]; } catch (e) { pushLogs('scalable-broadcast-disconnect', e); } }; }; function askNestedUsersToRejoin(relayReceivers) { try { var usersToAskRejoin = []; relayReceivers.forEach(function(receiver) { if (!!users[receiver.userid]) { users[receiver.userid].canRelay = false; users[receiver.userid].receivingFrom = null; receiver.socket.emit('rejoin-broadcast', receiver.broadcastId); } }); } catch (e) { pushLogs('askNestedUsersToRejoin', e); } } function getFirstAvailableBroadcaster(broadcastId, maxRelayLimitPerUser) { try { var broadcastInitiator = users[broadcastId]; // if initiator is capable to receive users if (broadcastInitiator.relayReceivers.length < maxRelayLimitPerUser) { return broadcastInitiator; } // otherwise if initiator knows who is current relaying user if (broadcastInitiator.lastRelayuserid) { var lastRelayUser = users[broadcastInitiator.lastRelayuserid]; if (lastRelayUser && lastRelayUser.relayReceivers.length < maxRelayLimitPerUser) { return lastRelayUser; } } // otherwise, search for a user who not relayed anything yet var userFound; for (var n in users) { var user = users[n]; if (userFound) { continue; } else if (user.broadcastId === broadcastId) { if (!user.relayReceivers.length && user.canRelay === true) { userFound = user; } } } if (userFound) { return userFound; } // need to increase "maxRelayLimitPerUser" in this situation // so that each relaying user can distribute the bandwidth return broadcastInitiator; } catch (e) { pushLogs('getFirstAvailableBroadcaster', e); } }