Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor WebRTC class #13736

Merged
merged 13 commits into from
May 20, 2019
150 changes: 58 additions & 92 deletions app/webrtc/client/WebRTCClass.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,63 +9,47 @@ import { Notifications } from '/app/notifications';
import { settings } from '/app/settings';
import { modal } from '/app/ui-utils';
import { ChatSubscription } from '/app/models';
import EventEmitter from 'wolfy87-eventemitter';

const WEB_RTC_EVENTS = {
WEB_RTC: 'webrtc',
STATUS: 0,
CALL: 1,
JOIN: 2,
CANDIDATE: 3,
DESCRIPTION: 4,
};

class WebRTCTransportClass {
class WebRTCTransportClass extends EventEmitter {
constructor(webrtcInstance) {
super();
this.debug = false;
this.webrtcInstance = webrtcInstance;
this.callbacks = {};
Notifications.onRoom(this.webrtcInstance.room, 'webrtc', (type, data) => {
const { onRemoteStatus } = this.callbacks;
Notifications.onRoom(this.webrtcInstance.room, WEB_RTC_EVENTS.WEB_RTC, (type, data) => {
this.log('WebRTCTransportClass - onRoom', type, data);
switch (type) {
case 'status':
if (onRemoteStatus && onRemoteStatus.length) {
onRemoteStatus.forEach((fn) => fn(data));
}
}
this.emit(type, data);
});
}

log(...args) {
if (this.debug === true) {
console.log.apply(console, args);
console.log(...args);
}
}

onUserStream(type, data) {
if (data.room !== this.webrtcInstance.room) {
return;
}

this.log('WebRTCTransportClass - onUser', type, data);
const { onRemoteCall, onRemoteJoin, onRemoteCandidate, onRemoteDescription } = this.callbacks;
this.emit(type, data);

switch (type) {
case 'call':
if (onRemoteCall && onRemoteCall.length) {
onRemoteCall.forEach((fn) => fn(data));
}
break;
case 'join':
if (onRemoteJoin && onRemoteJoin.length) {
onRemoteJoin.forEach((fn) => fn(data));
}
break;
case 'candidate':
if (onRemoteCandidate && onRemoteCandidate.length) {
onRemoteCandidate.forEach((fn) => fn(data));
}
break;
case 'description':
if (onRemoteDescription && onRemoteDescription.length) {
onRemoteDescription.forEach((fn) => fn(data));
}
}
}

startCall(data) {
this.log('WebRTCTransportClass - startCall', this.webrtcInstance.room, this.webrtcInstance.selfId);
Notifications.notifyUsersOfRoom(this.webrtcInstance.room, 'webrtc', 'call', {
Notifications.notifyUsersOfRoom(this.webrtcInstance.room, WEB_RTC_EVENTS.WEB_RTC, WEB_RTC_EVENTS.CALL, {
from: this.webrtcInstance.selfId,
room: this.webrtcInstance.room,
media: data.media,
Expand All @@ -76,14 +60,14 @@ class WebRTCTransportClass {
joinCall(data) {
this.log('WebRTCTransportClass - joinCall', this.webrtcInstance.room, this.webrtcInstance.selfId);
if (data.monitor === true) {
Notifications.notifyUser(data.to, 'webrtc', 'join', {
Notifications.notifyUser(data.to, WEB_RTC_EVENTS.WEB_RTC, WEB_RTC_EVENTS.JOIN, {
from: this.webrtcInstance.selfId,
room: this.webrtcInstance.room,
media: data.media,
monitor: data.monitor,
});
} else {
Notifications.notifyUsersOfRoom(this.webrtcInstance.room, 'webrtc', 'join', {
Notifications.notifyUsersOfRoom(this.webrtcInstance.room, WEB_RTC_EVENTS.WEB_RTC, WEB_RTC_EVENTS.JOIN, {
from: this.webrtcInstance.selfId,
room: this.webrtcInstance.room,
media: data.media,
Expand All @@ -96,60 +80,40 @@ class WebRTCTransportClass {
data.from = this.webrtcInstance.selfId;
data.room = this.webrtcInstance.room;
this.log('WebRTCTransportClass - sendCandidate', data);
Notifications.notifyUser(data.to, 'webrtc', 'candidate', data);
Notifications.notifyUser(data.to, WEB_RTC_EVENTS.WEB_RTC, WEB_RTC_EVENTS.CANDIDATE, data);
}

sendDescription(data) {
data.from = this.webrtcInstance.selfId;
data.room = this.webrtcInstance.room;
this.log('WebRTCTransportClass - sendDescription', data);
Notifications.notifyUser(data.to, 'webrtc', 'description', data);
Notifications.notifyUser(data.to, WEB_RTC_EVENTS.WEB_RTC, WEB_RTC_EVENTS.DESCRIPTION, data);
}

sendStatus(data) {
this.log('WebRTCTransportClass - sendStatus', data, this.webrtcInstance.room);
data.from = this.webrtcInstance.selfId;
Notifications.notifyRoom(this.webrtcInstance.room, 'webrtc', 'status', data);
Notifications.notifyRoom(this.webrtcInstance.room, WEB_RTC_EVENTS.WEB_RTC, WEB_RTC_EVENTS.STATUS, data);
}

onRemoteCall(fn) {
const { callbacks } = this;
if (callbacks.onRemoteCall == null) {
callbacks.onRemoteCall = [];
}
callbacks.onRemoteCall.push(fn);
return this.on(WEB_RTC_EVENTS.CALL, fn);
}

onRemoteJoin(fn) {
const { callbacks } = this;
if (callbacks.onRemoteJoin == null) {
callbacks.onRemoteJoin = [];
}
callbacks.onRemoteJoin.push(fn);
return this.on(WEB_RTC_EVENTS.JOIN, fn);
}

onRemoteCandidate(fn) {
const { callbacks } = this;
if (callbacks.onRemoteCandidate == null) {
callbacks.onRemoteCandidate = [];
}
callbacks.onRemoteCandidate.push(fn);
return this.on(WEB_RTC_EVENTS.CANDIDATE, fn);
}

onRemoteDescription(fn) {
const { callbacks } = this;
if (callbacks.onRemoteDescription == null) {
callbacks.onRemoteDescription = [];
}
callbacks.onRemoteDescription.push(fn);
return this.on(WEB_RTC_EVENTS.DESCRIPTION, fn);
}

onRemoteStatus(fn) {
const { callbacks } = this;
if (callbacks.onRemoteStatus == null) {
callbacks.onRemoteStatus = [];
}
callbacks.onRemoteStatus.push(fn);
return this.on(WEB_RTC_EVENTS.STATUS, fn);
}


Expand All @@ -165,7 +129,7 @@ class WebRTCClass {
this.config = {
iceServers: [],
};
this.debug = false;
this.debug = true;
this.TransportClass = WebRTCTransportClass;
this.selfId = selfId;
this.room = room;
Expand Down Expand Up @@ -212,8 +176,8 @@ class WebRTCClass {
} else if (userAgent.indexOf('safari') !== -1) {
this.navigator = 'safari';
}
const nav = this.navigator;
this.screenShareAvailable = nav === 'chrome' || nav === 'firefox' || nav === 'electron';

this.screenShareAvailable = ['chrome', 'firefox', 'electron'].includes(this.navigator);
this.media = {
video: false,
audio: true,
Expand All @@ -224,11 +188,17 @@ class WebRTCClass {
this.transport.onRemoteCandidate(this.onRemoteCandidate.bind(this));
this.transport.onRemoteDescription(this.onRemoteDescription.bind(this));
this.transport.onRemoteStatus(this.onRemoteStatus.bind(this));


Meteor.setInterval(this.checkPeerConnections.bind(this), 1000);

// Meteor.setInterval(this.broadcastStatus.bind(@), 1000);
}

onUserStream(...args) {
return this.transport.onUserStream(...args);
}

log(...args) {
if (this.debug === true) {
console.log.apply(console, args);
Expand All @@ -241,11 +211,13 @@ class WebRTCClass {

checkPeerConnections() {
const { peerConnections } = this;
Object.keys(peerConnections).forEach((id) => {
const peerConnection = peerConnections[id];
if (peerConnection.iceConnectionState !== 'connected' && peerConnection.iceConnectionState !== 'completed' && peerConnection.createdAt + 5000 < Date.now()) {
const date = Date.now();
Object.entries(peerConnections).some(([id, peerConnection]) => {
if (!['connected', 'completed'].includes(peerConnection.iceConnectionState) && peerConnection.createdAt + 5000 < date) {
this.stopPeerConnection(id);
return true;
}
return false;
});
}

Expand All @@ -254,9 +226,7 @@ class WebRTCClass {
const itemsById = {};
const { peerConnections } = this;

Object.keys(peerConnections).forEach((id) => {
const peerConnection = peerConnections[id];

Object.entries(peerConnections).forEach(([id, peerConnection]) => {
peerConnection.getRemoteStreams().forEach((remoteStream) => {
const item = {
id,
Expand Down Expand Up @@ -289,7 +259,7 @@ class WebRTCClass {
this.remoteItemsById.set(itemsById);
}

resetCallInProgress() {
resetCallInProgress = () => {
this.callInProgress.set(false);
}

Expand All @@ -299,11 +269,10 @@ class WebRTCClass {
}
const remoteConnections = [];
const { peerConnections } = this;
Object.keys(peerConnections).forEach((id) => {
const peerConnection = peerConnections[id];
Object.keys(peerConnections).entries(([id, { remoteMedia: media }]) => {
remoteConnections.push({
id,
media: peerConnection.remoteMedia,
media,
});
});

Expand All @@ -327,7 +296,7 @@ class WebRTCClass {
// this.log(onRemoteStatus, arguments);
this.callInProgress.set(true);
Meteor.clearTimeout(this.callInProgressTimeout);
this.callInProgressTimeout = Meteor.setTimeout(this.resetCallInProgress.bind(this), 2000);
this.callInProgressTimeout = Meteor.setTimeout(this.resetCallInProgress, 2000);
if (this.active !== true) {
return;
}
Expand Down Expand Up @@ -539,8 +508,7 @@ class WebRTCClass {
this.videoEnabled.set(this.media.video === true);
this.audioEnabled.set(this.media.audio === true);
const { peerConnections } = this;
Object.keys(peerConnections).forEach((id) => {
const peerConnection = peerConnections[id];
Object.entries(peerConnections).forEach(([, peerConnection]) => {
peerConnection.addStream(stream);
});
callback(null, this.localStream);
Expand All @@ -557,7 +525,7 @@ class WebRTCClass {
@param id {String}
*/

stopPeerConnection(id) {
stopPeerConnection = (id) => {
const peerConnection = this.peerConnections[id];
if (peerConnection == null) {
return;
Expand All @@ -570,9 +538,7 @@ class WebRTCClass {
stopAllPeerConnections() {
const { peerConnections } = this;

Object.keys(peerConnections).forEach((id) => {
this.stopPeerConnection(id);
});
Object.keys(peerConnections).forEach(this.stopPeerConnection);

window.audioContext && window.audioContext.close();
}
Expand Down Expand Up @@ -962,8 +928,8 @@ const WebRTC = new class {
this.instancesByRoomId = {};
}

getInstanceByRoomId(roomId) {
const subscription = ChatSubscription.findOne({ rid: roomId });
getInstanceByRoomId(rid) {
const subscription = ChatSubscription.findOne({ rid });
if (!subscription) {
return;
}
Expand All @@ -981,25 +947,25 @@ const WebRTC = new class {
if (enabled === false) {
return;
}
if (this.instancesByRoomId[roomId] == null) {
this.instancesByRoomId[roomId] = new WebRTCClass(Meteor.userId(), roomId);
if (this.instancesByRoomId[rid] == null) {
this.instancesByRoomId[rid] = new WebRTCClass(Meteor.userId(), rid);
}
return this.instancesByRoomId[roomId];
return this.instancesByRoomId[rid];
}
};

Meteor.startup(function() {
Tracker.autorun(function() {
if (Meteor.userId()) {
Notifications.onUser('webrtc', (type, data) => {
Notifications.onUser(WEB_RTC_EVENTS.WEB_RTC, (type, data) => {
if (data.room == null) {
return;
}
const webrtc = WebRTC.getInstanceByRoomId(data.room);
webrtc.transport.onUserStream(type, data);
webrtc.onUserStream(type, data);
});
}
});
});

export { WebRTC };
export { WebRTC, WEB_RTC_EVENTS };