From e49946b049c8cdde86c81686a98a32ec4007db3f Mon Sep 17 00:00:00 2001 From: Anton Georgiev <anto.georgiev@gmail.com> Date: Mon, 27 May 2019 11:34:56 +0000 Subject: [PATCH] bbb-webrtc-sfu code is now only found in own repo inside bigbluebutton/ --- labs/bbb-webrtc-sfu/.gitignore | 1 - labs/bbb-webrtc-sfu/Dockerfile | 17 - labs/bbb-webrtc-sfu/README.md | 0 .../config/custom-environment-variables.yml | 7 - .../bbb-webrtc-sfu/config/default.example.yml | 62 -- labs/bbb-webrtc-sfu/debug-start.sh | 1 - labs/bbb-webrtc-sfu/docker-entrypoint.sh | 11 - labs/bbb-webrtc-sfu/lib/ProcessManager.js | 120 ---- labs/bbb-webrtc-sfu/lib/audio/AudioManager.js | 142 ---- labs/bbb-webrtc-sfu/lib/audio/AudioProcess.js | 10 - labs/bbb-webrtc-sfu/lib/audio/audio.js | 316 --------- labs/bbb-webrtc-sfu/lib/base/BaseManager.js | 195 ------ labs/bbb-webrtc-sfu/lib/base/BaseProcess.js | 64 -- labs/bbb-webrtc-sfu/lib/base/BaseProvider.js | 90 --- labs/bbb-webrtc-sfu/lib/base/errors.js | 30 - .../lib/bbb/messages/Constants.js | 180 ----- .../lib/bbb/messages/Messaging.js | 124 ---- .../lib/bbb/messages/OutMessage.js | 35 - .../lib/bbb/messages/OutMessage2x.js | 52 -- .../audio/UserConnectedToGlobalAudio.js | 16 - .../audio/UserConnectedToGlobalAudio2x.js | 21 - .../audio/UserDisconnectedFromGlobalAudio.js | 16 - .../UserDisconnectedFromGlobalAudio2x.js | 21 - .../RecordingStatusRequestMessage2x.js | 18 - ...skShareRTMPBroadcastStartedEventMessage.js | 22 - ...skShareRTMPBroadcastStoppedEventMessage.js | 22 - ...shareRTMPBroadcastStartedEventMessage2x.js | 25 - ...shareRTMPBroadcastStoppedEventMessage2x.js | 25 - .../StartTranscoderRequestMessage.js | 20 - .../transcode/StartTranscoderSysReqMsg.js | 18 - .../transcode/StopTranscoderRequestMessage.js | 15 - .../transcode/StopTranscoderSysReqMsg.js | 17 - .../UserCamBroadcastStoppedEventMessage2x.js | 18 - .../bbb/messages/video/WebRTCShareEvent.js | 23 - .../lib/bbb/pubsub/RedisWrapper.js | 186 ----- labs/bbb-webrtc-sfu/lib/bbb/pubsub/bbb-gw.js | 165 ----- .../connection-manager/ConnectionManager.js | 99 --- .../lib/connection-manager/HttpServer.js | 31 - .../connection-manager/MessageValidator.js | 81 --- .../WebsocketConnectionManager.js | 142 ---- labs/bbb-webrtc-sfu/lib/h264-sdp.js | 80 --- .../lib/mcs-core/CoreProcess.js | 18 - .../lib/adapters/freeswitch/AudioHandler.js | 99 --- .../lib/adapters/freeswitch/freeswitch.js | 289 -------- .../mcs-core/lib/adapters/kurento/errors.js | 62 -- .../mcs-core/lib/adapters/kurento/kurento.js | 640 ------------------ .../lib/mcs-core/lib/constants/Constants.js | 117 ---- .../lib/mcs-core/lib/media/MCSApiStub.js | 170 ----- .../lib/mcs-core/lib/media/MediaController.js | 364 ---------- .../lib/mcs-core/lib/model/MediaSession.js | 171 ----- .../mcs-core/lib/model/RecordingSession.js | 28 - .../lib/mcs-core/lib/model/Room.js | 34 - .../lib/mcs-core/lib/model/SdpSession.js | 99 --- .../lib/mcs-core/lib/model/SfuUser.js | 211 ------ .../lib/mcs-core/lib/model/UriSession.js | 28 - .../lib/mcs-core/lib/model/User.js | 25 - .../lib/mcs-core/lib/utils/SdpWrapper.js | 295 -------- .../lib/mcs-core/lib/utils/util.js | 48 -- labs/bbb-webrtc-sfu/lib/media-handler.js | 64 -- .../lib/screenshare/ScreenshareManager.js | 142 ---- .../lib/screenshare/ScreenshareProcess.js | 10 - .../lib/screenshare/screenshare.js | 489 ------------- labs/bbb-webrtc-sfu/lib/utils/Logger.js | 48 -- labs/bbb-webrtc-sfu/lib/utils/Utils.js | 17 - labs/bbb-webrtc-sfu/lib/video/VideoManager.js | 146 ---- labs/bbb-webrtc-sfu/lib/video/VideoProcess.js | 10 - labs/bbb-webrtc-sfu/lib/video/video.js | 332 --------- labs/bbb-webrtc-sfu/package-lock.json | 443 ------------ labs/bbb-webrtc-sfu/package.json | 22 - labs/bbb-webrtc-sfu/server.js | 29 - 70 files changed, 6988 deletions(-) delete mode 100644 labs/bbb-webrtc-sfu/.gitignore delete mode 100644 labs/bbb-webrtc-sfu/Dockerfile delete mode 100644 labs/bbb-webrtc-sfu/README.md delete mode 100644 labs/bbb-webrtc-sfu/config/custom-environment-variables.yml delete mode 100644 labs/bbb-webrtc-sfu/config/default.example.yml delete mode 100644 labs/bbb-webrtc-sfu/debug-start.sh delete mode 100755 labs/bbb-webrtc-sfu/docker-entrypoint.sh delete mode 100644 labs/bbb-webrtc-sfu/lib/ProcessManager.js delete mode 100755 labs/bbb-webrtc-sfu/lib/audio/AudioManager.js delete mode 100644 labs/bbb-webrtc-sfu/lib/audio/AudioProcess.js delete mode 100644 labs/bbb-webrtc-sfu/lib/audio/audio.js delete mode 100644 labs/bbb-webrtc-sfu/lib/base/BaseManager.js delete mode 100644 labs/bbb-webrtc-sfu/lib/base/BaseProcess.js delete mode 100644 labs/bbb-webrtc-sfu/lib/base/BaseProvider.js delete mode 100644 labs/bbb-webrtc-sfu/lib/base/errors.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/Constants.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/Messaging.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/OutMessage.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/OutMessage2x.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserConnectedToGlobalAudio.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserConnectedToGlobalAudio2x.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserDisconnectedFromGlobalAudio.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserDisconnectedFromGlobalAudio2x.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/recording/RecordingStatusRequestMessage2x.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/DeskShareRTMPBroadcastStartedEventMessage.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/DeskShareRTMPBroadcastStoppedEventMessage.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/ScreenshareRTMPBroadcastStartedEventMessage2x.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/ScreenshareRTMPBroadcastStoppedEventMessage2x.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StartTranscoderRequestMessage.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StartTranscoderSysReqMsg.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StopTranscoderRequestMessage.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StopTranscoderSysReqMsg.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/video/UserCamBroadcastStoppedEventMessage2x.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/messages/video/WebRTCShareEvent.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/pubsub/RedisWrapper.js delete mode 100644 labs/bbb-webrtc-sfu/lib/bbb/pubsub/bbb-gw.js delete mode 100644 labs/bbb-webrtc-sfu/lib/connection-manager/ConnectionManager.js delete mode 100644 labs/bbb-webrtc-sfu/lib/connection-manager/HttpServer.js delete mode 100644 labs/bbb-webrtc-sfu/lib/connection-manager/MessageValidator.js delete mode 100644 labs/bbb-webrtc-sfu/lib/connection-manager/WebsocketConnectionManager.js delete mode 100644 labs/bbb-webrtc-sfu/lib/h264-sdp.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/CoreProcess.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/freeswitch/AudioHandler.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/freeswitch/freeswitch.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/kurento/errors.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/kurento/kurento.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/constants/Constants.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/media/MCSApiStub.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/media/MediaController.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/MediaSession.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/RecordingSession.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/Room.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/SdpSession.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/SfuUser.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/UriSession.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/User.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/utils/SdpWrapper.js delete mode 100644 labs/bbb-webrtc-sfu/lib/mcs-core/lib/utils/util.js delete mode 100644 labs/bbb-webrtc-sfu/lib/media-handler.js delete mode 100644 labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareManager.js delete mode 100644 labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareProcess.js delete mode 100644 labs/bbb-webrtc-sfu/lib/screenshare/screenshare.js delete mode 100644 labs/bbb-webrtc-sfu/lib/utils/Logger.js delete mode 100644 labs/bbb-webrtc-sfu/lib/utils/Utils.js delete mode 100755 labs/bbb-webrtc-sfu/lib/video/VideoManager.js delete mode 100644 labs/bbb-webrtc-sfu/lib/video/VideoProcess.js delete mode 100644 labs/bbb-webrtc-sfu/lib/video/video.js delete mode 100644 labs/bbb-webrtc-sfu/package-lock.json delete mode 100644 labs/bbb-webrtc-sfu/package.json delete mode 100755 labs/bbb-webrtc-sfu/server.js diff --git a/labs/bbb-webrtc-sfu/.gitignore b/labs/bbb-webrtc-sfu/.gitignore deleted file mode 100644 index 8d87b1d267..0000000000 --- a/labs/bbb-webrtc-sfu/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/* diff --git a/labs/bbb-webrtc-sfu/Dockerfile b/labs/bbb-webrtc-sfu/Dockerfile deleted file mode 100644 index ebba290ee6..0000000000 --- a/labs/bbb-webrtc-sfu/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM node:8 - -ADD . /source -RUN cp /source/config/default.example.yml /source/config/production.yml - -ENV NODE_ENV production - -RUN cd /source \ - && mv docker-entrypoint.sh /usr/local/bin/ \ - && npm install \ - && npm cache clear --force - -WORKDIR /source - -EXPOSE 3008 - -CMD [ "docker-entrypoint.sh" ] diff --git a/labs/bbb-webrtc-sfu/README.md b/labs/bbb-webrtc-sfu/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/labs/bbb-webrtc-sfu/config/custom-environment-variables.yml b/labs/bbb-webrtc-sfu/config/custom-environment-variables.yml deleted file mode 100644 index 074de09626..0000000000 --- a/labs/bbb-webrtc-sfu/config/custom-environment-variables.yml +++ /dev/null @@ -1,7 +0,0 @@ -kurentoUrl: KURENTO_URL -kurentoIp: KURENTO_IP -redisHost: REDIS_HOST -freeswitch: - ip: FREESWITCH_IP -log: - level: LOG_LEVEL diff --git a/labs/bbb-webrtc-sfu/config/default.example.yml b/labs/bbb-webrtc-sfu/config/default.example.yml deleted file mode 100644 index 9e6f515fc9..0000000000 --- a/labs/bbb-webrtc-sfu/config/default.example.yml +++ /dev/null @@ -1,62 +0,0 @@ -# Websocket URL under which kurento is listening -kurentoUrl: "ws://HOST/kurento" -# The external IP of the host where Kurento is located -kurentoIp: "" -# The external IP of the host where Red5/BBB is located. Used for the RTMP url -localIpAddress: "" -acceptSelfSignedCertificate: false -redisHost : "127.0.0.1" -redisPort : "6379" -# Port under which bbb-webrtc-sfu serves client -clientPort : "3008" -# The following UDP port boundary is related to the ports ffmpeg can use to generate the RTMP stream -minVideoPort: 30000 -maxVideoPort: 33000 -# Timeout (ms) that triggers a failure when no media has reached the server -mediaFlowTimeoutDuration: 5000 -from-screenshare: "from-screenshare-sfu" -to-screenshare: "to-screenshare-sfu" -from-video: "from-video-sfu" -to-video: "to-video-sfu" -from-audio: "from-audio-sfu" -to-audio: "to-audio-sfu" -to-akka: "to-akka-apps-redis-channel" -from-akka: "from-akka-apps-redis-channel" -common-message-version: "2.x" -# FORCES H.264 for webcams. Endpoints without H.264 WILL NOT WORK. -# Disabling it will make the process go untouched and may cause transcoding. -webcam-force-h264: true -# Preferred H.264 profile-level-id for webcams. Forces everyone to use CB -webcam-preferred-h264-profile: "42e01f" -# Target bitrate (kbps) for webcams. Value 0 leaves it unconstrained. -webcam-target-bitrate: 300 -# FORCES H.264 for screenshare. Endpoints without H.264 WILL NOT WORK. -# Disabling it will make the process go untouched and may cause transcoding. -screenshare-force-h264: true -# Preferred H.264 profile-level-id for screenshare. Forces everyone to use CB -screenshare-preferred-h264-profile: "42e01f" -# Base interval for keyframe requsitions to the screenshare streamer -screenshareKeyframeInterval: 2 -# Target bitrate (kbps) for screenshare. Value 0 leaves it unconstrained. -screenshare-target-bitrate: 0 -# Size of the websocket pool SFU uses to connect to Kurento. -kurento-websocket-pool-size: 1 - -recordScreenSharing: true -recordWebcams: false -recordingBasePath: "file:///var/kurento" - -recordingMediaProfile: 'MKV_VIDEO_ONLY' -recordingFormat: 'mkv' - -redisExpireTime: 1209600 # 14 days as per the akka keys - -# Used for the listen only bridge. The IP MUST be the one where FS is binded to -freeswitch: - ip: 'FREESWITCH_IP' - port: '5066' - -# Log levels, in order of specificity: info, warn, verbose, debug, trace -log: - filename: '/var/log/bbb-webrtc-sfu/bbb-webrtc-sfu.log' - level: 'verbose' diff --git a/labs/bbb-webrtc-sfu/debug-start.sh b/labs/bbb-webrtc-sfu/debug-start.sh deleted file mode 100644 index b7c6990b40..0000000000 --- a/labs/bbb-webrtc-sfu/debug-start.sh +++ /dev/null @@ -1 +0,0 @@ -node --inspect --debug-brk server.js diff --git a/labs/bbb-webrtc-sfu/docker-entrypoint.sh b/labs/bbb-webrtc-sfu/docker-entrypoint.sh deleted file mode 100755 index d424e511a1..0000000000 --- a/labs/bbb-webrtc-sfu/docker-entrypoint.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -e - -CONTAINER_IP=$(hostname -I | awk '{print $1}') - -sed -i "s|^\(localIpAddress\):.*|\1: \"$CONTAINER_IP\"|g" config/production.yml - -if [ ! -z "$KURENTO_NAME" ]; then - export KURENTO_IP=$(getent hosts $KURENTO_NAME | awk '{ print $1 }') -fi - -npm start diff --git a/labs/bbb-webrtc-sfu/lib/ProcessManager.js b/labs/bbb-webrtc-sfu/lib/ProcessManager.js deleted file mode 100644 index 9753a37ebd..0000000000 --- a/labs/bbb-webrtc-sfu/lib/ProcessManager.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Lucas Fialho Zawacki - * Paulo Renato Lanzarin - * (C) Copyright 2017 Bigbluebutton - * - */ - -'use strict'; - -const cp = require('child_process'); -const Logger = require('./utils/Logger'); -const SCREENSHARE_PATH = './lib/screenshare/ScreenshareProcess'; -const VIDEO_PATH = './lib/video/VideoProcess.js'; -const AUDIO_PATH = './lib/audio/AudioProcess.js'; - -module.exports = class ProcessManager { - constructor() { - this.screensharePid; - this.videoPid; - this.screenshareProcess; - this.videoProcess; - this.audioProcess; - this.processes = {}; - this.runningState = "RUNNING"; - } - - async start () { - let screenshareProcess = this.startProcess(SCREENSHARE_PATH); - let videoProcess = this.startProcess(VIDEO_PATH); - let audioProcess = this.startProcess(AUDIO_PATH); - - - this.processes[screenshareProcess.pid] = screenshareProcess; - this.processes[videoProcess.pid] = videoProcess; - this.processes[audioProcess.pid] = audioProcess; - - process.on('SIGTERM', async () => { - await this.finishChildProcesses(); - process.exit(0); - }); - - process.on('SIGINT', async () => { - await this.finishChildProcesses(); - process.exit(0); - }); - - process.on('uncaughtException', async (error) => { - Logger.error('[ProcessManager] Uncaught exception', error.stack); - await this.finishChildProcesses(); - process.exit('1'); - }); - - // Added this listener to identify unhandled promises, but we should start making - // sense of those as we find them - process.on('unhandledRejection', (reason, p) => { - Logger.error('[ProcessManager] Unhandled Rejection at: Promise', p, 'reason:', reason); - }); - } - - startProcess (processPath) { - Logger.info("[ProcessManager] Starting process at path", processPath); - let proc = cp.fork(processPath, { - // Pass over all of the environment. - env: process.ENV, - // Share stdout/stderr, so we can hear the inevitable errors. - silent: false - }); - - proc.path = processPath; - - proc.on('message', this.onMessage); - proc.on('error', this.onError); - - // Tries to restart process on unsucessful exit - proc.on('exit', (code, signal) => { - let processId = proc.pid; - if (this.runningState === 'RUNNING' && code === 1) { - Logger.error('[ProcessManager] Received exit event from child process with PID', proc.pid, ' with code', code, '. Restarting it'); - this.restartProcess(processId); - } - }); - - return proc; - } - - restartProcess (pid) { - let proc = this.processes[pid]; - if (proc) { - let newProcess = this.startProcess(proc.path); - this.processes[newProcess.pid] = newProcess; - delete this.processes[pid]; - } - } - - onMessage (message) { - Logger.info('[ProcessManager] Received child message from', this.pid, message); - } - - onError (e) { - Logger.error('[ProcessManager] Received child error', this.pid, e); - } - - onDisconnect (e) { - } - - async finishChildProcesses () { - this.runningState = "STOPPING"; - - for (var proc in this.processes) { - if (this.processes.hasOwnProperty(proc)) { - let procObj = this.processes[proc]; - if (typeof procObj.exit === 'function' && !procObj.killed) { - await procObj.exit() - } - } - } - - this.runningState = "STOPPED"; - } -} diff --git a/labs/bbb-webrtc-sfu/lib/audio/AudioManager.js b/labs/bbb-webrtc-sfu/lib/audio/AudioManager.js deleted file mode 100755 index 54d8eebd3d..0000000000 --- a/labs/bbb-webrtc-sfu/lib/audio/AudioManager.js +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Lucas Fialho Zawacki - * Paulo Renato Lanzarin - * (C) Copyright 2017 Bigbluebutton - * - */ - -"use strict"; - -const BigBlueButtonGW = require('../bbb/pubsub/bbb-gw'); -const Audio = require('./audio'); -const BaseManager = require('../base/BaseManager'); -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); -const errors = require('../base/errors'); - -module.exports = class AudioManager extends BaseManager { - constructor (connectionChannel, additionalChannels, logPrefix) { - super(connectionChannel, additionalChannels, logPrefix); - this.sfuApp = C.AUDIO_APP; - this._meetings = {}; - this._trackMeetingTermination(); - this.messageFactory(this._onMessage); - this._iceQueues = {}; - } - - _trackMeetingTermination () { - switch (C.COMMON_MESSAGE_VERSION) { - case "1.x": - this._bbbGW.on(C.DICONNECT_ALL_USERS, (payload) => { - let meetingId = payload[C.MEETING_ID]; - this._disconnectAllUsers(meetingId); - }); - break; - default: - this._bbbGW.on(C.DICONNECT_ALL_USERS_2x, (payload) => { - let meetingId = payload[C.MEETING_ID_2x]; - this._disconnectAllUsers(meetingId); - }); - } - } - - _disconnectAllUsers(meetingId) { - let sessionId = this._meetings[meetingId]; - if (typeof sessionId !== 'undefined') { - Logger.debug(this._logPrefix, 'Disconnecting all users from', sessionId); - delete this._meetings[meetingId]; - this._stopSession(sessionId); - } - } - - async _onMessage(message) { - Logger.debug(this._logPrefix, 'Received message [' + message.id + '] from connection', message.connectionId); - let sessionId = message.voiceBridge; - let voiceBridge = sessionId; - let connectionId = message.connectionId; - - let session = this._fetchSession(sessionId); - let iceQueue = this._fetchIceQueue(sessionId+connectionId); - - switch (message.id) { - case 'start': - try { - if (!session) { - session = new Audio(this._bbbGW, connectionId, voiceBridge); - } - - this._meetings[message.internalMeetingId] = sessionId; - this._sessions[sessionId] = session; - - const { sdpOffer, caleeName, userId, userName } = message; - - // starts audio session by sending sessionID, websocket and sdpoffer - const sdpAnswer = await session.start(sessionId, connectionId, sdpOffer, caleeName, userId, userName); - Logger.info(this._logPrefix, "Started presenter ", sessionId, " for connection", connectionId); - - // Empty the ICE queue - this._flushIceQueue(session, iceQueue); - - session.once(C.MEDIA_SERVER_OFFLINE, async (event) => { - const errorMessage = this._handleError(this._logPrefix, connectionId, caleeName, C.RECV_ROLE, errors.MEDIA_SERVER_OFFLINE); - errorMessage.id = 'webRTCAudioError'; - this._bbbGW.publish(JSON.stringify({ - ...errorMessage, - }), C.FROM_AUDIO); - }); - - this._bbbGW.publish(JSON.stringify({ - connectionId: connectionId, - id : 'startResponse', - type: 'audio', - response : 'accepted', - sdpAnswer : sdpAnswer - }), C.FROM_AUDIO); - - Logger.info(this._logPrefix, "Sending startResponse to user", sessionId, "for connection", session._id); - } catch (err) { - const errorMessage = this._handleError(this._logPrefix, connectionId, message.caleeName, C.RECV_ROLE, err); - errorMessage.id = 'webRTCAudioError'; - return this._bbbGW.publish(JSON.stringify({ - ...errorMessage - }), C.FROM_AUDIO); - } - break; - - case 'stop': - Logger.info(this._logPrefix, 'Received stop message for session', sessionId, "at connection", connectionId); - - if (session) { - session.stopListener(connectionId); - } else { - Logger.warn(this._logPrefix, "There was no audio session on stop for", sessionId); - } - break; - - case 'iceCandidate': - if (session) { - session.onIceCandidate(message.candidate, connectionId); - } else { - Logger.warn(this._logPrefix, "There was no audio session for onIceCandidate for", sessionId, ". There should be a queue here"); - iceQueue.push(message.candidate); - } - break; - - case 'close': - Logger.info(this._logPrefix, 'Connection ' + connectionId + ' closed'); - this._deleteIceQueue(sessionId+connectionId); - if (typeof session !== 'undefined') { - Logger.info(this._logPrefix, "Stopping viewer " + sessionId); - session.stopListener(message.connectionId); - } - break; - - default: - const errorMessage = this._handleError(this._logPrefix, connectionId, null, null, errors.SFU_INVALID_REQUEST); - this._bbbGW.publish(JSON.stringify({ - ...errorMessage, - }), C.FROM_AUDIO); - break; - } - } -}; diff --git a/labs/bbb-webrtc-sfu/lib/audio/AudioProcess.js b/labs/bbb-webrtc-sfu/lib/audio/AudioProcess.js deleted file mode 100644 index 08903d4f8d..0000000000 --- a/labs/bbb-webrtc-sfu/lib/audio/AudioProcess.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -const AudioManager= require('./AudioManager'); -const BaseProcess = require('../base/BaseProcess'); -const C = require('../bbb/messages/Constants'); - -const manager = new AudioManager(C.TO_AUDIO, [C.FROM_BBB_MEETING_CHAN], C.AUDIO_MANAGER_PREFIX); -const newProcess = new BaseProcess(manager, C.AUDIO_PROCESS_PREFIX); - -newProcess.start(); diff --git a/labs/bbb-webrtc-sfu/lib/audio/audio.js b/labs/bbb-webrtc-sfu/lib/audio/audio.js deleted file mode 100644 index f1b8e059fa..0000000000 --- a/labs/bbb-webrtc-sfu/lib/audio/audio.js +++ /dev/null @@ -1,316 +0,0 @@ -'use strict'; - -const kurento = require('kurento-client'); -const config = require('config'); -const kurentoUrl = config.get('kurentoUrl'); -const MCSApi = require('../mcs-core/lib/media/MCSApiStub'); -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); -const Messaging = require('../bbb/messages/Messaging'); -const BaseProvider = require('../base/BaseProvider'); -const LOG_PREFIX = "[audio]"; - -module.exports = class Audio extends BaseProvider { - constructor(_bbbGW, _id, voiceBridge) { - super(); - this.sfuApp = C.AUDIO_APP; - this.mcs = new MCSApi(); - this.bbbGW = _bbbGW; - this.id = _id; - this.voiceBridge = voiceBridge; - this.sourceAudio; - this.sourceAudioStarted = false; - this.audioEndpoints = {}; - this.role; - this.webRtcEndpoint = null; - this.userId; - - this.connectedUsers = {}; - this.candidatesQueue = {} - } - - onIceCandidate (_candidate, connectionId) { - if (this.audioEndpoints[connectionId]) { - try { - this.flushCandidatesQueue(connectionId); - this.mcs.addIceCandidate(this.audioEndpoints[connectionId], _candidate); - } - catch (err) { - const userId = this.getUser(connectionId); - this._handleError(LOG_PREFIX, err, "recv", userId); - } - } - else { - if(!this.candidatesQueue[connectionId]) { - this.candidatesQueue[connectionId] = []; - } - this.candidatesQueue[connectionId].push(_candidate); - } - }; - - flushCandidatesQueue (connectionId) { - if (this.audioEndpoints[connectionId]) { - try { - if (this.candidatesQueue[connectionId]) { - while(this.candidatesQueue[connectionId].length) { - const candidate = this.candidatesQueue[connectionId].shift(); - this.mcs.addIceCandidate(this.audioEndpoints[connectionId], candidate); - } - } - } - catch (err) { - const userId = this.getUser(connectionId); - this._handleError(LOG_PREFIX, err, "recv", userId); - Logger.error(LOG_PREFIX, "ICE candidate could not be added to media controller.", err); - } - } - } - -/** - * Include user to a hash object indexed by it's connectionId - * @param {String} connectionId Current connection id at the media manager - * @param {Object} user {userId: String, userName: String} - */ - addUser(connectionId, user) { - if (this.connectedUsers.hasOwnProperty(connectionId)) { - Logger.warn(LOG_PREFIX, "Updating user for connectionId", connectionId) - } - this.connectedUsers[connectionId] = user; - }; - -/** - * Exclude user from a hash object indexed by it's connectionId - * @param {String} connectionId Current connection id at the media manager - */ - removeUser(connectionId) { - if (this.connectedUsers.hasOwnProperty(connectionId)) { - delete this.connectedUsers[connectionId]; - } else { - Logger.error(LOG_PREFIX, "Missing connectionId", connectionId); - } - }; - -/** - * Consult user from a hash object indexed by it's connectionId - * @param {String} connectionId Current connection id at the media manager - * @return {Object} user {userId: String, userName: String} - */ - getUser(connectionId) { - if (this.connectedUsers.hasOwnProperty(connectionId)) { - return this.connectedUsers[connectionId]; - } else { - Logger.error(LOG_PREFIX, "Missing connectionId", connectionId); - } - }; - - mediaState (event) { - let msEvent = event.event; - - switch (event.eventTag) { - - case "MediaStateChanged": - break; - - default: Logger.warn(LOG_PREFIX, "Unrecognized event"); - } - } - - mediaStateWebRtc (event, id) { - let msEvent = event.event; - - switch (event.eventTag) { - case "OnIceCandidate": - let candidate = msEvent.candidate; - Logger.debug(LOG_PREFIX, 'Received ICE candidate from mcs-core for media session', event.id, '=>', candidate); - - this.bbbGW.publish(JSON.stringify({ - connectionId: id, - id : 'iceCandidate', - type: 'audio', - cameraId: this._id, - candidate : candidate - }), C.FROM_AUDIO); - - break; - - case "MediaStateChanged": - break; - - case "MediaFlowOutStateChange": - Logger.info('[audio]', msEvent.type, '[' + msEvent.state? msEvent.state : 'UNKNOWN_STATE' + ']', 'for media session', event.id); - // TODO treat this accordingly =( (prlanzarin 05/02/2018) - - break; - - case "MediaFlowInStateChange": - Logger.info('[audio]', msEvent.type, '[' + msEvent.state? msEvent.state : 'UNKNOWN_STATE' + ']', 'for media session ', event.id); - if (msEvent.state === 'FLOWING') { - this._onRtpMediaFlowing(id); - } else { - this._onRtpMediaNotFlowing(id); - } - break; - - default: Logger.warn(LOG_PREFIX, "Unrecognized event", event); - } - } - - start (sessionId, connectionId, sdpOffer, caleeName, userId, userName) { - return new Promise(async (resolve, reject) => { - Logger.info(LOG_PREFIX, "Starting audio instance for", this.id); - let sdpAnswer; - - // Storing the user data to be used by the pub calls - let user = {userId: userId, userName: userName}; - this.addUser(connectionId, user); - - try { - if (!this.sourceAudioStarted) { - this.userId = await this.mcs.join(this.voiceBridge, 'SFU', {}); - Logger.info(LOG_PREFIX, "MCS join for", this.id, "returned", this.userId); - - const ret = await this.mcs.publish(this.userId, - this.voiceBridge, - 'RtpEndpoint', - {descriptor: sdpOffer, adapter: 'Freeswitch', name: caleeName}); - - this.sourceAudio = ret.sessionId; - this.mcs.on('MediaEvent' + this.sourceAudio, this.mediaState.bind(this)); - this.mcs.on('ServerState' + this.sourceAudio, this.serverState.bind(this)); - this.sourceAudioStarted = true; - - Logger.info(LOG_PREFIX, "MCS publish for user", this.userId, "returned", this.sourceAudio); - } - - const retSubscribe = await this.mcs.subscribe(this.userId, - this.sourceAudio, - 'WebRtcEndpoint', - {descriptor: sdpOffer, adapter: 'Kurento'}); - - this.audioEndpoints[connectionId] = retSubscribe.sessionId; - - sdpAnswer = retSubscribe.answer; - this.flushCandidatesQueue(connectionId); - - this.mcs.on('MediaEvent' + retSubscribe.sessionId, (event) => { - this.mediaStateWebRtc(event, connectionId) - }); - - Logger.info(LOG_PREFIX, "MCS subscribe for user", this.userId, "returned", retSubscribe.sessionId); - - return resolve(sdpAnswer); - } - catch (err) { - return reject(this._handleError(LOG_PREFIX, err, "recv", userId)); - } - }); - } - - async stopListener(id) { - const listener = this.audioEndpoints[id]; - const userId = this.getUser(id); - Logger.info(LOG_PREFIX, 'Releasing endpoints for', listener); - - this.sendUserDisconnectedFromGlobalAudioMessage(id); - - if (listener) { - try { - if (this.audioEndpoints && Object.keys(this.audioEndpoints).length === 1) { - await this.mcs.leave(this.voiceBridge, this.userId); - this.sourceAudioStarted = false; - } - else { - await this.mcs.unsubscribe(this.userId, listener); - } - - delete this.candidatesQueue[id]; - delete this.audioEndpoints[id]; - - return; - } - catch (err) { - this._handleError(LOG_PREFIX, err, "recv", userId); - return; - } - } - } - - async stop () { - Logger.info(LOG_PREFIX, 'Releasing endpoints for user', this.userId, 'at room', this.voiceBridge); - - try { - await this.mcs.leave(this.voiceBridge, this.userId); - - for (var listener in this.audioEndpoints) { - delete this.audioEndpoints[listener]; - } - - for (var queue in this.candidatesQueue) { - delete this.candidatesQueue[queue]; - } - - for (var connection in this.connectedUsers) { - this.sendUserDisconnectedFromGlobalAudioMessage(connection); - } - - this.sourceAudioStarted = false; - - return Promise.resolve(); - } - catch (err) { - throw (this._handleError(LOG_PREFIX, err, "recv", this.userId)); - } - }; - - sendUserDisconnectedFromGlobalAudioMessage(connectionId) { - let user = this.getUser(connectionId); - let msg = Messaging.generateUserDisconnectedFromGlobalAudioMessage(this.voiceBridge, user.userId, user.userName); - Logger.info(LOG_PREFIX, 'Sending global audio disconnection for user', user); - - // Interoperability between transcoder messages - switch (C.COMMON_MESSAGE_VERSION) { - case "1.x": - this.bbbGW.publish(msg, C.TO_BBB_MEETING_CHAN, function(error) {}); - break; - default: - this.bbbGW.publish(msg, C.TO_AKKA_APPS_CHAN_2x, function(error) {}); - } - - this.removeUser(connectionId); - }; - - sendUserConnectedToGlobalAudioMessage(connectionId) { - let user = this.getUser(connectionId); - let msg = Messaging.generateUserConnectedToGlobalAudioMessage(this.voiceBridge, user.userId, user.userName); - Logger.info(LOG_PREFIX, 'Sending global audio connection for user', user); - - // Interoperability between transcoder messages - switch (C.COMMON_MESSAGE_VERSION) { - case "1.x": - this.bbbGW.publish(msg, C.TO_BBB_MEETING_CHAN, function(error) {}); - break; - default: - this.bbbGW.publish(msg, C.TO_AKKA_APPS_CHAN_2x, function(error) {}); - } - }; - - _onRtpMediaFlowing(connectionId) { - Logger.info(LOG_PREFIX, "RTP Media FLOWING for voice bridge", this.voiceBridge); - this.sendUserConnectedToGlobalAudioMessage(connectionId); - this.bbbGW.publish(JSON.stringify({ - connectionId: connectionId, - id: "webRTCAudioSuccess", - success: "MEDIA_FLOWING" - }), C.FROM_AUDIO); - }; - - _onRtpMediaNotFlowing(connectionId) { - Logger.warn(LOG_PREFIX, "RTP Media NOT FLOWING for voice bridge" + this.voiceBridge); - this.bbbGW.publish(JSON.stringify({ - connectionId: connectionId, - id: "webRTCAudioError", - error: C.MEDIA_ERROR - }), C.FROM_AUDIO); - this.removeUser(connectionId); - }; -}; diff --git a/labs/bbb-webrtc-sfu/lib/base/BaseManager.js b/labs/bbb-webrtc-sfu/lib/base/BaseManager.js deleted file mode 100644 index cae3a03460..0000000000 --- a/labs/bbb-webrtc-sfu/lib/base/BaseManager.js +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Lucas Fialho Zawacki - * Paulo Renato Lanzarin - * (C) Copyright 2017 Bigbluebutton - * - */ - -"use strict"; - -const BigBlueButtonGW = require('../bbb/pubsub/bbb-gw'); -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); -const errors = require('../base/errors'); - -module.exports = class BaseManager { - constructor (connectionChannel, additionalChannels = [], logPrefix = C.BASE_MANAGER_PREFIX) { - this._sessions = {}; - this._bbbGW = new BigBlueButtonGW(); - this._redisGateway; - this._connectionChannel = connectionChannel; - this._additionalChanels = additionalChannels; - this._logPrefix = logPrefix; - this._iceQueues = {}; - } - - async start() { - try { - // Additional channels that this manager is going to use - this._additionalChanels.forEach((channel) => { - this._bbbGW.addSubscribeChannel(channel); - }); - } - catch (error) { - Logger.error(this._logPrefix, 'Could not connect to Redis channel', error); - await this.stopAll(); - throw new Error(error); - } - } - - async messageFactory (handler) { - // Entrypoint for messages to the manager (from the connection-manager/ws module - this._redisGateway = await this._bbbGW.addSubscribeChannel(this._connectionChannel); - this._redisGateway.on(C.REDIS_MESSAGE, handler.bind(this)); - } - - _fetchSession (sessionId) { - return this._sessions[sessionId]; - } - - _fetchIceQueue (sessionId) { - if (this._iceQueues[sessionId] == null) { - this._iceQueues[sessionId] = []; - } - - return this._iceQueues[sessionId] ; - } - - _flushIceQueue (session, queue) { - if (queue) { - let candidate; - while(candidate = queue.pop()) { - session.onIceCandidate(candidate); - } - } - } - - _deleteIceQueue (sessionId) { - if (this._iceQueues[sessionId]) { - delete this._iceQueues[sessionId]; - } - } - - _killConnectionSessions (connectionId) { - const keys = Object.keys(this._sessions); - keys.forEach((sessionId) => { - let session = this._sessions[sessionId]; - if(session && session.connectionId === connectionId) { - let killedSessionId = session.connectionId + session.id + "-" + session.role; - this._stopSession(killedSessionId); - } - }); - } - - _stopSession (sessionId) { - return new Promise(async (resolve, reject) => { - Logger.info(this._logPrefix, 'Stopping session ' + sessionId); - try { - if (this._sessions == null|| sessionId == null) { - return resolve(); - } - - let session = this._sessions[sessionId]; - if(session) { - if (typeof session.stop === 'function') { - await session.stop(); - } - delete this._sessions[sessionId]; - this._logAvailableSessions(); - return resolve(); - } - } - catch (err) { - Logger.error(err); - return resolve(); - } - }); - } - - stopAll() { - return new Promise(async (resolve, reject) => { - try { - Logger.info(this._logPrefix, 'Stopping everything! '); - if (this._sessions == null) { - return resolve; - } - - let sessionIds = Object.keys(this._sessions); - let stopProcedures = []; - - for (let i = 0; i < sessionIds.length; i++) { - stopProcedures.push(this._stopSession(sessionIds[i])); - } - resolve(Promise.all(stopProcedures)); - } - catch (err) { - Logger.error(error); - resolve(); - } - }); - } - - _logAvailableSessions () { - if(this._sessions) { - let sessionMainKeys = Object.keys(this._sessions); - let logInfo = this._logPrefix + 'There are ' + sessionMainKeys.length + ' sessions available =>\n'; - for (var k in this._sessions) { - if(this._sessions[k]) { - logInfo += '(Session[' + k +']' + ' of type ' + this._sessions[k].constructor.name + ');\n'; - } - } - Logger.debug(logInfo); - } - } - - _handleError (logPrefix, connectionId, streamId, role, error) { - // Setting a default error in case it was unhandled - if (error == null) { - error = { code: 2200, reason: errors[2200] } - } - - if (error && this._validateErrorMessage(error)) { - return error; - } - - const { code } = error; - const reason = errors[code]; - - if (reason == null) { - return; - } - - error.message = reason; - - Logger.debug(logPrefix, "Handling error", error.code, error.message); - Logger.trace(logPrefix, error.stack); - - return this._assembleErrorMessage(error, role, streamId, connectionId); - } - - _assembleErrorMessage (error, role, streamId, connectionId) { - return { - connectionId, - type: this.sfuApp, - id: 'error', - role, - streamId, - code: error.code, - reason: error.message, - }; - } - - _validateErrorMessage (error) { - const { - connectionId = null, - type = null, - id = null, - role = null, - streamId = null, - code = null, - reason = null, - } = error; - return connectionId && type && id && role && streamId && code && reason; - } - -}; diff --git a/labs/bbb-webrtc-sfu/lib/base/BaseProcess.js b/labs/bbb-webrtc-sfu/lib/base/BaseProcess.js deleted file mode 100644 index b229c262b7..0000000000 --- a/labs/bbb-webrtc-sfu/lib/base/BaseProcess.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict' - -const Logger = require('../utils/Logger'); -const config = require('config'); -const C = require('../bbb/messages/Constants'); - -module.exports = class BaseProcess { - constructor(manager, logPrefix = C.BASE_PROCESS_PREFIX) { - this.runningState = "RUNNING"; - this.manager = manager; - this.logPrefix = logPrefix; - } - - start () { - this.manager.start(); - - if (config.get('acceptSelfSignedCertificate')) { - process.env.NODE_TLS_REJECT_UNAUTHORIZED=0; - } - - process.on('disconnect', this.stop.bind(this)); - process.on('SIGTERM', this.stop.bind(this)); - process.on('SIGINT', this.stop.bind(this)); - process.on('uncaughtException', this.handleException.bind(this)); - process.on('unhandledRejection', this.handleRejection.bind(this)); - } - - async stop () { - try { - this.runningState = "STOPPING"; - Promise.race([this.manager.stopAll(), this._failOver()]).then(() => { - Logger.info(this.logPrefix, "Exiting process with code 0"); - process.exit(); - }); - } - catch (err) { - Logger.error(this.logPrefix, err); - Logger.info(this.logPrefix, "Exiting process with code 1"); - process.exit(1); - } - } - - _failOver () { - return new Promise((resolve, reject) => { - setTimeout(resolve, 5000); - }); - } - - handleException (error) { - Logger.error(this.logPrefix, 'TODO => Uncaught exception', error.stack); - if (this.runningState === "STOPPING") { - Logger.warn(this.logPrefix, "Exiting process with code 1"); - process.exit(1); - } - } - - handleRejection (reason, promise) { - Logger.error(this.logPrefix, 'TODO => Unhandled Rejection at: Promise', promise, 'reason:', reason); - if (this.runningState === "STOPPING") { - Logger.warn(this.logPrefix, "Exiting process with code 1"); - process.exit(1); - } - } -} diff --git a/labs/bbb-webrtc-sfu/lib/base/BaseProvider.js b/labs/bbb-webrtc-sfu/lib/base/BaseProvider.js deleted file mode 100644 index cf89a4c71f..0000000000 --- a/labs/bbb-webrtc-sfu/lib/base/BaseProvider.js +++ /dev/null @@ -1,90 +0,0 @@ -"use strict"; - -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); -const EventEmitter = require('events').EventEmitter; -const errors = require('../base/errors'); -const config = require('config'); -const LOG_PREFIX = '[base-provider]'; - -module.exports = class BaseProvider extends EventEmitter { - constructor () { - super(); - this.sfuApp = "base"; - } - - serverState (event) { - let code = null; - const { eventTag } = { ...event }; - if (eventTag && eventTag.code) { - code = eventTag.code; - } - - switch (code) { - case C.MEDIA_SERVER_OFFLINE: - Logger.error(LOG_PREFIX, "Provider received MEDIA_SERVER_OFFLINE event"); - this.emit(C.MEDIA_SERVER_OFFLINE, event); - break; - - default: - Logger.warn(LOG_PREFIX, "Unknown server state", event); - } - } - - - _handleError (logPrefix, error, role, streamId) { - // Setting a default error in case it was unhandled - if (error == null) { - error = { code: 2200, reason: errors[2200] } - } - - if (this._validateErrorMessage(error)) { - return error; - } - - const { code } = error; - const reason = errors[code]; - - if (reason == null) { - return; - } - - error.message = reason; - - Logger.debug(logPrefix, "Handling error", error.code, error.message); - Logger.trace(logPrefix, error.stack); - - return this._assembleErrorMessage(error, role, streamId); - } - - _assembleErrorMessage (error, role, streamId) { - return { - type: this.sfuApp, - id: 'error', - role, - streamId, - code: error.code, - reason: error.message, - }; - } - - _validateErrorMessage (error) { - const { - type = null, - id = null, - role = null, - streamId = null, - code = null, - reason = null, - } = error; - return type && id && role && streamId && code && reason; - } - - getRecordingPath (room, subPath, recordingName) { - const format = config.get('recordingFormat'); - const basePath = config.get('recordingBasePath'); - const timestamp = (new Date()).getTime(); - - return `${basePath}/${subPath}/${room}/${recordingName}-${timestamp}.${format}` - } -}; diff --git a/labs/bbb-webrtc-sfu/lib/base/errors.js b/labs/bbb-webrtc-sfu/lib/base/errors.js deleted file mode 100644 index adabafbedf..0000000000 --- a/labs/bbb-webrtc-sfu/lib/base/errors.js +++ /dev/null @@ -1,30 +0,0 @@ -const errorCodes = { - 2000: "MEDIA_SERVER_CONNECTION_ERROR", - 2001: "MEDIA_SERVER_OFFLINE", - 2002: "MEDIA_SERVER_NO_RESOURCES", - 2003: "MEDIA_SERVER_REQUEST_TIMEOUT", - 2004: "MEDIA_SERVER_GENERIC_ERROR", - 2020: "ICE_ADD_CANDIDATE_FAILED", - 2021: "ICE_GATHERING_FAILED", - 2022: "ICE_STATE_FAILED", - 2200: "MEDIA_GENERIC_ERROR", - 2201: "MEDIA_NOT_FOUND", - 2202: "MEDIA_INVALID_SDP", - 2203: "MEDIA_NO_AVAILABLE_CODEC", - 2208: "MEDIA_GENERIC_PROCESS_ERROR", - 2209: "MEDIA_ADAPTER_OBJECT_NOT_FOUND", - 2210: "MEDIA_CONNECT_ERROR", - 2211: "MEDIA_NOT_FLOWING", - 2300: "SFU_INVALID_REQUEST", -} - -const expandErrors = () => { - let expandedErrors = Object.keys(errorCodes).reduce((map, key) => { - map[errorCodes[key]] = { code: key, reason: errorCodes[key] }; - return map; - }, {}); - - return { ...errorCodes, ...expandedErrors }; -} - -module.exports = expandErrors(); diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/Constants.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/Constants.js deleted file mode 100644 index 038a0c01dd..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/Constants.js +++ /dev/null @@ -1,180 +0,0 @@ -"use strict"; - -const config = require('config'); -/** - * @classdesc - * Message constants for the communication with BigBlueButton - * @constructor - */ - function Constants () { - return { - // Media elements - WEBRTC: "WebRtcEndpoint", - RTP: "RtpEndpoint", - AUDIO: "AUDIO", - VIDEO: "VIDEO", - ALL: "ALL", - - // SFU app types - SCREENSHARE_APP: 'screenshare', - VIDEO_APP: 'video', - AUDIO_APP: 'audio', - - // SFU requisition roles - SEND_ROLE: 'send', - RECV_ROLE: 'recv', - SEND_RECV_ROLE: 'sendrecv', - - // Redis channels - FROM_BBB_TRANSCODE_SYSTEM_CHAN : "bigbluebutton:from-bbb-transcode:system", - FROM_VOICE_CONF_SYSTEM_CHAN: "from-voice-conf-redis-channel", - TO_BBB_TRANSCODE_SYSTEM_CHAN: "bigbluebutton:to-bbb-transcode:system", - TO_BBB_MEETING_CHAN: "bigbluebutton:to-bbb-apps:meeting", - FROM_BBB_MEETING_CHAN: "bigbluebutton:from-bbb-apps:meeting", - TO_AKKA_APPS_CHAN_2x: "to-akka-apps-redis-channel", - FROM_SCREENSHARE: config.get('from-screenshare'), - TO_SCREENSHARE: config.get('to-screenshare'), - FROM_VIDEO: config.get('from-video'), - TO_VIDEO: config.get('to-video'), - FROM_AUDIO: config.get('from-audio'), - TO_AUDIO: config.get('to-audio'), - TO_AKKA_APPS: config.get('to-akka'), - FROM_AKKA_APPS: config.get('from-akka'), - - // RedisWrapper events - REDIS_MESSAGE : "redis_message", - WEBSOCKET_MESAGE: "ws_message", - GATEWAY_MESSAGE: "gateway_message", - - RECORDING_STATUS_REQUEST_MESSAGE_2x: "GetRecordingStatusReqMsg", - RECORDING_STATUS_REPLY_MESSAGE_2x: "GetRecordingStatusRespMsg", - - // Message identifiers 1x - START_TRANSCODER_REQUEST: "start_transcoder_request_message", - START_TRANSCODER_REPLY: "start_transcoder_reply_message", - STOP_TRANSCODER_REQUEST: "stop_transcoder_request_message", - STOP_TRANSCODER_REPLY: "stop_transcoder_reply_message", - DESKSHARE_RTMP_BROADCAST_STARTED: "deskshare_rtmp_broadcast_started_message", - DESKSHARE_RTMP_BROADCAST_STOPPED: "deskshare_rtmp_broadcast_stopped_message", - GLOBAL_AUDIO_CONNECTED: "user_connected_to_global_audio", - GLOBAL_AUDIO_DISCONNECTED: "user_disconnected_from_global_audio", - DICONNECT_ALL_USERS: "disconnect_all_users_message", - - //Message identifiers 2x - SCREENSHARE_RTMP_BROADCAST_STARTED_2x: "ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg", - SCREENSHARE_RTMP_BROADCAST_STOPPED_2x: "ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg", - START_TRANSCODER_REQ_2x: "StartTranscoderSysReqMsg", - START_TRANSCODER_RESP_2x: "StartTranscoderSysRespMsg", - STOP_TRANSCODER_REQ_2x: "StopTranscoderSysReqMsg", - STOP_TRANSCODER_RESP_2x: "StopTranscoderSysRespMsg", - GLOBAL_AUDIO_CONNECTED_2x: "UserConnectedToGlobalAudioMsg", - GLOBAL_AUDIO_DISCONNECTED_2x: "UserDisconnectedFromGlobalAudioMsg", - DICONNECT_ALL_USERS_2x: "DisconnectAllClientsSysMsg", - USER_CAM_BROADCAST_STOPPED_2x: "UserBroadcastCamStopMsg", - PRESENTER_ASSIGNED_2x: "PresenterAssignedEvtMsg", - - STREAM_IS_RECORDED: "StreamIsRecordedMsg", - - START_WEBCAM_SHARE: "StartWebRTCShareEvent", - STOP_WEBCAM_SHARE: "StopWebRTCShareEvent", - - // Redis messages fields - // Transcoder 1x - USER_ID : "user_id", - OPTIONS: "options", - VOICE_CONF_ID : "voice_conf_id", - TRANSCODER_ID : "transcoder_id", - - // Transcoder 2x - USER_ID_2x : "userId", - TRANSCODER_ID_2x : "transcoderId", - MEETING_ID_2x: "meetingId", - - // Akka Apps 2x - REQUESTED_BY: "requestedBy", - - // Screenshare 2x - CONFERENCE_NAME: "voiceConf", - SCREENSHARE_CONF: "screenshareConf", - STREAM_URL: "stream", - TIMESTAMP: "timestamp", - VIDEO_WIDTH: "vidWidth", - VIDEO_HEIGHT: "vidHeight", - - // Audio - NAME: "name", - USERID: "userid", - - // RTP params - MEETING_ID : "meeting_id", - VOICE_CONF : "voice_conf", - KURENTO_ENDPOINT_ID : "kurento_endpoint_id", - PARAMS : "params", - MEDIA_DESCRIPTION: "media_description", - LOCAL_IP_ADDRESS: "local_ip_address", - LOCAL_VIDEO_PORT: "local_video_port", - DESTINATION_IP_ADDRESS : "destination_ip_address", - DESTINATION_VIDEO_PORT : "destination_video_port", - REMOTE_VIDEO_PORT : "remote_video_port", - CODEC_NAME: "codec_name", - CODEC_ID: "codec_id", - CODEC_RATE: "codec_rate", - RTP_PROFILE: "rtp_profile", - SEND_RECEIVE: "send_receive", - FRAME_RATE: "frame_rate", - INPUT: "input", - KURENTO_TOKEN : "kurento_token", - SCREENSHARE: "deskShare", - STREAM_TYPE: "stream_type", - STREAM_TYPE_SCREENSHARE: "stream_type_deskshare", - STREAM_TYPE_VIDEO: "stream_type_video", - RTP_TO_RTMP: "transcode_rtp_to_rtmp", - TRANSCODER_CODEC: "codec", - TRANSCODER_TYPE: "transcoder_type", - CALLERNAME: "callername", - - EVENT_NAME: 'eventName', - - TIMESTAMP: 'timestamp', - TIMESTAMP_UTC: 'timestampUTC', - - MODULE: 'module', - MODULE_WEBCAM: 'bbb-webrtc-sfu', - - FILENAME: 'filename', - - // Log prefixes - BASE_PROCESS_PREFIX: '[BaseProcess]', - BASE_MANAGER_PREFIX: '[BaseManager]', - BASE_PROVIDER_PREFIX: '[BaseProvider]', - SCREENSHARE_PROCESS_PREFIX: '[ScreenshareProcess]', - SCREENSHARE_MANAGER_PREFIX: '[ScreenshareManager]', - SCREENSHARE_PROVIDER_PREFIX: '[ScreenshareProvider]', - VIDEO_PROCESS_PREFIX: '[VideoProcess]', - VIDEO_MANAGER_PREFIX: '[VideoManager]', - VIDEO_PROVIDER_PREFIX: '[VideoProvider]', - AUDIO_PROCESS_PREFIX: '[AudioProcess]', - AUDIO_MANAGER_PREFIX: '[AudioManager]', - AUDIO_PROVIDER_PREFIX: '[AudioProvider]', - - // MCS error codes - MEDIA_SERVER_OFFLINE: 2001, - - // Media states' - MEDIA_FLOWING_IN: 'MEDIA_FLOWING_IN', - MEDIA_FLOWING_OUT: 'MEDIA_FLOWING_OUT', - MEDIA_NOT_FLOWING_IN: 'MEDIA_NOT_FLOWING_IN', - MEDIA_NOT_FLOWING_OUT: 'MEDIA_NOT_FLOWING_OUT', - MEDIA_CONNECTED: 'MEDIA_CONNECTED', - MEDIA_DISCONNECTED: 'MEDIA_DISCONNECTED', - ON_ICE_CANDIDATE: 'ON_ICE_CANDIDATE', - - MEDIA_STARTED: 'MEDIA_STARTED', - MEDIA_STOPPED: 'MEDIA_STOPPED', - MEDIA_STARTING: 'MEDIA_STARTING', - MEDIA_PAUSED: 'MEDIA_PAUSE' - } -} - -module.exports = Constants(); - diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/Messaging.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/Messaging.js deleted file mode 100644 index af8225fa61..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/Messaging.js +++ /dev/null @@ -1,124 +0,0 @@ -const Constants = require('./Constants.js'); - -// Messages - -let OutMessage = require('./OutMessage.js'); - -let StartTranscoderRequestMessage = - require('./transcode/StartTranscoderRequestMessage.js')(Constants); -let StopTranscoderRequestMessage = - require('./transcode/StopTranscoderRequestMessage.js')(Constants); -let StartTranscoderSysReqMsg = - require('./transcode/StartTranscoderSysReqMsg.js')(); -let StopTranscoderSysReqMsg = - require('./transcode/StopTranscoderSysReqMsg.js')(); -let DeskShareRTMPBroadcastStartedEventMessage = - require('./screenshare/DeskShareRTMPBroadcastStartedEventMessage.js')(Constants); -let DeskShareRTMPBroadcastStoppedEventMessage = - require('./screenshare/DeskShareRTMPBroadcastStoppedEventMessage.js')(Constants); -let ScreenshareRTMPBroadcastStartedEventMessage2x = - require('./screenshare/ScreenshareRTMPBroadcastStartedEventMessage2x.js')(Constants); -let ScreenshareRTMPBroadcastStoppedEventMessage2x = - require('./screenshare/ScreenshareRTMPBroadcastStoppedEventMessage2x.js')(Constants); -let UserCamBroadcastStoppedEventMessage2x = - require('./video/UserCamBroadcastStoppedEventMessage2x.js')(Constants); -let WebRTCShareEvent = require('./video/WebRTCShareEvent.js')(Constants); -let RecordingStatusRequestMessage2x = - require('./recording/RecordingStatusRequestMessage2x.js')(Constants); -let UserConnectedToGlobalAudio = - require('./audio/UserConnectedToGlobalAudio.js')(Constants); -let UserDisconnectedFromGlobalAudio = - require('./audio/UserDisconnectedFromGlobalAudio.js')(Constants); -let UserConnectedToGlobalAudio2x = - require('./audio/UserConnectedToGlobalAudio2x.js')(Constants); -let UserDisconnectedFromGlobalAudio2x = - require('./audio/UserDisconnectedFromGlobalAudio2x.js')(Constants); - - /** - * @classdesc - * Messaging utils to assemble JSON/Redis BigBlueButton messages - * @constructor - */ -function Messaging() {} - -Messaging.prototype.generateStartTranscoderRequestMessage = - function(meetingId, transcoderId, params) { - let statrm = new StartTranscoderSysReqMsg(meetingId, transcoderId, params); - return statrm.toJson(); -} - -Messaging.prototype.generateStopTranscoderRequestMessage = - function(meetingId, transcoderId) { - let stotrm = new StopTranscoderSysReqMsg(meetingId, transcoderId); - return stotrm.toJson(); -} - -Messaging.prototype.generateDeskShareRTMPBroadcastStartedEvent = - function(conferenceName, streamUrl, vw, vh, timestamp) { - let stadrbem = new DeskShareRTMPBroadcastStartedEventMessage(conferenceName, streamUrl, vw, vh, timestamp); - return stadrbem.toJson(); -} - -Messaging.prototype.generateDeskShareRTMPBroadcastStoppedEvent = - function(conferenceName, streamUrl, vw, vh, timestamp) { - let stodrbem = new DeskShareRTMPBroadcastStoppedEventMessage(conferenceName, streamUrl, vw, vh, timestamp); - return stodrbem.toJson(); -} - -Messaging.prototype.generateScreenshareRTMPBroadcastStartedEvent2x = - function(conferenceName, screenshareConf, streamUrl, vw, vh, timestamp) { - let stadrbem = new ScreenshareRTMPBroadcastStartedEventMessage2x(conferenceName, screenshareConf, streamUrl, vw, vh, timestamp); - return stadrbem.toJson(); -} - -Messaging.prototype.generateScreenshareRTMPBroadcastStoppedEvent2x = - function(conferenceName, screenshareConf, streamUrl, vw, vh, timestamp) { - let stodrbem = new ScreenshareRTMPBroadcastStoppedEventMessage2x(conferenceName, screenshareConf, streamUrl, vw, vh, timestamp); - return stodrbem.toJson(); -} - -Messaging.prototype.generateUserCamBroadcastStoppedEventMessage2x = - function(meetingId, userId, streamUrl) { - let stodrbem = new UserCamBroadcastStoppedEventMessage2x(meetingId, userId, streamUrl); - return stodrbem.toJson(); -} - -Messaging.prototype.generateWebRTCShareEvent = - function(name, meetingId, streamUrl) { - let stodrbem = new WebRTCShareEvent(name, meetingId, streamUrl); - return stodrbem.payload; -} - -Messaging.prototype.generateRecordingStatusRequestMessage = - function(meetingId, userId = '') { - let rsqm = new RecordingStatusRequestMessage2x(meetingId, userId); - return rsqm.toJson(); -} - -Messaging.prototype.generateUserConnectedToGlobalAudioMessage = - function(voiceConf, userId, name) { - let msg; - switch (Constants.COMMON_MESSAGE_VERSION) { - case "1.x": - msg = new UserConnectedToGlobalAudio(voiceConf, userId, name); - break; - default: - msg = new UserConnectedToGlobalAudio2x(voiceConf, userId, name); - } - return msg.toJson(); -} - -Messaging.prototype.generateUserDisconnectedFromGlobalAudioMessage = - function(voiceConf, userId, name) { - let msg; - switch (Constants.COMMON_MESSAGE_VERSION) { - case "1.x": - msg = new UserDisconnectedFromGlobalAudio(voiceConf, userId, name); - break; - default: - msg = new UserDisconnectedFromGlobalAudio2x(voiceConf, userId, name); - } - return msg.toJson(); -} - -module.exports = new Messaging(); diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/OutMessage.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/OutMessage.js deleted file mode 100644 index 04776dca76..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/OutMessage.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * (C) Copyright 2016 Mconf Tecnologia (http://mconf.com/) - */ - -/** - * @classdesc - * Base class for output messages sent to BBB - * @constructor - */ -function OutMessage(messageName) { - /** - * The header template of the message - * @type {Object} - */ - this.header = { - version: "0.0.1", - name: messageName - }; - - /** - * The payload of the message - * @type {Object} - */ - this.payload = null; - - /** - * Generates the JSON representation of the message - * @return {String} The JSON string of this message - */ - this.toJson = function () { - return JSON.stringify(this); - } -}; - -module.exports = OutMessage; diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/OutMessage2x.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/OutMessage2x.js deleted file mode 100644 index a6f9cafcfc..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/OutMessage2x.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * (C) Copyright 2016 Mconf Tecnologia (http://mconf.com/) - */ - -/** - * @classdesc - * Base class for output messages sent to BBB - * 2x model - * @constructor - */ -function OutMessage2x(messageName, routing, headerFields) { - - - this.envelope = { - name: messageName, - routing: routing - } - /** - * The header template of the message - * @type {Object} - */ - this.core = { - header : { - name: messageName - } - } - - // Copy header fiels to the header object - var keys1 = Object.keys(headerFields); - for (var k=0; k < keys1.length; k++) { - var key = keys1[k]; - if (typeof this.core.header[key] === 'undefined') { - this.core.header[key] = headerFields[key]; - } - } - - /** - * The body of the message - * @type {Object} - */ - this.core.body = null; - - /** - * Generates the JSON representation of the message - * @return {String} The JSON string of this message - */ - this.toJson = function () { - return JSON.stringify(this); - } -}; - -module.exports = OutMessage2x; diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserConnectedToGlobalAudio.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserConnectedToGlobalAudio.js deleted file mode 100644 index d9b868bc7e..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserConnectedToGlobalAudio.js +++ /dev/null @@ -1,16 +0,0 @@ -var inherits = require('inherits'); -var OutMessage = require('../OutMessage'); - -module.exports = function(Constants) { - function UserConnectedToGlobalAudio(voiceConf, userId, name) { - UserConnectedToGlobalAudio.super_.call(this, Constants.GLOBAL_AUDIO_CONNECTED); - - this.payload = {}; - this.payload[Constants.VOICE_CONF] = voiceConf; - this.payload[Constants.USERID] = userId; - this.payload[Constants.NAME] = name; - }; - - inherits(UserConnectedToGlobalAudio, OutMessage); - return UserConnectedToGlobalAudio; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserConnectedToGlobalAudio2x.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserConnectedToGlobalAudio2x.js deleted file mode 100644 index b5f318a922..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserConnectedToGlobalAudio2x.js +++ /dev/null @@ -1,21 +0,0 @@ -var inherits = require('inherits'); -var OutMessage2x = require('../OutMessage2x'); - -// TODO: Check if this is correct! -module.exports = function(Constants) { - function UserConnectedToGlobalAudio2x(voiceConf, userId, name) { - UserConnectedToGlobalAudio2x.super_.call( - this, - Constants.GLOBAL_AUDIO_CONNECTED_2x, - {voiceConf: voiceConf}, - {voiceConf: voiceConf} - ); - - this.core.body = {}; - this.core.body[Constants.USER_ID_2x] = userId; - this.core.body[Constants.NAME] = name; - }; - - inherits(UserConnectedToGlobalAudio2x, OutMessage2x); - return UserConnectedToGlobalAudio2x; -}; diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserDisconnectedFromGlobalAudio.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserDisconnectedFromGlobalAudio.js deleted file mode 100644 index a58bffb3b3..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserDisconnectedFromGlobalAudio.js +++ /dev/null @@ -1,16 +0,0 @@ -var inherits = require('inherits'); -var OutMessage = require('../OutMessage'); - -module.exports = function(Constants) { - function UserDisconnectedFromGlobalAudio(voiceConf, userId, name) { - UserDisconnectedFromGlobalAudio.super_.call(this, Constants.GLOBAL_AUDIO_DISCONNECTED); - - this.payload = {}; - this.payload[Constants.VOICE_CONF] = voiceConf; - this.payload[Constants.USERID] = userId; - this.payload[Constants.NAME] = name; - }; - - inherits(UserDisconnectedFromGlobalAudio, OutMessage); - return UserDisconnectedFromGlobalAudio; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserDisconnectedFromGlobalAudio2x.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserDisconnectedFromGlobalAudio2x.js deleted file mode 100644 index e79b277235..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/audio/UserDisconnectedFromGlobalAudio2x.js +++ /dev/null @@ -1,21 +0,0 @@ -var inherits = require('inherits'); -var OutMessage2x = require('../OutMessage2x'); - -// TODO: Check if this is correct! -module.exports = function(Constants) { - function UserDisconnectedFromGlobalAudio2x(voiceConf, userId, name) { - UserDisconnectedFromGlobalAudio2x.super_.call( - this, - Constants.GLOBAL_AUDIO_DISCONNECTED_2x, - {voiceConf: voiceConf}, - {voiceConf: voiceConf} - ); - - this.core.body = {}; - this.core.body[Constants.USER_ID_2x] = userId; - this.core.body[Constants.NAME] = name; - }; - - inherits(UserDisconnectedFromGlobalAudio2x, OutMessage2x); - return UserDisconnectedFromGlobalAudio2x; -}; diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/recording/RecordingStatusRequestMessage2x.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/recording/RecordingStatusRequestMessage2x.js deleted file mode 100644 index 2f7e7f2c56..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/recording/RecordingStatusRequestMessage2x.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * - */ - -var inherits = require('inherits'); -var OutMessage2x = require('../OutMessage2x'); - -module.exports = function (C) { - function RecordingStatusRequestMessage2x (meetingId, userId) { - RecordingStatusRequestMessage2x.super_.call(this, C.RECORDING_STATUS_REQUEST_MESSAGE_2x, {sender: 'bbb-webrtc-sfu'}, {meetingId, userId}); - - this.core.body = {}; - this.core.body[C.REQUESTED_BY] = userId; - }; - - inherits(RecordingStatusRequestMessage2x, OutMessage2x); - return RecordingStatusRequestMessage2x; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/DeskShareRTMPBroadcastStartedEventMessage.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/DeskShareRTMPBroadcastStartedEventMessage.js deleted file mode 100644 index 34579de0bf..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/DeskShareRTMPBroadcastStartedEventMessage.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * - */ - -var inherits = require('inherits'); -var OutMessage = require('../OutMessage'); - -module.exports = function (Constants) { - function DeskShareRTMPBroadcastStartedEventMessage (conferenceName, streamUrl, vw, vh, timestamp) { - DeskShareRTMPBroadcastStartedEventMessage.super_.call(this, Constants.DESKSHARE_RTMP_BROADCAST_STARTED); - - this.payload = {}; - this.payload[Constants.CONFERENCE_NAME] = conferenceName; - this.payload[Constants.STREAM_URL] = streamUrl; - this.payload[Constants.TIMESTAMP] = timestamp; - this.payload[Constants.VIDEO_WIDTH] = vw; - this.payload[Constants.VIDEO_HEIGHT] = vh; - }; - - inherits(DeskShareRTMPBroadcastStartedEventMessage, OutMessage); - return DeskShareRTMPBroadcastStartedEventMessage; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/DeskShareRTMPBroadcastStoppedEventMessage.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/DeskShareRTMPBroadcastStoppedEventMessage.js deleted file mode 100644 index 81e7125db7..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/DeskShareRTMPBroadcastStoppedEventMessage.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * - */ - -var inherits = require('inherits'); -var OutMessage = require('../OutMessage'); - -module.exports = function (Constants) { - function DeskShareRTMPBroadcastStoppedEventMessage (conferenceName, streamUrl, vw, vh, timestamp) { - DeskShareRTMPBroadcastStoppedEventMessage.super_.call(this, Constants.DESKSHARE_RTMP_BROADCAST_STOPPED); - - this.payload = {}; - this.payload[Constants.CONFERENCE_NAME] = conferenceName; - this.payload[Constants.STREAM_URL] = streamUrl; - this.payload[Constants.TIMESTAMP] = timestamp; - this.payload[Constants.VIDEO_WIDTH] = vw; - this.payload[Constants.VIDEO_HEIGHT] = vh; - }; - - inherits(DeskShareRTMPBroadcastStoppedEventMessage, OutMessage); - return DeskShareRTMPBroadcastStoppedEventMessage; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/ScreenshareRTMPBroadcastStartedEventMessage2x.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/ScreenshareRTMPBroadcastStartedEventMessage2x.js deleted file mode 100644 index 28867a2265..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/ScreenshareRTMPBroadcastStartedEventMessage2x.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * - */ - -var inherits = require('inherits'); -var OutMessage2x = require('../OutMessage2x'); - -module.exports = function (C) { - function ScreenshareRTMPBroadcastStartedEventMessage2x (conferenceName, screenshareConf, - streamUrl, vw, vh, timestamp) { - ScreenshareRTMPBroadcastStartedEventMessage2x.super_.call(this, C.SCREENSHARE_RTMP_BROADCAST_STARTED_2x, - {voiceConf: conferenceName}, {voiceConf: conferenceName}); - - this.core.body = {}; - this.core.body[C.CONFERENCE_NAME] = conferenceName; - this.core.body[C.SCREENSHARE_CONF] = screenshareConf; - this.core.body[C.STREAM_URL] = streamUrl; - this.core.body[C.VIDEO_WIDTH] = vw; - this.core.body[C.VIDEO_HEIGHT] = vh; - this.core.body[C.TIMESTAMP] = timestamp; - }; - - inherits(ScreenshareRTMPBroadcastStartedEventMessage2x, OutMessage2x); - return ScreenshareRTMPBroadcastStartedEventMessage2x; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/ScreenshareRTMPBroadcastStoppedEventMessage2x.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/ScreenshareRTMPBroadcastStoppedEventMessage2x.js deleted file mode 100644 index d02dfb2cd3..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/screenshare/ScreenshareRTMPBroadcastStoppedEventMessage2x.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * - */ - -var inherits = require('inherits'); -var OutMessage2x = require('../OutMessage2x'); - -module.exports = function (C) { - function ScreenshareRTMPBroadcastStoppedEventMessage2x (conferenceName, screenshareConf, - streamUrl, vw, vh, timestamp) { - ScreenshareRTMPBroadcastStoppedEventMessage2x.super_.call(this, C.SCREENSHARE_RTMP_BROADCAST_STOPPED_2x, - {voiceConf: conferenceName}, {voiceConf: conferenceName}); - - this.core.body = {}; - this.core.body[C.CONFERENCE_NAME] = conferenceName; - this.core.body[C.SCREENSHARE_CONF] = screenshareConf; - this.core.body[C.STREAM_URL] = streamUrl; - this.core.body[C.VIDEO_WIDTH] = vw; - this.core.body[C.VIDEO_HEIGHT] = vh; - this.core.body[C.TIMESTAMP] = timestamp; - }; - - inherits(ScreenshareRTMPBroadcastStoppedEventMessage2x, OutMessage2x); - return ScreenshareRTMPBroadcastStoppedEventMessage2x; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StartTranscoderRequestMessage.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StartTranscoderRequestMessage.js deleted file mode 100644 index 69d5f0890f..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StartTranscoderRequestMessage.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * - */ - -var inherits = require('inherits'); -var OutMessage = require('../OutMessage'); - -module.exports = function (Constants) { - function StartTranscoderRequestMessage (meetingId, transcoderId, params) { - StartTranscoderRequestMessage.super_.call(this, Constants.START_TRANSCODER_REQUEST); - - this.payload = {}; - this.payload[Constants.MEETING_ID] = meetingId; - this.payload[Constants.TRANSCODER_ID] = transcoderId; - this.payload[Constants.PARAMS] = params; - }; - - inherits(StartTranscoderRequestMessage, OutMessage); - return StartTranscoderRequestMessage; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StartTranscoderSysReqMsg.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StartTranscoderSysReqMsg.js deleted file mode 100644 index bd517a1490..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StartTranscoderSysReqMsg.js +++ /dev/null @@ -1,18 +0,0 @@ -var inherits = require('inherits'); -var OutMessage2x = require('../OutMessage2x'); -var C = require('../Constants'); - -module.exports = function() { - function StartTranscoderSysReqMsg(meetingId, transcoderId, params) { - StartTranscoderSysReqMsg.super_.call(this, C.START_TRANSCODER_REQ_2x, - {sender: "kurento-screenshare"}, - {meetingId: meetingId}); - - this.core.body = {}; - this.core.body[C.TRANSCODER_ID_2x] = transcoderId; - this.core.body[C.PARAMS] = params; - }; - - inherits(StartTranscoderSysReqMsg, OutMessage2x); - return StartTranscoderSysReqMsg; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StopTranscoderRequestMessage.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StopTranscoderRequestMessage.js deleted file mode 100644 index ad030d2c62..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StopTranscoderRequestMessage.js +++ /dev/null @@ -1,15 +0,0 @@ -var inherits = require('inherits'); -var OutMessage = require('../OutMessage'); - -module.exports = function (Constants) { - function StopTranscoderRequestMessage (meetingId, transcoderId) { - StopTranscoderRequestMessage.super_.call(this, Constants.STOP_TRANSCODER_REQUEST); - - this.payload = {}; - this.payload[Constants.MEETING_ID] = meetingId; - this.payload[Constants.TRANSCODER_ID] = transcoderId; - }; - - inherits(StopTranscoderRequestMessage, OutMessage); - return StopTranscoderRequestMessage; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StopTranscoderSysReqMsg.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StopTranscoderSysReqMsg.js deleted file mode 100644 index 639b415792..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/transcode/StopTranscoderSysReqMsg.js +++ /dev/null @@ -1,17 +0,0 @@ -var inherits = require('inherits'); -var OutMessage2x = require('../OutMessage2x'); -var C = require('../Constants'); - -module.exports = function() { - function StopTranscoderSysReqMsg(meetingId, transcoderId) { - StopTranscoderSysReqMsg.super_.call(this, C.STOP_TRANSCODER_REQ_2x, - {sender: "kurento-screenshare"}, - {meetingId: meetingId}); - - this.core.body = {}; - this.core.body[C.TRANSCODER_ID_2x] = transcoderId; - }; - - inherits(StopTranscoderSysReqMsg, OutMessage2x); - return StopTranscoderSysReqMsg; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/video/UserCamBroadcastStoppedEventMessage2x.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/video/UserCamBroadcastStoppedEventMessage2x.js deleted file mode 100644 index a4a3202500..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/video/UserCamBroadcastStoppedEventMessage2x.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * - */ - -var inherits = require('inherits'); -var OutMessage2x = require('../OutMessage2x'); - -module.exports = function (C) { - function UserCamBroadcastStoppedEventMessage2x (meetingId, userId, stream) { - UserCamBroadcastStoppedEventMessage2x.super_.call(this, C.USER_CAM_BROADCAST_STOPPED_2x, {sender: 'bbb-webrtc-sfu'}, {meetingId, userId}); - - this.core.body = {}; - this.core.body[C.STREAM_URL] = stream; - }; - - inherits(UserCamBroadcastStoppedEventMessage2x, OutMessage2x); - return UserCamBroadcastStoppedEventMessage2x; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/messages/video/WebRTCShareEvent.js b/labs/bbb-webrtc-sfu/lib/bbb/messages/video/WebRTCShareEvent.js deleted file mode 100644 index 1d43a68d0b..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/messages/video/WebRTCShareEvent.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - */ - -var hrTime = require('../../../utils/Utils').hrTime; - -module.exports = function (C) { - function WebRTCShareEvent (name, meetingId, filename) { - - let date = new Date(); - let timestamp = hrTime(); - - this.payload = {}; - this.payload[C.EVENT_NAME] = name; - this.payload[C.MODULE] = C.MODULE_WEBCAM; - this.payload[C.MEETING_ID] = meetingId; - this.payload[C.TIMESTAMP] = timestamp; - this.payload[C.TIMESTAMP_UTC] = date.getTime() ; - this.payload[C.FILENAME] = filename; - }; - - return WebRTCShareEvent; -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/pubsub/RedisWrapper.js b/labs/bbb-webrtc-sfu/lib/bbb/pubsub/RedisWrapper.js deleted file mode 100644 index 5e14fba7fe..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/pubsub/RedisWrapper.js +++ /dev/null @@ -1,186 +0,0 @@ -/** - * @classdesc - * Redis wrapper class for connecting to Redis channels - */ - -'use strict'; - -/* Modules */ - -const redis = require('redis'); -const config = require('config'); -const Constants = require('../messages/Constants.js'); -const EventEmitter = require('events').EventEmitter; -const Logger = require('../../utils/Logger'); - -/* Public members */ - -module.exports = class RedisWrapper extends EventEmitter { - constructor(subpattern) { - super(); - // Redis PubSub client holders - this.redisCli = null; - this.redisPub = null; - // Pub and Sub channels/patterns - this.subpattern = subpattern; - } - - static get _retryThreshold() { - return 1000 * 60 * 60; - } - - static get _maxRetries() { - return 10; - } - - startPublisher () { - var options = { - host : config.get('redisHost'), - port : config.get('redisPort'), - //password: config.get('redis.password') - retry_strategy: this._redisRetry - }; - - this.redisPub = redis.createClient(options); - } - - startSubscriber () { - let self = this; - if (this.redisCli) { - Logger.warn("[RedisWrapper] Redis Client already exists"); - return; - } - - var options = { - host : config.get('redisHost'), - port : config.get('redisPort'), - //password: config.get('redis.password') - retry_strategy: this._redisRetry - }; - - this.redisCli = redis.createClient(options); - - this.redisCli.on("connect", () => { - //TODO - }); - - this.redisCli.on("error", (error) => { - Logger.error("[RedisWrapper] Wrapper returned an error", error); - }); - - this.redisCli.on("reconnecting", (msg) => { - Logger.warn("[RedisWrapper] Wrapper instance is reconnecting", msg); - //TODO - }); - - this.redisCli.on("psubscribe", (channel, count) => { - Logger.info("[RedisWrapper] Successfully subscribed to pattern [" + channel + "]"); - }); - - this.redisCli.on("pmessage", this._onMessage.bind(this)); - - if (!this.subpattern) { - throw new Error("[RedisWrapper] No subscriber pattern"); - } - - this.redisCli.psubscribe(this.subpattern); - - Logger.info("[RedisWrapper] Started Redis client at " + options.host + ":" + options.port + - " for subscription pattern: " + this.subpattern); - } - - stopRedis (callback) { - if (this.redisCli){ - this.redisCli.quit(); - } - callback(false); - } - - publishToChannel (_message, channel) { - let message = _message; - if(this.redisPub) { - this.redisPub.publish(channel, message); - } - } - - pushToList (key, string, callback) { - if (this.redisPub) { - this.redisPub.rpush(key, string, callback); - } else { - callback(true, null) - } - } - - setKeyWithIncrement (key, message, callback) { - let blowObject = function (obj) { - let arr = []; - Object.keys(obj).map(function (key) { - arr.push(key) - arr.push(obj[key]); - }); - return arr; - } - - if (this.redisPub){ - this.redisPub.incr('global:nextRecordedMsgId', (err, msgId) => { - if (err) { - return callback(err, null); - } - - let incr = key + ':' + msgId; - let value = blowObject(message); - this.redisPub.hmset(incr, value, (err) => { - if (err) { - return callback(err, null); - } - - // Return the increment number to the caller - callback(err, msgId); - }); - }); - - } else { - callback(true, null); - } - } - - expireKey (key, seconds, callback) { - if (this.redisPub) { - this.redisPub.expire(key, seconds, callback); - } else { - callback(true, null); - } - } - - getChannels () { - return new Promise((resolve, reject) => { - this.redisPub.pubsub('channels', (error, channels) => { - if (error) { - return reject(error); - } - return resolve(channels); - }); - }); - } - - /* Private members */ - - _onMessage (pattern, channel, _message) { - let message = (typeof _message !== 'object')?JSON.parse(_message):_message; - // use event emitter to throw new message - this.emit(Constants.REDIS_MESSAGE, message); - } - - static _redisRetry (options) { - // if (options.error && options.error.code === 'ECONNREFUSED') { - // return new Error('The server refused the connection'); - // } - if (options.total_retry_time > RedisWrapper._retryThreshold) { - return new Error('Retry time exhausted'); - } - if (options.times_connected > RedisWrapper._maxRetries) { - return undefined; - } - return Math.max(options.attempt * 100, 3000); - } -} diff --git a/labs/bbb-webrtc-sfu/lib/bbb/pubsub/bbb-gw.js b/labs/bbb-webrtc-sfu/lib/bbb/pubsub/bbb-gw.js deleted file mode 100644 index bf6c4d062f..0000000000 --- a/labs/bbb-webrtc-sfu/lib/bbb/pubsub/bbb-gw.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * @classdesc - * BigBlueButton redis gateway for bbb-screenshare node app - */ - -'use strict'; - -/* Modules */ - -const C = require('../messages/Constants.js'); -const RedisWrapper = require('./RedisWrapper.js'); -const config = require('config'); -const util = require('util'); -const EventEmitter = require('events').EventEmitter; -const Logger = require('../../utils/Logger'); - -let instance = null; - -module.exports = class BigBlueButtonGW extends EventEmitter { - constructor() { - if(!instance){ - super(); - this.subscribers = {}; - this.publisher = null; - instance = this; - } - - return instance; - } - - addSubscribeChannel (channel) { - if (this.subscribers[channel]) { - return this.subscribers[channel]; - } - - let wrobj = new RedisWrapper(channel); - this.subscribers[channel] = {}; - this.subscribers[channel] = wrobj; - try { - wrobj.startSubscriber(); - wrobj.on(C.REDIS_MESSAGE, this.incomingMessage.bind(this)); - Logger.info("[BigBlueButtonGW] Added redis client to this.subscribers[" + channel + "]"); - return Promise.resolve(wrobj); - } - catch (error) { - return Promise.reject("[BigBlueButtonGW] Could not start redis client for channel " + channel); - } - } - - /** - * Capture messages from subscribed channels and emit an event with it's - * identifier and payload. Check Constants.js for the identifiers. - * - * @param {Object} message Redis message - */ - incomingMessage (message) { - let header; - let payload; - let msg = (typeof message !== 'object')?JSON.parse(message):message; - let meetingId; - - // Trying to parse both message types, 1x and 2x - if (msg.header) { - header = msg.header; - payload = msg.payload; - } - else if (msg.core) { - header = msg.core.header; - payload = msg.core.body; - } - - if (header){ - switch (header.name) { - // interoperability with 1.1 - case C.START_TRANSCODER_REPLY: - meetingId = payload[C.MEETING_ID]; - this.emit(C.START_TRANSCODER_REPLY+meetingId, payload); - break; - case C.STOP_TRANSCODER_REPLY: - meetingId = payload[C.MEETING_ID]; - this.emit(C.STOP_TRANSCODER_REPLY+meetingId, payload); - break; - case C.DICONNECT_ALL_USERS: - this.emit(C.DICONNECT_ALL_USERS, payload); - break; - // 2x messages - case C.START_TRANSCODER_RESP_2x: - meetingId = header[C.MEETING_ID_2x]; - payload[C.MEETING_ID_2x] = meetingId; - this.emit(C.START_TRANSCODER_RESP_2x+meetingId, payload); - break; - case C.STOP_TRANSCODER_RESP_2x: - meetingId = header[C.MEETING_ID_2x]; - payload[C.MEETING_ID_2x] = meetingId; - this.emit(C.STOP_TRANSCODER_RESP_2x+meetingId, payload); - break; - case C.USER_CAM_BROADCAST_STARTED_2x: - this.emit(C.USER_CAM_BROADCAST_STARTED_2x, payload[C.STREAM_URL]); - break; - case C.RECORDING_STATUS_REPLY_MESSAGE_2x: - meetingId = header[C.MEETING_ID_2x]; - this.emit(C.RECORDING_STATUS_REPLY_MESSAGE_2x+meetingId, payload); - break; - case C.DICONNECT_ALL_USERS_2x: - payload[C.MEETING_ID_2x] = header[C.MEETING_ID_2x]; - this.emit(C.DICONNECT_ALL_USERS_2x, payload); - break; - case C.PRESENTER_ASSIGNED_2x: - meetingId = header[C.MEETING_ID_2x]; - payload[C.MEETING_ID_2x] = meetingId; - this.emit(C.PRESENTER_ASSIGNED_2x+meetingId, payload); - break; - default: - this.emit(C.GATEWAY_MESSAGE, msg); - } - } - else { - this.emit(C.GATEWAY_MESSAGE, msg); - } - } - - publish (message, channel) { - if (!this.publisher) { - this.publisher = new RedisWrapper(); - this.publisher.startPublisher(); - } - - if (typeof this.publisher.publishToChannel === 'function') { - this.publisher.publishToChannel(message, channel); - } - } - - writeMeetingKey(meetingId, message, callback) { - const EXPIRE_TIME = config.get('redisExpireTime'); - if (!this.publisher) { - this.publisher = new RedisWrapper(); - this.publisher.startPublisher(); - } - - let recKey = 'recording:' + meetingId; - - this.publisher.setKeyWithIncrement(recKey, message, (err, msgId) => { - - this.publisher.pushToList('meeting:' + meetingId + ':recordings', msgId); - - // Not implemented yet - this.publisher.expireKey(recKey + ':' + msgId, EXPIRE_TIME, (err) => { - Logger.info('Recording key will expire in', EXPIRE_TIME, 'seconds', err); - }); - }); - } - - async isChannelAvailable (channel) { - const channels = await this.publisher.getChannels(); - return channels.includes(channel); - } - - getChannels () { - return this.publisher.getChannels(); - } - - setEventEmitter (emitter) { - this.emitter = emitter; - } -} diff --git a/labs/bbb-webrtc-sfu/lib/connection-manager/ConnectionManager.js b/labs/bbb-webrtc-sfu/lib/connection-manager/ConnectionManager.js deleted file mode 100644 index efd15d8746..0000000000 --- a/labs/bbb-webrtc-sfu/lib/connection-manager/ConnectionManager.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Lucas Fialho Zawacki - * Paulo Renato Lanzarin - * (C) Copyright 2017 Bigbluebutton - * - */ - -'use strict'; - -const http = require('http'); -const EventEmitter = require('events'); -const BigBlueButtonGW = require('../bbb/pubsub/bbb-gw'); -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); - -// Global variables -module.exports = class ConnectionManager { - - constructor (settings, logger) { - this._screenshareSessions = {}; - - this._setupBBB(); - - this._emitter = this._setupEventEmitter(); - this._adapters = []; - } - - setHttpServer(httpServer) { - this.httpServer = httpServer; - } - - listen(callback) { - this.httpServer.listen(callback); - } - - addAdapter(adapter) { - adapter.setEventEmitter(this._emitter); - this._adapters.push(adapter); - } - - _setupEventEmitter() { - let self = this; - let emitter = new EventEmitter(); - - emitter.on(C.WEBSOCKET_MESSAGE, (data) => { - switch (data.type) { - case "screenshare": - self._bbbGW.publish(JSON.stringify(data), C.TO_SCREENSHARE); - break; - - case "video": - self._bbbGW.publish(JSON.stringify(data), C.TO_VIDEO); - break; - - case "audio": - self._bbbGW.publish(JSON.stringify(data), C.TO_AUDIO); - break; - - case "default": - // TODO handle API error message; - } - }); - - return emitter; - } - - async _setupBBB() { - this._bbbGW = new BigBlueButtonGW(); - - try { - const screenshare = await this._bbbGW.addSubscribeChannel(C.FROM_SCREENSHARE); - const video = await this._bbbGW.addSubscribeChannel(C.FROM_VIDEO); - const audio = await this._bbbGW.addSubscribeChannel(C.FROM_AUDIO); - - const emitFunk = (data) => { - this._emitter.emit('response', data); - }; - - screenshare.on(C.REDIS_MESSAGE, emitFunk); - video.on(C.REDIS_MESSAGE, emitFunk); - - audio.on(C.REDIS_MESSAGE, (data) => { - this._emitter.emit('response', data); - }); - - Logger.info('[ConnectionManager] Successfully subscribed to processes redis channels'); - } - catch (err) { - Logger.info('[ConnectionManager] ' + err); - this._stopAll; - } - } - - _stopSession(sessionId) { - } - - _stopAll() { - } -} diff --git a/labs/bbb-webrtc-sfu/lib/connection-manager/HttpServer.js b/labs/bbb-webrtc-sfu/lib/connection-manager/HttpServer.js deleted file mode 100644 index 8b49a75f47..0000000000 --- a/labs/bbb-webrtc-sfu/lib/connection-manager/HttpServer.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; - -const http = require("http"); -const fs = require("fs"); -const config = require('config'); -const Logger = require('../utils/Logger'); - -module.exports = class HttpServer { - - constructor() { - //const privateKey = fs.readFileSync('sslcert/server.key', 'utf8'); - //const certificate = fs.readFileSync('sslcert/server.crt', 'utf8'); - //const credentials = {key: privateKey, cert: certificate}; - - this.port = config.get('clientPort'); - - this.server = http.createServer((req,res) => { - // - }); - } - - getServerObject() { - return this.server; - } - - listen(callback) { - Logger.info('[HttpServer] Listening in port ' + this.port); - this.server.listen(this.port, callback); - } - -} diff --git a/labs/bbb-webrtc-sfu/lib/connection-manager/MessageValidator.js b/labs/bbb-webrtc-sfu/lib/connection-manager/MessageValidator.js deleted file mode 100644 index 84022e2688..0000000000 --- a/labs/bbb-webrtc-sfu/lib/connection-manager/MessageValidator.js +++ /dev/null @@ -1,81 +0,0 @@ -const Joi = require('joi'); - -let instance = null; - -module.exports = class MessageParser { - constructor() { - if(!instance){ - instance = this; - } - return instance; - } - - static const schema { - startScreenshare: Joi.object().keys({ - sdpOffer : Joi.string().required(), - vh: Joi.number().required(), - vw: Joi.number().required() - }), - - startVideo: Joi.object().keys({ - internalMeetingId: joi.string().required(), - callerName : Joi.string().required(), - }), - - startAudio: Joi.object().keys({ - internalMeetingId: joi.string().required(), - callerName : Joi.string().required(), - }), - - playStart: Joi.object().keys({ - }), - - playStop: Joi.object().keys.({ - }), - - stop: Joi.object().keys({ - }), - - onIceCandidate: Joi.object().keys({ - internalMeetingId: joi.string().required(), - candidate: Joi.object().required(), - }), - } - - static const messageTemplate Joi.object().keys({ - id: Joi.string().required(), - type: joi.string().required(), - role: joi.string().required(), - }) - - static const validateMessage (msg) { - let res = Joi.validate(msg, messageTemplate, {allowUnknown: true}); - - if (!res.error) { - res = Joi.validate(msg, schema[msg.id]); - } - - return res; - } - - _parse (message) { - let parsed = { id: '' }; - - try { - parsed = JSON.parse(message); - } catch (e) { - console.error(e); - } - - let res = validateMessage(parsed); - - if (res.error) { - parsed.validMessage = false; - parsed.errors = res.error; - } else { - parsed.validMessage = true; - } - - return parsed; - } -} diff --git a/labs/bbb-webrtc-sfu/lib/connection-manager/WebsocketConnectionManager.js b/labs/bbb-webrtc-sfu/lib/connection-manager/WebsocketConnectionManager.js deleted file mode 100644 index b0fbcc3ae7..0000000000 --- a/labs/bbb-webrtc-sfu/lib/connection-manager/WebsocketConnectionManager.js +++ /dev/null @@ -1,142 +0,0 @@ -'use strict'; - -const ws = require('ws'); -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); - -// ID counter -let connectionIDCounter = 0; - -module.exports = class WebsocketConnectionManager { - constructor (server, path) { - this.wss = new ws.Server({ - server, - path - }); - - this.webSockets = {}; - - this.wss.on('connection', this._onNewConnection.bind(this)); - } - - setEventEmitter (emitter) { - this.emitter = emitter; - this.emitter.on('response', this._onServerResponse.bind(this)); - } - - _onServerResponse (data) { - // Here this is the 'ws' instance - const connectionId = data? data.connectionId : null; - const ws = this.webSockets[connectionId]; - if (ws) { - if (data.id === 'close') { - try { - ws.close(); - } catch (err) { - Logger.warn('[WebsocketConnectionManager] Error on closing WS for', connectionId, err) - } - } else { - this.sendMessage(ws, data); - } - } - } - - _onNewConnection (ws) { - ws.id = connectionIDCounter++; - this.webSockets[ws.id] = ws; - Logger.info("[WebsocketConnectionManager] New connection with id [ " + ws.id + " ]"); - - ws.on('message', (data) => { - this._onMessage(ws, data); - }); - - ws.on('close', (err) => { - this._onClose(ws, err); - }); - - ws.on('error', (err) => { - this._onError(ws, err); - }); - }; - - _onMessage (ws, data) { - let message = {}; - - try { - message = JSON.parse(data); - - if (message.id === 'ping') { - this.sendMessage(ws, {id: 'pong'}); - return; - } - - message.connectionId = ws.id; - - if (!ws.sessionId) { - ws.sessionId = message.voiceBridge; - } - - if (!ws.route) { - ws.route = message.type; - } - - if (!ws.role) { - ws.role = message.role; - } - } catch(e) { - Logger.error(" [WebsocketConnectionManager] JSON message parse error " + e); - message = {}; - } - - // Test for empty or invalid JSON - if (Object.getOwnPropertyNames(message).length !== 0) { - this.emitter.emit(C.WEBSOCKET_MESSAGE, message); - } - } - - _onError (ws, err) { - Logger.debug('[WebsocketConnectionManager] Connection error [' + ws.id + ']', err); - let message = { - id: 'error', - type: ws.route, - role: ws.role, - voiceBridge: ws.sessionId, - connectionId: ws.id - } - - this.emitter.emit(C.WEBSOCKET_MESSAGE, message); - - delete this.webSockets[ws.id]; - } - - _onClose (ws, err) { - Logger.info('[WebsocketConnectionManager] Closed connection on [' + ws.id + ']'); - let message = { - id: 'close', - type: ws.route, - role: ws.role, - voiceBridge: ws.sessionId, - connectionId: ws.id - } - - this.emitter.emit(C.WEBSOCKET_MESSAGE, message); - - delete this.webSockets[ws.id]; - } - - sendMessage (ws, json) { - - if (ws._closeCode === 1000) { - Logger.error("[WebsocketConnectionManager] Websocket closed, not sending"); - this._onError(ws, "[WebsocketConnectionManager] Error: not opened"); - } - - return ws.send(JSON.stringify(json), (error) => { - if(error) { - Logger.error('[WebsocketConnectionManager] Websocket error "' + error + '" on message "' + json.id + '"'); - - this._onError(ws, error); - } - }); - } -} diff --git a/labs/bbb-webrtc-sfu/lib/h264-sdp.js b/labs/bbb-webrtc-sfu/lib/h264-sdp.js deleted file mode 100644 index e65c058126..0000000000 --- a/labs/bbb-webrtc-sfu/lib/h264-sdp.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * A module with the sole purpose of removing all non h264 options from an sdpOffer - * - * We use this to prevent any transcoding from the Kurento side if Firefox or Chrome offer VP8/VP9 as - * the default format. - */ - -const sdpTransform = require('sdp-transform'); - -exports.transform = function(sdp, preferredProfile = null) { - - let mediaIndex = 0; - let res = sdpTransform.parse(sdp); - let validPayloads; - - if (res.media[0].type === 'audio') { - // Audio - res.media[mediaIndex].rtp = res.media[mediaIndex].rtp.filter(function(elem) { - return elem.codec === 'opus'; - }); - - validPayloads = res.media[mediaIndex].rtp.map(function(elem) { - return elem.payload; - }); - - res.media[mediaIndex].fmtp = res.media[mediaIndex].fmtp.filter(function(elem) { - return validPayloads.indexOf(elem.payload) >= 0; - }); - - res.media[mediaIndex].payloads = validPayloads.join(' '); - - mediaIndex += 1; - } - - // Video - const availablePayloads = res.media[mediaIndex].rtp.map(elem => { - return elem.payload; - }); - - res.media[mediaIndex].rtp = res.media[mediaIndex].rtp.filter(function(elem) { - return elem.codec === 'H264'; - }); - - preferProfile(res.media[mediaIndex], preferredProfile); - - validPayloads = res.media[mediaIndex].rtp.map(function(elem) { - return elem.payload; - }); - - res.media[mediaIndex].fmtp = res.media[mediaIndex].fmtp.filter(function(elem) { - return validPayloads.indexOf(elem.payload) >= 0; - }); - - res.media[mediaIndex].rtcpFb = res.media[mediaIndex].rtcpFb.filter(function(elem) { - return validPayloads.indexOf(elem.payload) >= 0; - }); - - - res.media[mediaIndex].payloads = validPayloads.join(' '); - - return sdpTransform.write(res); -}; - -const preferProfile = function (mediaLine, profileToFilter) { - if (profileToFilter == null) { - return; - } - - const profileRegex = /(?:profile\-level\-id\=)([\d\w]*)/i; - const validProfilePayloads = mediaLine.fmtp.filter(e => { - let profileSpec = profileRegex.exec(e.config); - return profileSpec && profileSpec[1] && profileSpec[1] === profileToFilter; - }).map(e => e.payload); - - if (validProfilePayloads.length > 0) { - mediaLine.rtp = mediaLine.rtp.filter(e => { - return validProfilePayloads.includes(e.payload); - }); - } -}; diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/CoreProcess.js b/labs/bbb-webrtc-sfu/lib/mcs-core/CoreProcess.js deleted file mode 100644 index 9717a8807f..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/CoreProcess.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * WIP: the mcs-core lib will be turned into a dependecy sometime in the near - * future, and it will probably act with a separate process that answers via - * its own redis channel - */ -const Logger = require('../../utils/Logger'); -const MCSApiStub = require('./media/MCSApiStub'); - -process.on('uncaughtException', function (error) { - Logger.error("[mcs-core-process] Uncaught exception with error", error.stack); -}); - -process.on('disconnect',function() { - Logger.info("[mcs-core-process] Parent process exited!"); - process.kill(); -}); - -core = new MCSApiStub(); diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/freeswitch/AudioHandler.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/freeswitch/AudioHandler.js deleted file mode 100644 index e5028f2a4b..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/freeswitch/AudioHandler.js +++ /dev/null @@ -1,99 +0,0 @@ -'use strict'; -const Logger = require('../../../../utils/Logger'); -const config = require('config'); -const KURENTO_IP = config.get('kurentoIp'); - -var kmh = function(sdp) { - this.endpointSdp = sdp; -}; -/** - * @classdesc - * Custom sipjs's MediaHandler to manage media communication between - * Kurento and Freeswitch - * @constructor - */ -kmh.prototype.AudioHandler = function (session, options ) { - this.session = session; - this.options = options; - this.Kurento; - this.sdp = null; - this.endpointSdp = null; - this.remote_sdp = null; - this.version = '0.0.1'; - - //Default video configuration - this.video = { - configuration: { - codecId: '96', - sendReceive: 'sendrecv', - rtpProfile: 'RTP/AVP', - codecName: 'H264' , - codecRate: '90000', - frameRate: '30.000000' - } - }; -}; - -/** - * Factory method for AudioHandler - * @param {Object} session Current session of this media handler - * @param {Object} options Options - * @return {AudioHandler} A AudioHandler - */ -kmh.prototype.AudioHandler.defaultFactory = function audioDefaultFactory(session, options) { - return new kmh.prototype.AudioHandler(session, options); -}; - -/** - * Setup method for this media handler. This method MUST be called before - * the SIP session starts. - * @param {Object} configuration Configuration parameters for the session - */ -kmh.prototype.AudioHandler.setup = function (sdp, rtp, kurento) { - kmh.prototype.AudioHandler.prototype.sendSdp = sdp; - kmh.prototype.AudioHandler.prototype.rtp = rtp; - kmh.prototype.AudioHandler.prototype.Kurento = kurento; - - Logger.info('[mcs-audio-handler] Setting SDP'); -}; - -kmh.prototype.AudioHandler.prototype = { - - isReady: function () { return true; }, - - close: function () { - if (this.timeout) { - clearTimeout(this.timeout); - delete this.timeout; - } - delete this.session; - }, - - render: function(){}, - mute: function(){}, - unmute: function(){}, - - getDescription: async function (onSuccess, onFailure, mediaHint) { - if(this.endpointSdp === null) { - Logger.info("[mcs-audio-handler] Processing SDP for Kurento RTP endpoint", this.rtp); - this.endpointSdp = await this.Kurento.processOffer(this.rtp, this.remote_sdp); - this.endpointSdp = this.endpointSdp.replace(/(IP4\s[0-9.]*)/g, 'IP4 ' + KURENTO_IP); - } - this.sdp = this.endpointSdp; - this.timeout = setTimeout(function () { - delete this.timeout; - onSuccess(this.sdp); - }.bind(this), 0); - }, - - setDescription: function (description, onSuccess, onFailure) { - Logger.debug(" [AudioHandler] Remote SDP: ", description); - this.remote_sdp = description; - this.timeout = setTimeout(function () { - delete this.timeout; - onSuccess(); - }.bind(this), 0); - } -}; - -module.exports = new kmh(); diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/freeswitch/freeswitch.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/freeswitch/freeswitch.js deleted file mode 100644 index 10f8e5c254..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/freeswitch/freeswitch.js +++ /dev/null @@ -1,289 +0,0 @@ -'use strict' - -const C = require('../../constants/Constants.js'); -const config = require('config'); -const EventEmitter = require('events').EventEmitter; -const audioHandler = require('./AudioHandler.js'); -const Logger = require('../../../../utils/Logger'); -const SIPJS = require('sip.js'); -const LOCAL_IP_ADDRESS = config.get('localIpAddress'); -const FREESWITCH_IP = config.get('freeswitch').ip; -const FREESWITCH_PORT = config.get('freeswitch').port; -const Kurento = require('../kurento/kurento'); -const isError = require('../../utils/util').isError; - -let instance = null; - -/* Public members */ -module.exports = class Freeswitch extends EventEmitter { - constructor(serverUri) { - if(!instance){ - super(); - this._serverUri = serverUri; - this._userAgents = {}; - this._sessions = {}; - this._rtpConverters = {}; - this._Kurento = new Kurento(config.get('kurentoUrl')); - this._Kurento.on(C.ERROR.MEDIA_SERVER_OFFLINE, () => { - this.emit(C.ERROR.MEDIA_SERVER_OFFLINE); - }); - - instance = this; - } - - return instance; - } - - async init () { - Logger.debug("[mcs-media] freeswitch init stub"); - await this._Kurento.init(); - } - - - _getMediaServerClient (serverUri) { - return new Promise((resolve, reject) => { - }); - } - - async createMediaElement (roomId, type, params) { - if (this._userAgents[roomId]) { - return Promise.resolve(roomId); - } - try { - let userAgent = await this._createUserAgent(type, params.name, roomId); - this._userAgents[roomId] = userAgent; - return Promise.resolve(roomId); - } - catch (err) { - return Promise.reject(err); - } - } - - async connect (sourceId, sinkId, type) { - let source = this._sessions[sourceId]; - Logger.debug("[mcs-media-freeswitch] Connecting", this._rtpConverters[sourceId], "to", sinkId); - - if (source) { - return new Promise((resolve, reject) => { - switch (type) { - case 'ALL': - - case 'AUDIO': - - case 'VIDEO': - this._Kurento.connect(this._rtpConverters[sourceId], sinkId, type); - return resolve(); - break; - - default: return reject("[mcs-media] Invalid connect type"); - } - }); - } - else { - return Promise.reject("[mcs-media] Failed to connect " + type + ": " + sourceId + " to " + sinkId); - } - } - - stop (roomId, type = {}, elementId) { - return new Promise(async (resolve, reject) => { - try { - Logger.info("[mcs-media-freeswitch] Releasing endpoint", elementId, "from room", roomId); - - await this._stopUserAgent(elementId); - await this._stopRtpConverter(roomId, elementId); - return resolve(); - } - catch (error) { - error = this._handleError(error); - return reject(error); - } - }); - } - - async _stopUserAgent (elementId) { - return new Promise(async (resolve, reject) => { - Logger.debug("[mcs-media-freeswitch] Releasing userAgent", elementId); - let userAgent = this._userAgents[elementId]; - - if (userAgent) { - Logger.debug("[mcs-media-freeswitch] Stopping user agent", elementId); - await userAgent.stop(); - delete this._userAgents[elementId]; - return resolve(); - } - else { - return resolve(); - } - }); - } - - async _stopRtpConverter (roomId, elementId) { - return new Promise(async (resolve, reject) => { - let rtpConverter = this._rtpConverters[elementId]; - if (rtpConverter) { - Logger.debug("[mcs-media-freeswitch] Stopping converter", rtpConverter); - await this._Kurento.stop(roomId, C.MEDIA_TYPE.RTP, this._rtpConverters[elementId]); - delete this._rtpConverters[elementId]; - return resolve(); - } - else { - return resolve(); - } - }); - } - - async processOffer (elementId, sdpOffer, params) { - let userAgent = this._userAgents[elementId]; - let rtpEndpoint; - - return new Promise(async (resolve, reject) => { - try { - if (userAgent) { - - if (this._rtpConverters[elementId]) { - rtpEndpoint = this._rtpConverters[elementId]; - } - else { - rtpEndpoint = await this._Kurento.createMediaElement(elementId, 'RtpEndpoint'); - this._rtpConverters[elementId] = rtpEndpoint; - } - - Logger.info("[mcs-media-freeswitch] RTP endpoint equivalent to SIP instance is", rtpEndpoint); - - let session = this.sipCall(userAgent, - params.name, - elementId, - FREESWITCH_IP, - FREESWITCH_PORT, - rtpEndpoint - //sdpOffer - ); - - session.on('accepted', (response, cause) => { - this._sessions[elementId] = session; - return resolve(session.remote_sdp); - }); - - session.on('rejected', (response, cause) => { - Logger.info("session rejected", response, cause); - }); - - session.on('failed', (response, cause) => { - Logger.info("session failed", response, cause); - }); - - session.on('progress', (response) => { - Logger.info("session progress", response); - }); - - } else { - return reject("[mcs-media] There is no element " + elementId); - } - } - catch (error) { - this._handleError(error); - reject(error); - } - }); - } - - trackMediaState (elementId, type) { - let userAgent = this._userAgents[elementId]; - if (userAgent) { - userAgent.on('invite', function(session) { - Logger.info("[mcs-media-freeswitch] On UserAgentInvite"); - }); - - userAgent.on('message', function(message) { - Logger.info("[mcs-media-freeswitch] On UserAgentMessage", message); - }); - - userAgent.on('connected', function() { - Logger.info("[mcs-media-freeswitch] On UserAgentConnected"); - }); - - userAgent.on('disconnected', function (){ - Logger.warn("[mcs-media-freeswitch] UserAgent disconnected"); - }); - - return; - } - } - - _destroyElements() { - for (var ua in this._userAgents) { - if (this._userAgents.hasOwnProperty(ua)) { - delete this._mediaElements[ua]; - } - } - } - - _createUserAgent (type, displayName, roomId) { - var mediaFactory = audioHandler.AudioHandler.defaultFactory; - var newUA = new SIPJS.UA({ - uri: 'sip:' + C.FREESWITCH.GLOBAL_AUDIO_PREFIX + roomId + '@' + LOCAL_IP_ADDRESS, - wsServers: 'ws://' + FREESWITCH_IP + ':' + FREESWITCH_PORT, - displayName: displayName, - register: false, - mediaHandlerFactory: mediaFactory, - userAgentString: C.STRING.SIP_USER_AGENT, - log: { - builtinEnabled: false, - level: 3, - connector: this.sipjsLogConnector - }, - traceSip: true, - hackIpInContact: LOCAL_IP_ADDRESS - }); - - Logger.info("[mcs-freeswitch-adapter] Created new user agent for endpoint " + displayName); - - return newUA; - } - -/** - * Makes a sip call to a Freeswitch instance - * @param {UA} caller's SIP.js User Agent - * @param {String} username The user identifier (Kurento Endpoint ID) - * @param {String} voiceBridge The voiceBridge we are going to call to - * @param {String} host Freeswitch host address - * @param {String} port Freeswitch port - */ - sipCall (userAgent, username, voiceBridge, host, port, rtp) { - //call options - var options = { - media: { - constraints: { - audio: true, - video: false - }, - }, - inviteWithoutSdp: true, - params: { - from_displayName : username - } - }; - - audioHandler.AudioHandler.setup(null, rtp, this._Kurento); - - var sipUri = new SIPJS.URI('sip', voiceBridge, host, port); - - Logger.info('[mcs-media-freeswitch] Making SIP call to: ' + sipUri + ' from: ' + username); - - return userAgent.invite(sipUri, options); - } - - _handleError(error) { - // Checking if the error needs to be wrapped into a JS Error instance - if (!isError(error)) { - error = new Error(error); - } - - error.code = C.ERROR.MEDIA_SERVER_ERROR; - Logger.error('[mcs-media] Media Server returned error', error); - } - - sipjsLogConnector (level, category, label, content) { - Logger.debug('[SIP.js] ' + content); - } -}; diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/kurento/errors.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/kurento/errors.js deleted file mode 100644 index b96dea1954..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/kurento/errors.js +++ /dev/null @@ -1,62 +0,0 @@ -// Adapted translation of https""://github.com/Kurento/kms-core/blob/master/src/server/interface/KurentoException.hpp - -const C = require('../../constants/Constants'); - -module.exports = { - /* GENERIC MEDIA ERRORS */ - 40001: { type: "MARSHALL_ERROR", error: C.ERROR.MEDIA_SERVER_GENERIC_ERROR }, - 40003: { type: "UNEXPECTED_ERROR", error: C.ERROR.MEDIA_SERVER_GENERIC_ERROR }, - 40004: { type: "CONNECT_ERROR", error: C.ERROR.MEDIA_CONNECT_ERROR }, - 40005: { type: "UNSUPPORTED_MEDIA_TYPE", error: C.ERROR.MEDIA_INVALID_TYPE }, - 40006: { type: "NOT_IMPLEMENTED" , error: C.ERROR.MEDIA_INVALID_OPERATION }, - 40007: { type: "INVALID_SESSION", error: C.ERROR.CONNECTION_ERROR }, - 40008: { type: "MALFORMED_TRANSACTION", error: C.ERROR.MEDIA_INVALID_OPERATION }, - 40009: { type: "NOT_ENOUGH_RESOURCES", error: C.ERROR.MEDIA_SERVER_NO_RESOURCES }, - - /* MediaObject ERRORS */ - 40100: { type: "MEDIA_OBJECT_TYPE_NOT_FOUND", error: C.ERROR.MEDIA_INVALID_TYPE }, - 40101: { type: "MEDIA_OBJECT_NOT_FOUND", error: C.ERROR.MEDIA_NOT_FOUND }, - 40104: { type: "MEDIA_OBJECT_CONSTRUCTOR_NOT_FOUND", error: C.ERROR.MEDIA_INVALID_OPERATION }, - 40105: { type: "MEDIA_OBJECT_METHOD_NOT_FOUND", error: C.ERROR.MEDIA_INVALID_OPERATION }, - 40106: { type: "MEDIA_OBJECT_EVENT_NOT_SUPPORTED", error: C.ERROR.MEDIA_INVALID_OPERATION }, - 40107: { type: "MEDIA_OBJECT_ILLEGAL_PARAM_ERROR", error: C.ERROR.MEDIA_INVALID_OPERATION }, - 40108: { type: "MEDIA_OBJECT_NOT_AVAILABLE", error: C.ERROR.MEDIA_NOT_FOUND }, - 40109: { type: "MEDIA_OBJECT_NOT_FOUND_TRANSACTION_NO_COMMIT", error: C.ERROR.MEDIA_INVALID_OPERATION }, - 40110: { type: "MEDIA_OBJECT_TAG_KEY_NOT_FOUND", error: C.ERROR.MEDIA_INVALID_OPERATION }, - 40111: { type: "MEDIA_OBJECT_OPERATION_NOT_SUPPORTED", error: C.ERROR.MEDIA_INVALID_OPERATION }, - - /* SDP ERRORS */ - 40200: { type: "SDP_CREATE_ERROR", error: C.ERROR.MEDIA_GENERIC_ERROR }, - 40201: { type: "SDP_PARSE_ERROR", error: C.ERROR.MEDIA_INVALID_SDP }, - 40202: { type: "SDP_END_POINT_NO_LOCAL_SDP_ERROR", error: C.ERROR.MEDIA_INVALID_SDP }, - 40203: { type: "SDP_END_POINT_NO_REMOTE_SDP_ERROR", error: C.ERROR.MEDIA_INVALID_SDP }, - 40204: { type: "SDP_END_POINT_GENERATE_OFFER_ERROR", error: C.ERROR.MEDIA_GENERATE_OFFER_FAILED }, - 40205: { type: "SDP_END_POINT_PROCESS_OFFER_ERROR" , error: C.ERROR.MEDIA_PROCESS_OFFER_FAILED }, - 40206: { type: "SDP_END_POINT_PROCESS_ANSWER_ERROR", error: C.ERROR.MEDIA_PROCESS_ANSWER_FAILED }, - 40207: { type: "SDP_CONFIGURATION_ERROR", error: C.ERROR.MEDIA_GENERIC_ERROR }, - 40208: { type: "SDP_END_POINT_ALREADY_NEGOTIATED", error: C.ERROR.MEDIA_PROCESS_OFFER_FAILED }, - 40209: { type: "SDP_END_POINT_NOT_OFFER_GENERATED", error: C.ERROR.MEDIA_GENERATE_OFFER_FAILED }, - 40210: { type: "SDP_END_POINT_ANSWER_ALREADY_PROCCESED", error: C.ERROR.MEDIA_PROCESS_ANSWER_FAILED }, - 40211: { type: "SDP_END_POINT_CANNOT_CREATE_SESSON", error: C.ERROR.MEDIA_GENERIC_ERROR }, - - /* HTTP ERRORS */ - 40300: { type: "HTTP_END_POINT_REGISTRATION_ERROR" }, - - /* ICE ERRORS */ - 40400: { type: "ICE_GATHER_CANDIDATES_ERROR", error: C.ERROR.ICE_GATHERING_FAILED }, - 40401: { type: "ICE_ADD_CANDIDATE_ERROR", error: C.ERROR.ICE_CANDIDATE_FAILED }, - - /* SERVER MANAGER ERRORS */ - 40500: { type: "SERVER_MANAGER_ERROR_KMD_NOT_FOUND", error: C.ERROR.MEDIA_SERVER_GENERIC_ERROR }, - - /* URI ERRORS */ - 40600: { type: "URI_ERROR_MIN" }, - 40699: { type: "URI_ERROR_MAX" }, - 40600: { type: "URI_PATH_FILE_NOT_FOUND", error: C.ERROR.MEDIA_INVALID_OPERATION }, - - /* PLAYER ERRORS */ - 40700: { type: "PLAYER_ERROR_MIN" }, - 40799: { type: "PLAYER_ERROR_MAX" }, - 40700: { type: "PLAYER_SEEK_FAIL", error: C.ERROR.MEDIA_GENERIC_ERROR }, -}; - diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/kurento/kurento.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/kurento/kurento.js deleted file mode 100644 index d4ca0bf970..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/adapters/kurento/kurento.js +++ /dev/null @@ -1,640 +0,0 @@ -'use strict' - -const C = require('../../constants/Constants.js'); -const config = require('config'); -const mediaServerClient = require('kurento-client'); -const EventEmitter = require('events').EventEmitter; -const Logger = require('../../../../utils/Logger'); -const isError = require('../../utils/util').isError; -const ERRORS = require('./errors.js'); -const KURENTO_WEBSOCKET_POOL_SIZE = config.get('kurento-websocket-pool-size'); - -let instance = null; - -module.exports = class MediaServer extends EventEmitter { - constructor(serverUri, globalEmitter) { - if (!instance){ - super(); - this._serverUri = serverUri; - this._globalEmitter = globalEmitter; - this._mediaPipelines = {}; - this._mediaElements = {}; - this._mediaServers; - this._status = C.STATUS.STOPPED; - this._reconnectionRoutine = null; - instance = this; - } - - return instance; - } - - init () { - return new Promise(async (resolve, reject) => { - try { - if (this._mediaServers == null || this._mediaServers.length === 0) { - this._mediaServers = []; - for (var i = 0; i < KURENTO_WEBSOCKET_POOL_SIZE; i++) { - let client = await this._getMediaServerClient(this._serverUri); - this._mediaServers.push(client); - } - this._globalEmitter.on(C.EVENT.ROOM_EMPTY, this._releasePipeline.bind(this)); - Logger.info("[mcs-media] Retrieved", this._mediaServers.length, "media server clients"); - this._status = C.STATUS.STARTING; - this._monitorConnectionState(); - return resolve(); - } - resolve(); - } - catch (error) { - this.emit(C.ERROR.MEDIA_SERVER_OFFLINE); - reject(this._handleError(error)); - } - }); - } - - _getMediaServerClient (serverUri) { - return new Promise((resolve, reject) => { - mediaServerClient(serverUri, {failAfter: 1}, (error, client) => { - if (error) { - return reject(this._handleError(error)); - } - resolve(client); - }); - }); - } - - _monitorConnectionState () { - try { - if (this._mediaServers) { - Logger.debug('[mcs-media] Monitoring connection state'); - this._mediaServers.forEach(ms => { - ms.on('disconnect', this._onDisconnection.bind(this)); - ms.on('reconnected',this._onReconnection.bind(this)); - }); - } - } - catch (err) { - this._handleError(err); - } - } - - _onDisconnection () { - if (this._status !== C.STATUS.STOPPED) { - Logger.error('[mcs-media] Media server was disconnected for some reason, will have to clean up all elements and notify users'); - this._status = C.STATUS.STOPPED; - this._destroyElements(); - this._destroyMediaServer(); - this.emit(C.ERROR.MEDIA_SERVER_OFFLINE); - this._reconnectToServer(); - } - } - - _onReconnection (sameSession) { - if (!sameSession) { - if (this._status !== C.STATUS.STOPPED) { - this._status = C.STATUS.RESTARTING; - this._destroyElements(); - this._destroyMediaServer(); - this.emit(C.ERROR.MEDIA_SERVER_OFFLINE); - } - - this._status = C.STATUS.STARTED; - Logger.info('[mcs-media] Media server is back online'); - this.emit(C.EVENT.MEDIA_SERVER_ONLINE); - } - } - - _reconnectToServer () { - if (this._reconnectionRoutine == null && this._status !== C.STATUS.RESTARTING) { - this._status = C.STATUS.RESTARTING; - this._reconnectionRoutine = setInterval(async () => { - try { - for (var i = 0; i < KURENTO_WEBSOCKET_POOL_SIZE; i++) { - let client = await this._getMediaServerClient(this._serverUri); - this._mediaServers.push(client); - } - Logger.info("[mcs-media] Retrieved", this._mediaServers.length, "media server clients"); - this._monitorConnectionState(); - clearInterval(this._reconnectionRoutine); - this._reconnectionRoutine = null; - Logger.warn("[mcs-media] Reconnection to media server succeeded"); - } - catch (error) { - this._mediaServers = []; - } - }, 2000); - } - } - - _getClientFromPool () { - // Round robin the pool - let client = this._mediaServers.shift(); - this._mediaServers.push(client); - return client; - } - - _getMediaPipeline (roomId) { - return new Promise((resolve, reject) => { - try { - let mediaServer = this._getClientFromPool(); - if (this._mediaPipelines[roomId]) { - mediaServer.getMediaobjectById(this._mediaPipelines[roomId].id, (error, pipeline) => { - if (error) { - return reject(this._handleError(error)); - } - Logger.warn('[mcs-media] Pipeline for', roomId, 'already exists.', pipeline.id); - return resolve(pipeline); - }); - } else { - mediaServer.create('MediaPipeline', (error, pipeline) => { - if (error) { - return reject(this._handleError(error)); - } - this._mediaPipelines[roomId] = pipeline; - pipeline.activeElements = {}; - resolve(pipeline); - }); - } - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - _releasePipeline (room) { - return new Promise(async (resolve, reject) => { - try { - Logger.debug("[mcs-media] Releasing room", room, "pipeline"); - const pipeline = this._mediaPipelines[room]; - if (pipeline && typeof pipeline.release === 'function') { - pipeline.release((error) => { - if (error) { - return reject(this._handleError(error)); - } - Logger.debug("[mcs-media] Pipeline", pipeline.id, "released"); - delete this._mediaPipelines[room]; - return resolve() - }); - } - } - catch (error) { - return reject(this._handleError(error)); - } - }); - } - - _createElement (pipeline, type, options) { - return new Promise((resolve, reject) => { - try { - pipeline.create(type, options, (error, mediaElement) => { - if (error) { - return reject(this._handleError(error)); - } - Logger.info("[mcs-media] Created [" + type + "] media element: " + mediaElement.id); - this._mediaElements[mediaElement.id] = mediaElement; - return resolve(mediaElement); - }); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - createMediaElement (roomId, type, options = {}) { - options = options || {}; - return new Promise(async (resolve, reject) => { - try { - const pipeline = await this._getMediaPipeline(roomId); - const mediaElement = await this._createElement(pipeline, type, options); - if (typeof mediaElement.setKeyframeInterval === 'function' && options.keyframeInterval) { - Logger.debug("[mcs-media] Creating element with keyframe interval set to", options.keyframeInterval); - mediaElement.setKeyframeInterval(options.keyframeInterval); - } - this._mediaPipelines[roomId].activeElements[mediaElement.id] = mediaElement.id; - resolve(mediaElement.id); - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - _getMediaElement (elementId) { - return new Promise((resolve, reject) => { - try { - let mediaServer = this._getClientFromPool(); - if (this._mediaElements[elementId]) { - mediaServer.getMediaobjectById(elementId, (error, element) => { - if (error || element == null) { - return reject(this._handleError(error)); - } - Logger.trace('[mcs-media] Element', elementId, 'found'); - return resolve(element); - }); - } else { - return reject(this._handleError(ERRORS[40101])); - } - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - async startRecording (sourceId) { - return new Promise(async (resolve, reject) => { - try { - const source = await this._getMediaElement(sourceId); - source.record((err) => { - if (err) { - return reject(this._handleError(err)); - } - return resolve(); - }); - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - async _stopRecording (sourceId) { - return new Promise(async (resolve, reject) => { - try { - const source = await this._getMediaElement(sourceId); - source.stopAndWait((err) => { - if (err) { - return reject(this._handleError(err)); - } - return resolve(); - }); - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - async connect (sourceId, sinkId, type) { - return new Promise(async (resolve, reject) => { - try { - const source = await this._getMediaElement(sourceId); - const sink = await this._getMediaElement(sinkId); - - switch (type) { - case 'ALL': - source.connect(sink, (error) => { - if (error) { - return reject(this._handleError(error)); - } - return resolve(); - }); - break; - - - case 'AUDIO': - source.connect(sink, 'AUDIO', (error) => { - if (error) { - return reject(this._handleError(error)); - } - return resolve(); - }); - - case 'VIDEO': - source.connect(sink, (error) => { - if (error) { - return reject(this._handleError(error)); - } - return resolve(); - }); - break; - - default: - return reject(this._handleError(ERRORS[40107])); - } - } - catch (error) { - return reject(this._handleError(error)); - } - }); - } - - async disconnect (sourceId, sinkId, type) { - return new Promise(async (resolve, reject) => { - try { - const source = await this._getMediaElement(sourceId); - const sink = await this._getMediaElement(sinkId); - - switch (type) { - case 'ALL': - source.disconnect(sink, (error) => { - if (error) { - return reject(this._handleError(error)); - } - return resolve(); - }); - break; - - case 'AUDIO': - source.disconnect(sink, 'AUDIO', (error) => { - if (error) { - return reject(this._handleError(error)); - } - return resolve(); - }); - - case 'VIDEO': - source.disconnect(sink, (error) => { - if (error) { - return reject(this._handleError(error)); - } - return resolve(); - }); - break; - - default: - return reject(this._handleError(ERRORS[40107])); - } - } - catch (error) { - return reject(this._handleError(error)); - } - }); - } - - stop (room, type, elementId) { - const pipeline = this._mediaPipelines[room]; - const cleanPipeline = async (p) => { - if (p) { - if (p.activeElements[elementId]) { - delete p.activeElements[elementId]; - } - - const activeElements = Object.keys(p.activeElements).length; - - Logger.info("[mcs-media] Pipeline has a total of", activeElements, "active elements"); - if (activeElements <= 0) { - await this._releasePipeline(room); - } - } - } - - return new Promise(async (resolve, reject) => { - try { - Logger.info("[mcs-media] Releasing endpoint", elementId, "from room", room); - const mediaElement = await this._getMediaElement(elementId); - - if (type === 'RecorderEndpoint') { - await this._stopRecording(elementId); - } - - if (mediaElement && typeof mediaElement.release === 'function') { - mediaElement.release(async (error) => { - if (error) { - return reject(this._handleError(error)); - } - delete this._mediaElements[elementId]; - - cleanPipeline(pipeline); - - return resolve(); - }); - } - else { - Logger.warn("[mcs-media] Media element", elementId, "could not be found to stop"); - - cleanPipeline(pipeline); - - return resolve(); - } - } - catch (err) { - cleanPipeline(pipeline); - this._handleError(err); - resolve(); - } - }); - } - - addIceCandidate (elementId, candidate) { - return new Promise(async (resolve, reject) => { - try { - const mediaElement = await this._getMediaElement(elementId); - - if (mediaElement && candidate) { - mediaElement.addIceCandidate(candidate, (error) => { - if (error) { - return reject(this._handleError(error)); - } - Logger.debug("[mcs-media] Added ICE candidate for => " + elementId); - return resolve(); - }); - } - else { - return reject(this._handleError(ERRORS[40101])); - } - } - catch (error) { - return reject(this._handleError(error)); - } - }); - } - - gatherCandidates (elementId) { - Logger.info('[mcs-media] Gathering ICE candidates for ' + elementId); - - return new Promise(async (resolve, reject) => { - try { - const mediaElement = await this._getMediaElement(elementId); - mediaElement.gatherCandidates((error) => { - if (error) { - return reject(this._handleError(error)); - } - Logger.info('[mcs-media] Triggered ICE gathering for ' + elementId); - return resolve(); - }); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - setInputBandwidth (elementId, min, max) { - let mediaElement = this._mediaElements[elementId]; - - if (mediaElement) { - mediaElement.setMinVideoRecvBandwidth(min); - mediaElement.setMaxVideoRecvBandwidth(max); - } else { - return ("[mcs-media] There is no element " + elementId); - } - } - - setOutputBandwidth (elementId, min, max) { - let mediaElement = this._mediaElements[elementId]; - - if (mediaElement) { - mediaElement.setMinVideoSendBandwidth(min); - mediaElement.setMaxVideoSendBandwidth(max); - } else { - return ("[mcs-media] There is no element " + elementId ); - } - } - - setOutputBitrate (elementId, min, max) { - let mediaElement = this._mediaElements[elementId]; - - if (mediaElement) { - mediaElement.setMinOutputBitrate(min); - mediaElement.setMaxOutputBitrate(max); - } else { - return ("[mcs-media] There is no element " + elementId); - } - } - - processOffer (elementId, sdpOffer) { - return new Promise(async (resolve, reject) => { - try { - const mediaElement = await this._getMediaElement(elementId); - - if (mediaElement) { - mediaElement.processOffer(sdpOffer, (error, answer) => { - if (error) { - return reject(this._handleError(error)); - } - return resolve(answer); - }); - } - else { - return reject(this._handleError(ERRORS[40101])); - } - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - trackMediaState (elementId, type) { - switch (type) { - case C.MEDIA_TYPE.URI: - this.addMediaEventListener(C.EVENT.MEDIA_STATE.ENDOFSTREAM, elementId); - // TODO event type validator - this.addMediaEventListener(C.EVENT.MEDIA_STATE.CHANGED, elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.FLOW_IN, elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.FLOW_OUT, elementId); - break; - - case C.MEDIA_TYPE.WEBRTC: - this.addMediaEventListener(C.EVENT.MEDIA_STATE.CHANGED, elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.FLOW_IN, elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.FLOW_OUT, elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.ICE, elementId); - break; - - // TODO event type validator - case C.MEDIA_TYPE.RTP: - this.addMediaEventListener(C.EVENT.MEDIA_STATE.CHANGED, elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.FLOW_IN, elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.FLOW_OUT, elementId); - break; - - case C.MEDIA_TYPE.RECORDING: - this.addMediaEventListener(C.EVENT.RECORDING.STOPPED, elementId); - this.addMediaEventListener(C.EVENT.RECORDING.PAUSED, elementId); - this.addMediaEventListener(C.EVENT.RECORDING.STARTED. elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.FLOW_IN, elementId); - this.addMediaEventListener(C.EVENT.MEDIA_STATE.FLOW_OUT, elementId); - break; - - default: return; - } - return; - } - - addMediaEventListener (eventTag, elementId) { - const mediaElement = this._mediaElements[elementId]; - try { - if (mediaElement) { - Logger.debug('[mcs-media] Adding media state listener [' + eventTag + '] for ' + elementId); - mediaElement.on(eventTag, (event) => { - if (eventTag === C.EVENT.MEDIA_STATE.ICE) { - event.candidate = mediaServerClient.getComplexType('IceCandidate')(event.candidate); - } - this.emit(C.EVENT.MEDIA_STATE.MEDIA_EVENT+elementId , {eventTag, event}); - }); - } - } - catch (err) { - err = this._handleError(err); - } - } - - notifyMediaState (elementId, eventTag, event) { - this.emit(C.MEDIA_STATE.MEDIA_EVENT , {elementId, eventTag, event}); - } - - _destroyElements () { - for (var pipeline in this._mediaPipelines) { - if (this._mediaPipelines.hasOwnProperty(pipeline)) { - delete this._mediaPipelines[pipeline]; - } - } - - for (var element in this._mediaElements) { - if (this._mediaElements.hasOwnProperty(element)) { - delete this._mediaElements[element]; - } - } - } - - _destroyMediaServer() { - delete this._mediaServer; - } - - _handleError(err) { - let { message: oldMessage , code, stack } = err; - let message; - - if (code && code >= C.ERROR.MIN_CODE && code <= C.ERROR.MAX_CODE) { - return err; - } - - const error = ERRORS[code]? ERRORS[code].error : null; - - if (error == null) { - switch (oldMessage) { - case "Request has timed out": - ({ code, message } = C.ERROR.MEDIA_SERVER_REQUEST_TIMEOUT); - break; - - case "Connection error": - ({ code, message } = C.ERROR.CONNECTION_ERROR); - break; - - default: - ({ code, message } = C.ERROR.MEDIA_SERVER_GENERIC_ERROR); - } - } - else { - ({ code, message } = error); - } - - // Checking if the error needs to be wrapped into a JS Error instance - if (!isError(err)) { - err = new Error(message); - } - - err.code = code; - err.message = message; - err.details = oldMessage; - err.stack = stack - - Logger.debug('[mcs-media] Media Server returned an', err.code, err.message); - Logger.trace(err.stack); - return err; - } -}; diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/constants/Constants.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/constants/Constants.js deleted file mode 100644 index 532fce7301..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/constants/Constants.js +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @classdesc - * Message constants for the communication with BigBlueButton - * @constructor - */ - -'use strict' - -exports.ALL = 'ALL' - -exports.STATUS = {} -exports.STATUS.STARTED = "STARTED" -exports.STATUS.STOPPED = "STOPPED" -exports.STATUS.RUNNING = "RUNNING'" -exports.STATUS.STARTING = "STARTING" -exports.STATUS.STOPPING = "STOPPING" -exports.STATUS.RESTARTING = "RESTARTING" - -exports.USERS = {} -exports.USERS.SFU = "SFU" -exports.USERS.MCU = "MCU" - -exports.MEDIA_TYPE = {} -exports.MEDIA_TYPE.WEBRTC = "WebRtcEndpoint" -exports.MEDIA_TYPE.RTP= "RtpEndpoint" -exports.MEDIA_TYPE.URI = "PlayerEndpoint" -exports.MEDIA_TYPE.RECORDING = "RecorderEndpoint"; - -// Media server state changes -exports.EVENT = {} -exports.EVENT.MEDIA_SERVER_ONLINE = "MediaServerOnline" -exports.EVENT.NEW_MEDIA_SESSION = "NewMediaSession" -exports.EVENT.MEDIA_SESSION_STOPPED = "MediaSessionStopped" -exports.EVENT.MEDIA_STATE = {}; -exports.EVENT.MEDIA_STATE.MEDIA_EVENT = "MediaEvent" -exports.EVENT.MEDIA_STATE.CHANGED = "MediaStateChanged" -exports.EVENT.MEDIA_STATE.FLOW_OUT = "MediaFlowOutStateChange" -exports.EVENT.MEDIA_STATE.FLOW_IN = "MediaFlowInStateChange" -exports.EVENT.MEDIA_STATE.ENDOFSTREAM = "EndOfStream" -exports.EVENT.MEDIA_STATE.ICE = "OnIceCandidate" -exports.EVENT.SERVER_STATE = "ServerState" -exports.EVENT.ROOM_EMPTY = "RoomEmpty" - -exports.EVENT.RECORDING = {}; -exports.EVENT.RECORDING.STOPPED = 'Stopped'; -exports.EVENT.RECORDING.STARTED = 'Started'; -exports.EVENT.RECORDING.PAUSED = 'Paused'; - -// Error codes -exports.ERROR = {}; -exports.ERROR.MIN_CODE = 2000; -exports.ERROR.MAX_CODE = 2999; -exports.ERROR.CONNECTION_ERROR = { code: 2000, message: "MEDIA_SERVER_CONNECTION_ERROR" }; -exports.ERROR.MEDIA_SERVER_OFFLINE = { code: 2001, message: "MEDIA_SERVER_OFFLINE" }; -exports.ERROR.MEDIA_SERVER_NO_RESOURCES = { code: 2002, message: "MEDIA_SERVER_NO_RESOURCES" }; -exports.ERROR.MEDIA_SERVER_REQUEST_TIMEOUT = { code: 2003, message: "MEDIA_SERVER_REQUEST_TIMEOUT" }; -exports.ERROR.MEDIA_SERVER_GENERIC_ERROR = { code: 2019, message: "MEDIA_SERVER_GENERIC_ERROR" }; -exports.ERROR.ICE_CANDIDATE_FAILED = { code: 2020, message: "ICE_ADD_CANDIDATE_FAILED" }; -exports.ERROR.ICE_GATHERING_FAILED = { code: 2021, message: "ICE_GATHERING_FAILED" }; -exports.ERROR.ICE_STATE_FAILED = { code: 2022, message: "ICE_STATE_FAILED" }; - -exports.ERROR.ROOM_GENERIC_ERROR = { code: 2100, message: "ROOM_GENNERIC_ERROR" }; -exports.ERROR.ROOM_NOT_FOUND = { code: 2101, message: "ROOM_NOT_FOUND" }; -exports.ERROR.USER_GENERIC_ERROR = { code: 2110, message: "USER_GENERIC_ERROR" }; -exports.ERROR.USER_NOT_FOUND = { code: 2111, message: "USER_NOT_FOUND" }; - -exports.ERROR.MEDIA_GENERIC_ERROR = { code: 2200, message: "MEDIA_GENERIC_ERROR" }; -exports.ERROR.MEDIA_NOT_FOUND = { code: 2201, message: "MEDIA_NOT_FOUND" }; -exports.ERROR.MEDIA_INVALID_SDP = { code: 2202, message: "MEDIA_INVALID_SDP" }; -exports.ERROR.MEDIA_NO_AVAILABLE_CODEC = { code: 2203, message: "MEDIA_NO_AVAILABLE_CODEC" }; -exports.ERROR.MEDIA_INVALID_TYPE = { code: 2204, message: "MEDIA_INVALID_TYPE" }; -exports.ERROR.MEDIA_INVALID_OPERATION = { code: 2205, message: "MEDIA_INVALID_OPERATION" }; -exports.ERROR.MEDIA_PROCESS_OFFER_FAILED = { code: 2206, message : "MEDIA_PROCESS_OFFER_FAILED" }; -exports.ERROR.MEDIA_PROCESS_ANSWER_FAILED = { code: 2207, message : "MEDIA_PROCESS_ANSWER_FAILED" }; -exports.ERROR.MEDIA_GENERIC_PROCESS_ERROR = { code: 2208, message: "MEDIA_GENERIC_PROCESS_ERROR" }; -exports.ERROR.MEDIA_ADAPTER_OBJECT_NOT_FOUND = { code: 2209, message: "MEDIA_ADAPTER_OBJECT_NOT_FOUND" }; -exports.ERROR.MEDIA_CONNECT_ERROR = { code: 2210, message: "MEDIA_CONNECT_ERROR" }; - - -// Freeswitch Adapter -exports.FREESWITCH = {}; -exports.FREESWITCH.GLOBAL_AUDIO_PREFIX = "GLOBAL_AUDIO_"; - -// RTP params -exports.SDP = {}; -exports.SDP.PARAMS = "params" -exports.SDP.MEDIA_DESCRIPTION = "media_description" -exports.SDP.LOCAL_IP_ADDRESS = "local_ip_address" -exports.SDP.LOCAL_VIDEO_PORT = "local_video_port" -exports.SDP.DESTINATION_IP_ADDRESS = "destination_ip_address" -exports.SDP.DESTINATION_VIDEO_PORT = "destination_video_port" -exports.SDP.REMOTE_VIDEO_PORT = "remote_video_port" -exports.SDP.CODEC_NAME = "codec_name" -exports.SDP.CODEC_ID = "codec_id" -exports.SDP.CODEC_RATE = "codec_rate" -exports.SDP.RTP_PROFILE = "rtp_profile" -exports.SDP.SEND_RECEIVE = "send_receive" -exports.SDP.FRAME_RATE = "frame_rate" - -// Strings -exports.STRING = {} -exports.STRING.KURENTO = "Kurento" -exports.STRING.FREESWITCH = "Freeswitch" -exports.STRING.USER_AGENT = "MediaController" -exports.STRING.DEFAULT_NAME = "default" -exports.STRING.SIP_USER_AGENT = "SIP.js 0.7.8" -exports.STRING.ANONYMOUS = "ANONYMOUS" -exports.STRING.FS_USER_AGENT_STRING = "Freeswitch_User_Agent" -exports.STRING.XML_MEDIA_FAST_UPDATE = '<?xml version=\"1.0\" encoding=\"utf-8\" ?>' + - '<media_control>' + - '<vc_primitive>' + - '<to_encoder>' + - '<picture_fast_update>' + - '</picture_fast_update>' + - '</to_encoder>' + - '</vc_primitive>' + - '</media_control>' diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/media/MCSApiStub.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/media/MCSApiStub.js deleted file mode 100644 index 1c3d5befea..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/media/MCSApiStub.js +++ /dev/null @@ -1,170 +0,0 @@ -'use strict' - -const config = require('config'); -const C = require('../constants/Constants'); -const util = require('util'); -const EventEmitter = require('events').EventEmitter; -const MediaController = require('./MediaController.js'); -const Logger = require('../../../utils/Logger'); - -let instance = null; - -module.exports = class MCSApiStub extends EventEmitter { - constructor() { - if(!instance) { - super(); - this.emitter = this; - this._mediaController = new MediaController(this.emitter); - instance = this; - } - - return instance; - } - - async join (room, type, params) { - try { - const answer = await this._mediaController.join(room, type, params); - return (answer); - } - catch (error) { - throw (this._handleError(error, 'join', { room, type, params})); - } - } - - async leave (room, user) { - try { - const answer = await this._mediaController.leave(room, user); - return (answer); - } - catch (error) { - throw (this._handleError(error, 'leave', { room, user })); - } - } - - async publishnsubscribe (user, sourceId, sdp, params) { - try { - const answer = await this._mediaController.publishnsubscribe(user, sourceId, sdp, params); - return (answer); - } - catch (error) { - throw (this._handleError(error, 'publishnsubscribe', { user, sourceId, sdp, params })); - } - } - - async publish (user, room, type, params) { - try { - const answer = await this._mediaController.publish(user, room, type, params); - return (answer); - } - catch (error) { - throw (this._handleError(error, 'publish', { user, room, type, params })); - } - } - - async unpublish (user, mediaId) { - try { - await this._mediaController.unpublish(mediaId); - return ; - } - catch (error) { - throw (this._handleError(error, 'unpublish', { user, mediaId })); - } - } - - async subscribe (user, sourceId, type, params) { - try { - const answer = await this._mediaController.subscribe(user, sourceId, type, params); - - return (answer); - } - catch (error) { - throw (this._handleError(error, 'subscribe', { user, sourceId, type, params })); - } - } - - async unsubscribe (user, mediaId) { - try { - await this._mediaController.unsubscribe(user, mediaId); - return ; - } - catch (error) { - throw (this._handleError(error, 'unsubscribe', { user, mediaId })); - } - } - - async startRecording(userId, mediaId, recordingPath) { - try { - const answer = await this._mediaController.startRecording(userId, mediaId, recordingPath); - return (answer); - } - catch (error) { - throw (this._handleError(error, 'startRecording', { userId, mediaId, recordingPath})); - } - } - - async stopRecording(userId, sourceId, recId) { - try { - let answer = await this._mediaController.stopRecording(userId, sourceId, recId); - return (answer); - } - catch (error) { - throw (this._handleError(error, 'stopRecording', { userId, sourceId, recId })); - } - } - - async connect (source, sink, type) { - try { - await this._mediaController.connect(source, sink, type); - return ; - } - catch (error) { - throw (this._handleError(error, 'connect', { source, sink, type })); - } - } - - async disconnect (source, sink, type) { - try { - await this._mediaController.disconnect(source, sink, type); - return ; - } - catch (error) { - throw (this._handleError(error, 'disconnect', { source, sink, type })); - } - } - - async onEvent (eventName, mediaId) { - try { - const eventTag = this._mediaController.onEvent(eventName, mediaId); - this._mediaController.on(eventTag, (event) => { - this.emitter.emit(eventTag, event); - }); - - return (eventTag); - } - catch (error) { - throw (this._handleError(error, 'onEvent', { eventName, mediaId })); - } - } - - async addIceCandidate (mediaId, candidate) { - try { - await this._mediaController.addIceCandidate(mediaId, candidate); - return ; - } - catch (error) { - throw (this._handleError(error, 'addIceCandidate', { mediaId, candidate })); - } - } - - setStrategy (strategy) { - // TODO - } - - _handleError (error, operation, params) { - const { code, message, details } = error; - const response = { type: 'error', code, message, details, operation, params }; - Logger.error("[mcs-api] Reject operation", response.operation, "with", { error: response }); - - return response; - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/media/MediaController.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/media/MediaController.js deleted file mode 100644 index 8a8328a066..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/media/MediaController.js +++ /dev/null @@ -1,364 +0,0 @@ -'use strict' - -const config = require('config'); -const C = require('../constants/Constants'); -const Logger = require('../../../utils/Logger'); -// Model -const SfuUser = require('../model/SfuUser'); -const Room = require('../model/Room.js'); -const { handleError } = require('../utils/util'); -const LOG_PREFIX = "[mcs-controller]"; - -/* PUBLIC ELEMENTS */ - -let instance = null; - -module.exports = class MediaController { - constructor(emitter) { - if (!instance) { - this.emitter = emitter; - this._rooms = {}; - this._users = {}; - this._mediaSessions = {}; - instance = this; - } - - return instance; - } - - start (_kurentoClient, _kurentoToken, callback) { - // TODO - return callback(null); - } - - stop (callback) { - // TODO - } - - getRoom (roomId) { - return this._rooms[roomId]; - } - - async join (roomId, type, params) { - Logger.info("[mcs-controller] Join room => " + roomId + ' as ' + type); - try { - let session; - const room = await this.createRoomMCS(roomId); - const user = await this.createUserMCS(roomId, type, params); - room.setUser(user); - - if (params.sdp) { - session = user.addSdp(params.sdp); - } - if (params.uri) { - session = user.addUri(params.sdp); - } - - Logger.info("[mcs-controller] Resolving user " + user.id); - return Promise.resolve(user.id); - } - catch (err) { - return Promise.reject(this._handleError(err)); - } - } - - async leave (roomId, userId) { - try { - Logger.info("[mcs-controller] User => " + userId + " wants to leave "); - const room = this.getRoom(roomId); - const user = this.getUserMCS(userId); - - if (!user || !room) { - return Promise.resolve(); - } - - const killedMedias = await user.leave(); - - killedMedias.forEach((mediaId) => { - delete this._mediaSessions[killedMedias[mediaId]]; - }); - - room.destroyUser(user.id); - delete this._users[user.id]; - - return Promise.resolve(); - } - catch (err) { - return Promise.reject(this._handleError(err)); - } - } - - publishnsubscribe (userId, sourceId, sdp, params = {}) { - return new Promise(async (resolve, reject) => { - Logger.info("[mcs-controller] PublishAndSubscribe from user", userId, "to source", sourceId); - Logger.trace("[mcs-controler] PublishAndSubscribe descriptor is", params.descriptor); - - try { - const type = params.type; - const user = this.getUserMCS(userId); - const userId = user.id; - const session = user.addSdp(sdp, type); - const sessionId = session.id; - - this._mediaSessions[session.id] = session; - - const answer = await user.startSession(session.id); - await user.connect(sourceId, session.id); - - Logger.info("[mcs-controller] PublishAndSubscribe return a SDP session with ID", session.id); - resolve({userId, sessionId}); - session.sessionStarted(); - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - publish (userId, roomId, type, params = {}) { - return new Promise(async (resolve, reject) => { - Logger.info("[mcs-controller] Publish from user", userId, "to room", roomId); - Logger.trace("[mcs-controler] Publish descriptor is", params.descriptor); - - try { - const user = await this.getUserMCS(userId); - - Logger.info("[mcs-controller] Fetched user", user.id); - - switch (type) { - case C.MEDIA_TYPE.RTP: - case C.MEDIA_TYPE.WEBRTC: - case C.MEDIA_TYPE.URI: - const { session, answer } = await user.publish(params.descriptor, type, params); - this.addMediaSession(session); - resolve({ answer, sessionId: session.id }); - session.sessionStarted(); - break; - - default: - return reject(this._handleError(C.ERROR.MEDIA_INVALID_TYPE)); - } - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - addMediaSession (session) { - if (typeof this._mediaSessions[session.id] == 'undefined' || - !this._mediaSessions[session.id]) { - this._mediaSessions[session.id] = {}; - } - - this._mediaSessions[session.id] = session; - } - - subscribe (userId, sourceId, type, params = {}) { - return new Promise(async (resolve, reject) => { - Logger.info("[mcs-controller] Subscribe from user", userId, "to source", sourceId); - Logger.trace("[mcs-controler] Subscribe descriptor is", params.descriptor); - - try { - const user = await this.getUserMCS(userId); - const source = this.getMediaSession(sourceId); - - Logger.info("[mcs-controller] Fetched user", user.id); - - switch (type) { - case C.MEDIA_TYPE.RTP: - case C.MEDIA_TYPE.WEBRTC: - case C.MEDIA_TYPE.URI: - const { session, answer } = await user.subscribe(params.descriptor, type, source, params); - this.addMediaSession(session); - resolve({answer, sessionId: session.id}); - session.sessionStarted(); - break; - default: - return reject(this._handleError(C.ERROR.MEDIA_INVALID_TYPE)); - } - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - async unpublish (userId, mediaId) { - try { - const user = this.getUserMCS(userId); - const answer = await user.unpublish(mediaId); - delete this._mediaSessions[mediaId]; - return Promise.resolve(answer); - } - catch (err) { - err = this._handleError(err); - return Promise.reject(this._handleError(err)); - } - } - - async unsubscribe (userId, mediaId) { - try { - const user = this.getUserMCS(userId); - const answer = await user.unsubscribe(mediaId); - delete this._mediaSessions[mediaId]; - return Promise.resolve(); - } - catch (err) { - return Promise.reject(this._handleError(err)); - } - } - - startRecording (userId, sourceId, recordingPath) { - - return new Promise(async (resolve, reject) => { - try { - Logger.info("[mcs-controller] startRecording ", sourceId); - const user = await this.getUserMCS(userId); - const sourceSession = this.getMediaSession(sourceId); - - const session = await user.addRecording(recordingPath); - const answer = await user.startSession(session.id); - await sourceSession.connect(session._mediaElement); - - this._mediaSessions[session.id] = session; - - resolve(answer); - session.sessionStarted(); - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - async stopRecording (userId, sourceId, recId) { - return new Promise(async (resolve, reject) => { - Logger.info("[mcs-controller] Stopping recording session", recId); - try { - const user = await this.getUserMCS(userId); - const recSession = this.getMediaSession(recId); - const sourceSession = this.getMediaSession(sourceId); - - const answer = await user.stopSession(recSession.id); - user.unsubscribe(recSession.id); - this._mediaSessions[recId] = null; - return resolve(answer); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - connect (sourceId, sinkId, type) { - return new Promise(async (resolve, reject) => { - Logger.info("[mcs-controller] Connect", sourceId, "to", sinkId, "with type", type); - - try { - const sourceSession = this.getMediaSession(sourceId); - const sinkSession = this.getMediaSession(sinkId); - - await sourceSession.connect(sinkSession._mediaElement, type); - return resolve(); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - disconnect (sourceId, sinkId, type) { - return new Promise(async (resolve, reject) => { - try { - Logger.info("[mcs-controller] Disconnect", sourceId, "to", sinkId, "with type", type); - const sourceSession = this.getMediaSession(sourceId); - const sinkSession = this.getMediaSession(sinkId); - - await sourceSession.disconnect(sinkSession._mediaElement, type); - return resolve(); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - addIceCandidate (mediaId, candidate) { - return new Promise(async (resolve, reject) => { - try { - const session = this.getMediaSession(mediaId); - await session.addIceCandidate(candidate); - - return resolve(); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - /** - * Creates an empty {Room} room and indexes it - * @param {String} roomId - */ - async createRoomMCS (roomId) { - Logger.info("[mcs-controller] Creating new room with ID", roomId); - - if (this._rooms[roomId] == null) { - this._rooms[roomId] = new Room(roomId, this.emitter); - } - - return Promise.resolve(this._rooms[roomId]); - } - - /** - * Creates an {User} of type @type - * @param {String} roomId - */ - createUserMCS (roomId, type, params) { - let user; - Logger.info("[mcs-controller] Creating a new", type, "user at room", roomId); - - switch (type) { - case C.USERS.SFU: - user = new SfuUser(roomId, type, this.emitter, params.userAgentString, params.descriptor); - break; - case C.USERS.MCU: - Logger.warn("[mcs-controller] createUserMCS MCU TODO"); - break; - default: - Logger.warn("[mcs-controller] Unrecognized user type"); - } - - if(this._users[user.id] == null) { - this._users[user.id] = user; - } - - return Promise.resolve(user); - } - - getUserMCS (userId) { - const user = this._users[userId]; - - if (user == null) { - throw C.ERROR.USER_NOT_FOUND; - } - - return user; - } - - getMediaSession (mediaId) { - const media = this._mediaSessions[mediaId]; - - if (media == null) { - throw C.ERROR.MEDIA_NOT_FOUND; - } - - return media; - } - - _handleError (error) { - return handleError(LOG_PREFIX, error); - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/MediaSession.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/MediaSession.js deleted file mode 100644 index 2d8f37ad82..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/MediaSession.js +++ /dev/null @@ -1,171 +0,0 @@ -/** - * @classdesc - * Model class for external devices - */ - -'use strict' - -const C = require('../constants/Constants'); -const rid = require('readable-id'); -const Kurento = require('../adapters/kurento/kurento'); -const Freeswitch = require('../adapters/freeswitch/freeswitch'); -const config = require('config'); -const kurentoUrl = config.get('kurentoUrl'); -const Logger = require('../../../utils/Logger'); -const { handleError } = require('../utils/util'); -const LOG_PREFIX = "[mcs-media-session]"; - -module.exports = class MediaSession { - constructor ( - emitter, - room, - type = 'WebRtcEndpoint', - options = {} - ) { - this.id = rid(); - this.room = room; - this.emitter = emitter; - this._status = C.STATUS.STOPPED; - this._type = type; - this._mediaElement; - this.subscribedSessions = []; - this._options = options; - this._adapter = options.adapter? options.adapter : C.STRING.KURENTO; - this._name = options.name? options.name : C.STRING.DEFAULT_NAME; - this._MediaServer = MediaSession.getAdapter(this._adapter, emitter); - this.eventQueue = []; - } - - async start () { - this._status = C.STATUS.STARTING; - try { - const client = await this._MediaServer.init(); - - this._mediaElement = await this._MediaServer.createMediaElement(this.room, this._type, this._options); - - Logger.info("[mcs-media-session] New media session", this.id, "in room", this.room, "started with media server endpoint", this._mediaElement); - - this._MediaServer.on(C.EVENT.MEDIA_STATE.MEDIA_EVENT+this._mediaElement, (event) => { - event.id = this.id; - if (this._status !== C.STATUS.STARTED) { - Logger.debug("[mcs-media-session] Media session", this.id, "queuing event", event); - this.eventQueue.push(event); - } - else { - this.emitter.emit(C.EVENT.MEDIA_STATE.MEDIA_EVENT+this.id, event); - } - }); - - this._MediaServer.trackMediaState(this._mediaElement, this._type); - - this._MediaServer.on(C.ERROR.MEDIA_SERVER_OFFLINE, () => { - let event = {}; - event.eventTag = C.ERROR.MEDIA_SERVER_OFFLINE; - event.id = this.id; - this.emitter.emit(C.EVENT.SERVER_STATE+this.id, event); - }); - - this._MediaServer.on(C.EVENT.MEDIA_SERVER_ONLINE, () => { - let event = {}; - event.eventTag = C.EVENT.MEDIA_SERVER_ONLINE; - event.id = this.id; - this.emitter.emit(C.EVENT.SERVER_STATE+this.id); - }); - - return Promise.resolve(this._mediaElement); - } - catch (err) { - err = this._handleError(err); - return Promise.reject(err); - } - } - - async stop () { - if (this._status === C.STATUS.STARTED) { - this._status = C.STATUS.STOPPING; - try { - await this._MediaServer.stop(this.room, this._type, this._mediaElement); - this._status = C.STATUS.STOPPED; - Logger.info("[mcs-media-session] Session", this.id, "stopped with status", this._status); - this.emitter.emit(C.EVENT.MEDIA_SESSION_STOPPED, this.id); - return Promise.resolve(); - } - catch (err) { - err = this._handleError(err); - return Promise.reject(err); - } - } else { - return Promise.resolve(); - } - } - - async connect (sinkId, type = 'ALL') { - try { - Logger.info("[mcs-media-session] Connecting " + this._mediaElement + " => " + sinkId); - await this._MediaServer.connect(this._mediaElement, sinkId, type); - return Promise.resolve(); - } - catch (err) { - err = this._handleError(err); - return Promise.reject(err); - } - } - - async disconnect (sinkId, type = 'ALL') { - try { - Logger.info("[mcs-media-session] Dis-connecting " + this._mediaElement + " => " + sinkId); - await this._MediaServer.disconnect(this._mediaElement, sinkId, type); - return Promise.resolve(); - } - catch (err) { - err = this._handleError(err); - return Promise.reject(err); - } - } - - addMediaEventListener (type, mediaId) { - this._MediaServer.addMediaEventListener (type, mediaId); - } - - sessionStarted () { - if (this._status === C.STATUS.STARTING) { - this._status = C.STATUS.STARTED; - // FIXME: ugly hack, gotta change the event model to a subscription-based - // one to remove this - prlanzarin - setTimeout(() => { - this._flushEventQueue(); - }, 50); - Logger.debug("[mcs-media-session] Session", this.id, "successfully started"); - } - } - - _flushEventQueue () { - Logger.debug("[mcs-media-session] Flushing session", this.id, "events queue"); - while (this.eventQueue.length) { - let event = this.eventQueue.shift(); - this.emitter.emit(C.EVENT.MEDIA_STATE.MEDIA_EVENT+this.id, event); - } - } - - static getAdapter (adapter, emitter) { - let obj = null; - - Logger.info("[mcs-media-session] Session is using the", adapter, "adapter"); - - switch (adapter) { - case C.STRING.KURENTO: - obj = new Kurento(kurentoUrl, emitter); - break; - case C.STRING.FREESWITCH: - obj = new Freeswitch(); - break; - default: Logger.warn("[mcs-media-session] Invalid adapter", this.adapter); } - - return obj; - } - - _handleError (error) { - this._status = C.STATUS.STOPPED; - return handleError(LOG_PREFIX, error); - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/RecordingSession.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/RecordingSession.js deleted file mode 100644 index b358f70ec0..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/RecordingSession.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @classdesc - * Model class for Recording - */ - -'use strict' - -const config = require('config'); -const MediaSession = require('./MediaSession'); - -module.exports = class RecordingSession extends MediaSession { - constructor(emitter, room, recordingPath) { - const uri = recordingPath; - const options = { - mediaProfile: config.get('recordingMediaProfile'), - uri: uri, - stopOnEndOfStream: true - }; - - super(emitter, room, 'RecorderEndpoint', options); - this.filename = uri; - } - - async process () { - const answer = await this._MediaServer.startRecording(this._mediaElement); - return Promise.resolve({ recordingId: this.id, filename: this.filename, meetingId: this.room }); - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/Room.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/Room.js deleted file mode 100644 index ca8ae614a4..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/Room.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @classdesc - * Model class for rooms - */ - -'use strict' - -const C = require('../constants/Constants'); - -module.exports = class Room { - constructor(id, emitter) { - this._id = id; - this._users = {}; - this._mcuUsers = {}; - this.emitter = emitter; - } - - getUser (id) { - return this._users[id]; - } - - setUser (user) { - this._users[user.id] = user; - } - - destroyUser(userId) { - if (this._users[userId]) { - delete this._users[userId]; - if (Object.keys(this._users).length <= 0) { - this.emitter.emit(C.EVENT.ROOM_EMPTY, this._id); - } - } - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/SdpSession.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/SdpSession.js deleted file mode 100644 index e19813cb1b..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/SdpSession.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * @classdesc - * Model class for external devices - */ - -'use strict' - -const C = require('../constants/Constants'); -const SdpWrapper = require('../utils/SdpWrapper'); -const rid = require('readable-id'); -const MediaSession = require('./MediaSession'); -const config = require('config'); -const kurentoUrl = config.get('kurentoUrl'); -const kurentoIp = config.get('kurentoIp'); -const Logger = require('../../../utils/Logger'); - -module.exports = class SdpSession extends MediaSession { - constructor( - emitter, - offer = null, - room, - type = 'WebRtcEndpoint', - options - ) { - super(emitter, room, type, options); - Logger.info("[mcs-sdp-session] New session with options", options); - // {SdpWrapper} SdpWrapper - this._offer; - this._answer; - - if (offer) { - this.setOffer(offer); - } - } - - setOffer (offer) { - if (offer) { - this._offer = new SdpWrapper(offer, this._type); - } - } - - setAnswer (answer) { - if (answer) { - this._answer = new SdpWrapper(answer, this._type); - } - } - - process () { - return new Promise(async (resolve, reject) => { - try { - const answer = await this._MediaServer.processOffer(this._mediaElement, - this._offer.plainSdp, - { name: this._name } - ); - - this.setAnswer(answer); - - // Checks if the media server was able to find a compatible media line - if (answer && !this._hasAvailableCodec()) { - return reject(this._handleError(C.ERROR.MEDIA_NO_AVAILABLE_CODEC)); - } - - const { targetBitrate } = this._options; - - if (answer && targetBitrate && targetBitrate !== '0') { - this._answer.addBandwidth('video', targetBitrate); - } - - if (this._type !== 'WebRtcEndpoint') { - this._offer.replaceServerIpv4(kurentoIp); - return resolve(this._answer? this._answer._plainSdp : null); - } - - await this._MediaServer.gatherCandidates(this._mediaElement); - resolve(this._answer._plainSdp); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - addIceCandidate (candidate) { - return new Promise(async (resolve, reject) => { - try { - await this._MediaServer.addIceCandidate(this._mediaElement, candidate); - resolve(); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - _hasAvailableCodec () { - return (this._offer.hasAvailableVideoCodec() && this._answer.hasAvailableVideoCodec()) || - (this._offer.hasAvailableAudioCodec() && this._answer.hasAvailableAudioCodec()); - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/SfuUser.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/SfuUser.js deleted file mode 100644 index 2d1f917a29..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/SfuUser.js +++ /dev/null @@ -1,211 +0,0 @@ -/** - * @classdesc - * Model class for external devices - */ - -'use strict' - -const User = require('./User'); -const C = require('../constants/Constants'); -const SdpWrapper = require('../utils/SdpWrapper'); -const SdpSession = require('../model/SdpSession'); -const RecordingSession = require('../model/RecordingSession'); -const UriSession = require('../model/UriSession'); -const Logger = require('../../../utils/Logger'); -const isError = require('../utils/util').isError; - -module.exports = class SfuUser extends User { - constructor(_roomId, type, emitter, userAgentString = C.STRING.ANONYMOUS, sdp = null, uri = null) { - super(_roomId); - // {SdpWrapper} SdpWrapper - this._sdp; - this._mediaSessions = {} - this.userAgentString; - this.emitter = emitter; - if (sdp) { - this.addSdp(sdp); - } - if (uri) { - this.addUri(uri); - } - } - - // TODO switch from type to children UriSessions (RTSP|HTTP|etc) - async addUri (uri, type) { - const session = new UriSession(uri, type); - this.emitter.emit(C.EVENT.NEW_SESSION+this.id, session.id); - - session.emitter.once(C.EVENT.MEDIA_SESSION_STOPPED, (sessId) => { - if (sessId === session.id) { - Logger.info("[mcs-sfu-user] URI session stopped."); - this._mediaSessions[sessId] = null; - } - }); - - this._mediaSessions[session.id] = session; - - Logger.info("[mcs-sfu-user] Added new URI session", session.id, "to user", this.id); - - return session; - } - - addSdp (sdp, type, options) { - // TODO switch from type to children SdpSessions (WebRTC|SDP) - const session = new SdpSession(this.emitter, sdp, this.roomId, type, options); - session.emitter.on(C.EVENT.MEDIA_SESSION_STOPPED, (sessId) => { - if (sessId === session.id) { - Logger.info("[mcs-sfu-user] Session ", sessId, "stopped, cleaning it..."); - this._mediaSessions[sessId] = null; - } - }); - - this._mediaSessions[session.id] = session; - - Logger.info("[mcs-sfu-user] Added new SDP session", session.id, "to user", this.id); - - return session; - } - - addRecording (recordingPath) { - try { - const session = new RecordingSession(this.emitter, this.roomId, recordingPath); - this.emitter.emit(C.EVENT.NEW_SESSION+this.id, session.id); - - session.emitter.once(C.EVENT.MEDIA_SESSION_STOPPED, (sessId) => { - if (sessId === session.id) { - Logger.info("[mcs-sfu-user] Recording session stopped."); - this._mediaSessions[sessId] = null; - } - }); - - this._mediaSessions[session.id] = session; - Logger.info("[mcs-sfu-user] Added new recording session", session.id, "to user", this.id); - - return session; - } - catch (err) { - this._handleError(err); - } - } - - - startSession (sessionId) { - const session = this._mediaSessions[sessionId]; - return new Promise(async (resolve, reject) => { - try { - const mediaElement = await session.start(); - const answer = await session.process(); - resolve(answer); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - subscribe (sdp, type, source, params) { - return new Promise(async (resolve, reject) => { - try { - const session = this.addSdp(sdp, type, params); - const answer = await this.startSession(session.id); - await source.connect(session._mediaElement); - resolve({ session, answer }); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - publish (sdp, type, params) { - return new Promise(async (resolve, reject) => { - try { - const session = this.addSdp(sdp, type, params); - const answer = await this.startSession(session.id); - resolve({ session, answer }); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - unsubscribe (mediaId) { - return new Promise(async (resolve, reject) => { - try { - await this.stopSession(mediaId); - resolve(mediaId); - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - unpublish (mediaId) { - return new Promise(async (resolve, reject) => { - try { - await this.stopSession(mediaId); - resolve(mediaId); - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - stopSession (sessionId) { - const session = this._mediaSessions[sessionId]; - - return new Promise(async (resolve, reject) => { - try { - if (session) { - Logger.info("[mcs-sfu-user] Stopping session => " + sessionId); - await session.stop(); - delete this._mediaSessions[sessionId]; - } - - return resolve(); - } - catch (err) { - reject(this._handleError(err)); - } - }); - } - - connect (sourceId, sinkId) { - const session = this._mediaSessions[sourceId]; - - return new Promise(async (resolve, reject) => { - try { - if (session == null) { - return reject(this._handleError(C.ERROR.MEDIA_NOT_FOUND)); - } - Logger.info("[mcs-sfu-user] Connecting sessions " + sourceId + "=>" + sinkId); - await session.connect(sinkId); - return resolve(); - } - catch (err) { - return reject(this._handleError(err)); - } - }); - } - - async leave () { - const sessions = Object.keys(this._mediaSessions); - let stopProcedures = []; - Logger.info("[mcs-sfu-user] User sessions will be killed"); - - try { - for (let i = 0; i < sessions.length; i++) { - stopProcedures.push(this.stopSession(sessions[i])); - } - - return Promise.all(stopProcedures); - } - catch (err) { - err = this._handleError(err); - Promise.reject(err); - } - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/UriSession.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/UriSession.js deleted file mode 100644 index 904d8fc739..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/UriSession.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @classdesc - * Model class for external devices - */ - -'use strict' - -const C = require('../constants/Constants'); -const rid = require('readable-id'); -const EventEmitter = require('events').EventEmitter; -const MediaSession = require('./MediaSession'); -const Logger = require('../../../utils/Logger'); - -module.exports = class UriSession extends MediaSession { - constructor(uri = null) { - super(); - this.id = rid(); - this._status = C.STATUS.STOPPED; - this._uri; - if (uri) { - this.setUri(uri); - } - } - - setUri (uri) { - this._uri = uri; - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/User.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/User.js deleted file mode 100644 index bd5b993c96..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/model/User.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @classdesc - * Model class for external devices - */ - -'use strict' - -const rid = require('readable-id'); -const User = require('./User'); -const C = require('../constants/Constants.js'); -const Logger = require('../../../utils/Logger'); -const { handleError } = require('../utils/util'); -const LOG_PREFIX = "[mcs-user]"; - -module.exports = class User { - constructor(roomId, type, userAgentString = C.STRING.ANONYMOUS) { - this.roomId = roomId; - this.id = rid(); - this.userAgentString = userAgentString; - } - - _handleError (error) { - return handleError(LOG_PREFIX, error); - } -} diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/utils/SdpWrapper.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/utils/SdpWrapper.js deleted file mode 100644 index a19912bdba..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/utils/SdpWrapper.js +++ /dev/null @@ -1,295 +0,0 @@ -/** - * @classdesc - * Utils class for manipulating SDP - */ - -'use strict' - -var config = require('config'); -var transform = require('sdp-transform'); - -module.exports = class SdpWrapper { - constructor(sdp) { - this._plainSdp = sdp; - this._jsonSdp = transform.parse(sdp); - this._mediaLines = {}; - this._mediaCapabilities = {}; - this._profileThreshold = "ffffff"; - this.processSdp(); - } - - get plainSdp() { - return this._plainSdp; - } - - get jsonSdp() { - return this._jsonSdp; - } - - removeFmtp () { - return this._plainSdp.replace(/(a=fmtp:).*/g, ''); - } - - replaceServerIpv4 (ipv4) { - return this._plainSdp.replace(/(IP4\s[0-9.]*)/g, 'IP4 ' + ipv4); - } - - hasAudio () { - return this._mediaCapabilities.hasAudio; - } - - hasVideo () { - return this._mediaCapabilities.hasVideo; - } - - hasMultipleVideo () { - return this._mediaCapabilities.hasMultipleVideo; - } - - hasAvailableVideoCodec () { - return this._mediaCapabilities.hasAvailableVideoCodec; - } - - hasAvailableAudioCodec () { - return this._mediaCapabilities.hasAvailableAudioCodec; - } - - addBandwidth (type, bw) { - // Bandwidth format - // { type: 'TIAS or AS', limit: 2048000 } - for(var ml of this._jsonSdp.media) { - if(ml.type === type ) { - ml['bandwidth'] = []; - ml.bandwidth.push({ type: 'TIAS', limit: (bw >>> 0) * 1000 }); - ml.bandwidth.push({ type: 'AS', limit: bw }); - } - } - - this._plainSdp = transform.write(this._jsonSdp); - } - - /** - * Given a SDP, test if there is an audio description in it - * @return {boolean} true if there is more than one video description, else false - */ - _hasAudio () { - return /(m=audio)/i.test(this._plainSdp); - } - - /** - * Given a SDP, test if there is a video description in it - * @return {boolean} true if there is a video description, else false - */ - _hasVideo () { - return /(m=video)/i.test(this._plainSdp); - } - - /** - * Given a SDP, test if there is more than on video description - * @return {boolean} true if there is more than one video description, else false - */ - _hasMultipleVideo () { - return /(m=video)([\s\S]*\1){1,}/i.test(this._plainSdp); - } - - /** - * Tests if the current SDP has an available and valid video codec - * @return {boolean} true if there is an RTP video session specified and active - */ - _hasAvailableVideoCodec () { - return this._jsonSdp.media.some((ml) => { - let { type, rtp, port } = ml; - return type === 'video' && rtp && rtp.length > 0 && port !== 0; - }); - } - - /** - * Tests if the current SDP has an available and valid audio codec - * @return {boolean} true if there is an RTP audio session specified and active - */ - _hasAvailableAudioCodec () { - return this._jsonSdp.media.some((ml) => { - let { type, rtp, port } = ml; - return type === 'audio' && rtp && rtp.length > 0 && port !== 0; - }); - } - - /** - * Given a SDP, return its Session Description - * @param {string} sdp The Session Descriptor - * @return {string} Session description (SDP until the first media line) - */ - getSessionDescription (sdp) { - return sdp.match(/[\s\S]+?(?=m=audio|m=video)/i); - } - - removeSessionDescription (sdp) { - return sdp.match(/(?=[\s\S]+?)(m=audio[\s\S]+|m=video[\s\S]+)/i)[1]; - } - - getVideoParameters (sdp) { - var res = transform.parse(sdp); - var params = {}; - params.fmtp = ""; - params.codecId = 96; - var pt = 0; - for(var ml of res.media) { - if(ml.type == 'video') { - if (typeof ml.fmtp[0] != 'undefined' && ml.fmtp) { - params.codecId = ml.fmtp[0].payload; - params.fmtp = ml.fmtp[0].config; - return params; - } - } - } - return params; - } - - /** - * Given a SDP, return its Content Description - * @param {string} sdp The Session Descriptor - * @return {string} Content Description (SDP after first media description) - */ - getContentDescription (sdp) { - var res = transform.parse(sdp); - res.media = res.media.filter(function (ml) { return ml.type == "video" }); - var mangledSdp = transform.write(res); - if(typeof mangledSdp != undefined && mangledSdp && mangledSdp != "") { - return mangledSdp; - } - else - return sdp; - } - - /** - * Given a SDP, return its first Media Description - * @param {string} sdp The Session Descriptor - * @return {string} Content Description (SDP after first media description) - */ - getAudioDescription (sdp) { - var res = transform.parse(sdp); - res.media = res.media.filter(function (ml) { return ml.type == "audio" }); - // Hack: Some devices (Snom, Pexip) send crypto with RTP/AVP - // That is forbidden according to RFC3711 and FreeSWITCH rebukes it - res = this.removeTransformCrypto(res); - var mangledSdp = transform.write(res); - this.getSessionDescription(mangledSdp); - if(typeof mangledSdp != undefined && mangledSdp && mangledSdp != "") { - return mangledSdp; - } - else { - return sdp; - } - } - - /** - * Given a SDP, return its first Media Description - * @param {string} sdp The Session Descriptor - * @return {string} Content Description (SDP after first media description) - */ - getMainDescription () { - var res = transform.parse(this._plainSdp); - // Filter should also carry && ml.invalid[0].value != 'content:slides'; - // when content is enabled - res.media = res.media.filter(function (ml) { return ml.type == "video"}); //&& ml.invalid[0].value != 'content:slides'}); - var mangledSdp = transform.write(res); - if (typeof mangledSdp != undefined && mangledSdp && mangledSdp != "") { - return mangledSdp; - } - else { - return sdp; - } - } - - /** - * Given a JSON SDP, remove associated crypto 'a=' lines from media lines - * WARNING: HACK MADE FOR FreeSWITCH ~1.4 COMPATIBILITY - * @param {Object} sdp The Session Descriptor JSON - * @return {Object} JSON SDP without crypto lines - */ - removeTransformCrypto (sdp) { - for(var ml of sdp.media) { - delete ml['crypto']; - } - return sdp; - } - - removeHighQualityFmtps (sdp) { - let res = transform.parse(sdp); - let maxProfileLevel = config.get('kurento.maximum_profile_level_hex'); - let pt = 0; - let idx = 0; - for(var ml of res.media) { - if(ml.type == 'video') { - for(var fmtp of ml.fmtp) { - let fmtpConfig = transform.parseParams(fmtp.config); - let profileId = fmtpConfig['profile-level-id']; - if(typeof profileId !== 'undefined' && parseInt(profileId, 16) > parseInt(maxProfileLevel, 16)) { - pt = fmtp.payload; - delete ml.fmtp[idx]; - ml.rtp = ml.rtp.filter((rtp) => { return rtp.payload != pt}); - } - else { - // Remove fmtp further specifications - //let configProfile = "profile-level-id="+profileId; - //fmtp.config = configProfile; - } - idx++; - } - } - } - var mangledSdp = transform.write(res); - return mangledSdp; - } - - processSdp () { - let description = this._plainSdp; - - description = description.toString().replace(/telephone-event/, "TELEPHONE-EVENT"); - - this._mediaCapabilities.hasVideo = this._hasVideo(); - this._mediaCapabilities.hasAudio = this._hasAudio(); - this._mediaCapabilities.hasContent = this._hasMultipleVideo(); - this._mediaCapabilities.hasAvailableVideoCodec = this._hasAvailableVideoCodec(); - this._mediaCapabilities.hasAvailableAudioCodec = this._hasAvailableAudioCodec(); - this.sessionDescriptionHeader = this.getSessionDescription(description); - this.audioSdp = this.getAudioDescription(description); - this.mainVideoSdp = this.getMainDescription(description); - this.contentVideoSdp = this.getContentDescription(description); - - return; - } - - /* DEVELOPMENT METHODS */ - _disableMedia (sdp) { - return sdp.replace(/(m=application\s)\d*/g, "$10"); - }; - - /** - * Given a SDP, add Floor Control response - * @param {string} sdp The Session Descriptor - * @return {string} A new Session Descriptor with Floor Control - */ - _addFloorControl (sdp) { - return sdp.replace(/a=inactive/i, 'a=sendrecv\r\na=floorctrl:c-only\r\na=setup:active\r\na=connection:new'); - } - - /** - * Given a SDP, add Floor Control response to reinvite - * @param {string} sdp The Session Descriptor - * @return {string} A new Session Descriptor with Floor Control Id - */ - _addFloorId (sdp) { - sdp = sdp.replace(/(a=floorctrl:c-only)/i, '$1\r\na=floorid:1 m-stream:3'); - return sdp.replace(/(m=video.*)([\s\S]*?m=video.*)([\s\S]*)/i, '$1\r\na=content:main\r\na=label:1$2\r\na=content:slides\r\na=label:3$3'); - } - - /** - * Given the string representation of a Session Descriptor, remove it's video - * @param {string} sdp The Session Descriptor - * @return {string} A new Session Descriptor without the video - */ - _removeVideoSdp (sdp) { - return sdp.replace(/(m=video[\s\S]+)/g,''); - }; -}; diff --git a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/utils/util.js b/labs/bbb-webrtc-sfu/lib/mcs-core/lib/utils/util.js deleted file mode 100644 index c0be5773bb..0000000000 --- a/labs/bbb-webrtc-sfu/lib/mcs-core/lib/utils/util.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @classdesc - * Utils class for mcs-core - * @constructor - * - */ - -const C = require('../constants/Constants'); -const Logger = require('../../../utils/Logger'); - -exports.isError = (error) => { - return error && error.stack && error.message && typeof error.stack === 'string' - && typeof error.message === 'string'; -} - -exports.handleError = (logPrefix, error) => { - let { message, code, stack, data, details } = error; - if (code && code >= C.ERROR.MIN_CODE && code <= C.ERROR.MAX_CODE) { - return error; - } - - if (code == null) { - ({ code, message } = C.ERROR.MEDIA_GENERIC_ERROR); - } - else { - ({ code, message } = error); - } - - if (!this.isError(error)) { - error = new Error(message); - } - - error.code = code; - error.message = message; - error.stack = stack - - if (details) { - error.details = details; - } - else { - error.details = message; - } - - Logger.debug(logPrefix, "Handling error", error.code, error.message); - Logger.trace(logPrefix, error.stack); - - return error; -} diff --git a/labs/bbb-webrtc-sfu/lib/media-handler.js b/labs/bbb-webrtc-sfu/lib/media-handler.js deleted file mode 100644 index e5c104d927..0000000000 --- a/labs/bbb-webrtc-sfu/lib/media-handler.js +++ /dev/null @@ -1,64 +0,0 @@ -var config = require('config'); -var Constants = require('./bbb/messages/Constants'); - -module.exports.generateSdp = function(remote_ip_address, remote_video_port) { - return "v=0\r\n" - + "o=- 0 0 IN IP4 " + remote_ip_address + "\r\n" - + "s=Kurento-SCREENSHARE\r\n" - + "c=IN IP4 " + remote_ip_address + "\r\n" - + "t=0 0\r\n" - + "m=video " + remote_video_port + " RTP/AVPF 96\r\n" - + "a=rtpmap:96 H264/90000\r\n" - + "a=ftmp:96\r\n"; -} - -module.exports.generateVideoSdp = function (sourceIpAddress, sourceVideoPort) { - return "v=0\r\n" - + "o=- 0 0 IN IP4 " + sourceIpAddress + "\r\n" - + "s=Kurento-SCREENSHARE\r\n" - + 'm=video ' + sourceVideoPort + ' ' + this.videoConfiguration.rtpProfile + ' ' + this.videoConfiguration.codecId + '\r\n' - + 'a=' + this.videoConfiguration.sendReceive + '\r\n' - + 'c=IN IP4 ' + sourceIpAddress + '\r\n' - + 'a=rtpmap:' + this.videoConfiguration.codecId + ' ' + this.videoConfiguration.codecName + '/' + this.videoConfiguration.codecRate + '\r\n' - + 'a=fmtp:' + this.videoConfiguration.codecId + '\r\n' - + 'a=rtcp-fb:' + this.videoConfiguration.codecId + ' ccm fir \r\n' - + 'a=rtcp-fb:' + this.videoConfiguration.codecId + ' nack \r\n' - + 'a=rtcp-fb:' + this.videoConfiguration.codecId + ' nack pli \r\n' - + 'a=rtcp-fb:' + this.videoConfiguration.codecId + ' goog-remb \r\n'; -}; - -module.exports.videoConfiguration = { - codecId: '96', - sendReceive: 'sendrecv', - rtpProfile: 'RTP/AVPF', - codecName: 'H264', - frameRate: '30.000000', - codecRate: '90000' -}; - -module.exports.generateStreamUrl = function (address, meeting, path) { - return "rtmp://" + address + "/video-broadcast/" + meeting + "/" + path; -} - -module.exports.generateTranscoderParams = function (localIp, destIp, sendPort, recvPort, input, streamType, transcoderType, codec, callername, voiceConf) { - var rtpParams = {}; - rtpParams[Constants.LOCAL_IP_ADDRESS] = localIp; - rtpParams[Constants.LOCAL_VIDEO_PORT] = sendPort; - rtpParams[Constants.DESTINATION_IP_ADDRESS] = destIp; - rtpParams[Constants.REMOTE_VIDEO_PORT] = recvPort; - rtpParams[Constants.INPUT] = input; - rtpParams[Constants.STREAM_TYPE] = streamType; - rtpParams[Constants.TRANSCODER_TYPE] = transcoderType; - rtpParams[Constants.TRANSCODER_CODEC] = codec; - rtpParams[Constants.CALLERNAME] = callername; - rtpParams[Constants.VOICE_CONF] = voiceConf; - return rtpParams; -} - -module.exports.getPort = function (min_port, max_port) { - return Math.floor((Math.random() * (max_port - min_port + 1) + min_port)); -} - -module.exports.getVideoPort = function () { - return this.getPort(config.get('minVideoPort'), config.get('maxVideoPort')); -} diff --git a/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareManager.js b/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareManager.js deleted file mode 100644 index 0b42746094..0000000000 --- a/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareManager.js +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Lucas Fialho Zawacki - * Paulo Renato Lanzarin - * (C) Copyright 2017 Bigbluebutton - * - */ - -"use strict"; - -const BigBlueButtonGW = require('../bbb/pubsub/bbb-gw'); -const Screenshare = require('./screenshare'); -const BaseManager = require('../base/BaseManager'); -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); -const errors = require('../base/errors'); - -module.exports = class ScreenshareManager extends BaseManager { - constructor (connectionChannel, additionalChannels, logPrefix) { - super(connectionChannel, additionalChannels, logPrefix); - this.sfuApp = C.SCREENSHARE_APP; - this.messageFactory(this._onMessage); - this._iceQueues = {}; - } - - async _onMessage(message) { - Logger.debug(this._logPrefix, 'Received message [' + message.id + '] from connection', message.connectionId); - - const sessionId = message.voiceBridge; - const connectionId = message.connectionId; - const role = message.role; - const sdpOffer = message.sdpOffer - const callerName = message.callerName; - let iceQueue, session; - - session = this._fetchSession(sessionId); - iceQueue = this._fetchIceQueue(sessionId); - - switch (message.id) { - case 'start': - if (!session) { - session = new Screenshare(connectionId, this._bbbGW, - sessionId, connectionId, message.vh, message.vw, - message.internalMeetingId); - this._sessions[sessionId] = session; - } - - // starts screenshare peer with role by sending sessionID, websocket and sdpoffer - try { - const sdpAnswer = await session.start(sessionId, connectionId, sdpOffer, callerName, role) - Logger.info(this._logPrefix, "Started peer", sessionId, " for connection", connectionId); - - // Empty ice queue after starting session - if (iceQueue) { - let candidate; - while(candidate = iceQueue.pop()) { - session.onIceCandidate(candidate, role, callerName); - } - } - - this._bbbGW.publish(JSON.stringify({ - connectionId: connectionId, - type: C.SCREENSHARE_APP, - role: role, - id : 'startResponse', - response : 'accepted', - sdpAnswer : sdpAnswer - }), C.FROM_SCREENSHARE); - - session.once(C.MEDIA_SERVER_OFFLINE, (event) => { - this._stopSession(sessionId); - }); - - // listen for presenter change to avoid inconsistent states on reconnection - if (role === C.SEND_ROLE) { - this._bbbGW.once(C.PRESENTER_ASSIGNED_2x+message.internalMeetingId, async (payload) => { - Logger.info(this._logPrefix, "Presenter changed, forcibly closing screensharing session at", message.internalMeetingId); - await this.closeSession(session, connectionId, role, sessionId); - this._bbbGW.publish(JSON.stringify({ - connectionId: connectionId, - type: C.SCREENSHARE_APP, - id : 'close', - }), C.FROM_SCREENSHARE); - }); - } - - Logger.info(this._logPrefix, "Sending startResponse to peer", sessionId, "for connection", session._id); - } - catch (error) { - let errorMessage = this._handleError(this._logPrefix, connectionId, callerName, role, error); - this._bbbGW.publish(JSON.stringify({ - ...errorMessage - }), C.FROM_SCREENSHARE); - } - break; - - case 'stop': - Logger.info(this._logPrefix, 'Received stop message for session', sessionId, "at connection", connectionId); - - if (session) { - session._stop(sessionId); - } else { - Logger.warn(this._logPrefix, "There was no screensharing session on stop for", sessionId); - } - break; - - case 'iceCandidate': - if (session && session.constructor === Screenshare) { - session.onIceCandidate(message.candidate, role, callerName); - } else { - Logger.info(this._logPrefix, "Queueing ice candidate for later in screenshare", message.voiceBridge); - iceQueue.push(message.candidate); - } - break; - - case 'close': - Logger.info(this._logPrefix, 'Connection ' + connectionId + ' closed'); - this.closeSession(session, connectionId, role, sessionId); - break; - - default: - const errorMessage = this._handleError(this._logPrefix, connectionId, null, null, errors.SFU_INVALID_REQUEST); - this._bbbGW.publish(JSON.stringify({ - ...errorMessage, - }), C.FROM_SCREENSHARE); - break; - } - } - - async closeSession (session, connectionId, role, sessionId) { - if (session && session.constructor == Screenshare) { - if (role === C.SEND_ROLE && session) { - Logger.info(this._logPrefix, "Stopping presenter " + sessionId); - await this._stopSession(sessionId); - return; - } - if (role === C.RECV_ROLE && session) { - Logger.info(this._logPrefix, "Stopping viewer " + sessionId); - await session.stopViewer(connectionId); - } - } - } -}; diff --git a/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareProcess.js b/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareProcess.js deleted file mode 100644 index 7b871719ea..0000000000 --- a/labs/bbb-webrtc-sfu/lib/screenshare/ScreenshareProcess.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -const ScreenshareManager = require('./ScreenshareManager'); -const BaseProcess = require('../base/BaseProcess'); -const C = require('../bbb/messages/Constants'); - -const manager = new ScreenshareManager(C.TO_SCREENSHARE, [C.FROM_BBB_TRANSCODE_SYSTEM_CHAN, C.FROM_AKKA_APPS], C.SCREENSHARE_MANAGER_PREFIX); -const newProcess = new BaseProcess(manager, C.SCREENSHARE_PROCESS_PREFIX); - -newProcess.start(); diff --git a/labs/bbb-webrtc-sfu/lib/screenshare/screenshare.js b/labs/bbb-webrtc-sfu/lib/screenshare/screenshare.js deleted file mode 100644 index 1c970224ee..0000000000 --- a/labs/bbb-webrtc-sfu/lib/screenshare/screenshare.js +++ /dev/null @@ -1,489 +0,0 @@ -/* - * Lucas Fialho Zawacki - * Paulo Renato Lanzarin - * (C) Copyright 2017 Bigbluebutton - * - */ - -'use strict' - -const C = require('../bbb/messages/Constants'); -const MediaHandler = require('../media-handler'); -const Messaging = require('../bbb/messages/Messaging'); -const moment = require('moment'); -const h264_sdp = require('../h264-sdp'); -const now = moment(); -const MCSApi = require('../mcs-core/lib/media/MCSApiStub'); -const Logger = require('../utils/Logger'); -const BaseProvider = require('../base/BaseProvider'); -const config = require('config'); -const kurentoIp = config.get('kurentoIp'); -const localIpAddress = config.get('localIpAddress'); -const FORCE_H264 = config.get('screenshare-force-h264'); -const PREFERRED_H264_PROFILE = config.get('screenshare-preferred-h264-profile'); -const SCREENSHARE_TARGET_BITRATE = config.get('screenshare-target-bitrate'); -const SHOULD_RECORD = config.get('recordScreenSharing'); -const KEYFRAME_INTERVAL = config.get('screenshareKeyframeInterval'); -const LOG_PREFIX = "[screenshare]"; - -// Global MCS endpoints mapping. These hashes maps IDs generated by the mcs-core -// lib to the ones generate in the ScreenshareManager -var sharedScreens = {}; -var rtpEndpoints = {}; - -module.exports = class Screenshare extends BaseProvider { - constructor(id, bbbgw, voiceBridge, userId, vh, vw, meetingId) { - super(); - this.sfuApp = C.SCREENSHARE_APP; - this.mcs = new MCSApi(); - this.mcsUserId; - this.userId = userId; - this._id = id; - this._BigBlueButtonGW = bbbgw; - this._presenterEndpoint = null; - this._ffmpegEndpoint = null; - this._voiceBridge = voiceBridge; - this._meetingId = meetingId; - this._streamUrl = ""; - this._vw = vw; - this._vh = vh; - this._presenterCandidatesQueue = []; - this._viewersEndpoint = []; - this._viewersCandidatesQueue = []; - this._status = C.MEDIA_STOPPED; - this._rtmpBroadcastStarted = false; - this.recording = {}; - this.isRecorded = false; - this._recordingSubPath = 'screenshare'; - - this._BigBlueButtonGW.on(C.RECORDING_STATUS_REPLY_MESSAGE_2x+meetingId, (payload) => { - Logger.info("[Screenshare] RecordingStatusReply ", payload.recorded); - - if (payload.recorded) { - this.isRecorded = true; - } - }); - } - - async onIceCandidate (candidate, role, userId) { - Logger.debug(LOG_PREFIX, "onIceCandidate", role, userId, candidate); - switch (role) { - case C.SEND_ROLE: - if (this._presenterEndpoint) { - try { - await this.flushCandidatesQueue(this._presenterEndpoint, this._presenterCandidatesQueue); - await this.mcs.addIceCandidate(this._presenterEndpoint, candidate); - } catch (err) { - this._handleError(LOG_PREFIX, err, role, userId); - } - } else { - Logger.debug(LOG_PREFIX, "Pushing ICE candidate to presenter queue"); - this._presenterCandidatesQueue.push(candidate); - } - case C.RECV_ROLE: - let endpoint = this._viewersEndpoint[userId]; - if (endpoint) { - try { - await this.flushCandidatesQueue(endpoint, this._viewersCandidatesQueue[userId]); - await this.mcs.addIceCandidate(endpoint, candidate); - } catch (err) { - this._handleError(LOG_PREFIX, err, role, userId); - } - } else { - this._viewersCandidatesQueue[userId] = []; - Logger.debug(LOG_PREFIX, "Pushing ICE candidate to viewer queue", userId); - this._viewersCandidatesQueue[userId].push(candidate); - } - break; - default: - Logger.warn(LOG_PREFIX, "Unknown role", role); - } - } - - flushCandidatesQueue (mediaId, queue) { - return new Promise((resolve, reject) => { - Logger.debug(LOG_PREFIX, "flushCandidatesQueue", queue); - if (mediaId) { - let iceProcedures = queue.map((candidate) => { - this.mcs.addIceCandidate(mediaId, candidate); - }); - - return Promise.all(iceProcedures).then(() => { - queue = []; - resolve(); - }).catch((err) => { - Logger.error(LOG_PREFIX, "ICE candidate could not be added to media controller.", err); - reject(err); - }); - } - }); - } - - mediaStateRtp (event) { - let msEvent = event.event; - - switch (event.eventTag) { - case "MediaStateChanged": - break; - - case "MediaFlowOutStateChange": - Logger.info('[screenshare]', msEvent.type, '[' + msEvent.state? msEvent.state : 'UNKNOWN_STATE' + ']', 'for media session ', event.id); - break; - - case "MediaFlowInStateChange": - Logger.info('[screenshare]', msEvent.type, '[' + msEvent.state? msEvent.state : 'UNKNOWN_STATE' + ']', 'for media session ', event.id); - if (msEvent.state === 'FLOWING') { - this._onRtpMediaFlowing(); - } - else { - this._onRtpMediaNotFlowing(); - } - break; - - default: Logger.warn(LOG_PREFIX, "Unrecognized event", event); - } - } - - mediaStateWebRtc (event, id) { - let msEvent = event.event; - - switch (event.eventTag) { - case "OnIceCandidate": - let candidate = msEvent.candidate; - Logger.debug(LOG_PREFIX, 'Received ICE candidate from mcs-core for media session', event.id, '=>', candidate, "for connection", id); - - this._BigBlueButtonGW.publish(JSON.stringify({ - connectionId: id, - type: C.SCREENSHARE_APP, - id : 'iceCandidate', - cameraId: this._id, - candidate : candidate - }), C.FROM_SCREENSHARE); - - break; - - case "MediaStateChanged": - break; - - case "MediaFlowOutStateChange": - case "MediaFlowInStateChange": - Logger.info('[screenshare]', msEvent.type, '[' + msEvent.state? msEvent.state : 'UNKNOWN_STATE' + ']', 'for media session', event.id); - break; - - default: Logger.warn(LOG_PREFIX, "Unrecognized event", event); - } - } - - recordingState(event) { - const msEvent = event.event; - Logger.info('[Recording]', msEvent.type, '[', msEvent.state, ']', 'for recording session', event.id, 'for screenshare', this.streamName); - } - - async startRecording() { - return new Promise(async (resolve, reject) => { - try { - const recordingPath = this.getRecordingPath(this._meetingId, this._recordingSubPath, this._voiceBridge); - this.recording = await this.mcs.startRecording(this.mcsUserId, this._presenterEndpoint, recordingPath); - this.mcs.on('MediaEvent' + this.recording.recordingId, this.recordingState.bind(this)); - this.sendStartShareEvent(); - resolve(this.recording); - } catch (err) { - reject(this._handleError(LOG_PREFIX, err)); - } - }); - } - - sendStartShareEvent () { - let shareEvent = Messaging.generateWebRTCShareEvent('StartWebRTCDesktopShareEvent', this.recording.meetingId, this.recording.filename); - this._BigBlueButtonGW.writeMeetingKey(this.recording.meetingId, shareEvent, function(error) { - Logger.warn(LOG_PREFIX, 'Error writing START share event error', error); - }); - } - - sendStopShareEvent () { - let shareEvent = Messaging.generateWebRTCShareEvent('StopWebRTCDesktopShareEvent', this.recording.meetingId, this.recording.filename); - this._BigBlueButtonGW.writeMeetingKey(this.recording.meetingId, shareEvent, function(error) { - Logger.warn(LOG_PREFIX, 'Error writing STOP share event error', error); - }); - } - - sendGetRecordingStatusRequestMessage(userId) { - let req = Messaging.generateRecordingStatusRequestMessage(this._meetingId, userId); - - this._BigBlueButtonGW.publish(req, C.TO_AKKA_APPS); - } - - start (sessionId, connectionId, sdpOffer, userId, role) { - return new Promise(async (resolve, reject) => { - this._status = C.MEDIA_STARTING; - - // Forces H264 with a possible preferred profile - if (FORCE_H264) { - sdpOffer = h264_sdp.transform(sdpOffer, PREFERRED_H264_PROFILE); - } - - // Start the recording process - if (SHOULD_RECORD && role === C.SEND_ROLE) { - this.sendGetRecordingStatusRequestMessage(userId); - } - - Logger.info(LOG_PREFIX, "Starting session", this._voiceBridge + '-' + role); - if (!this.mcsUserId) { - try { - this.mcsUserId = await this.mcs.join(this._meetingId, 'SFU', {}); - Logger.info(LOG_PREFIX, "MCS Join for", this._id, "returned", this.mcsUserId); - - } - catch (error) { - Logger.error(LOG_PREFIX, "MCS Join returned error =>", error); - return reject(this._handleError(LOG_PREFIX, error, role, userId)); - } - } - - if (role === C.RECV_ROLE) { - try { - const sdpAnswer = await this._startViewer(connectionId, this._voiceBridge, sdpOffer, userId, this._presenterEndpoint) - return resolve(sdpAnswer); - } - catch (err) { - return reject(this._handleError(LOG_PREFIX, err, role, userId)); - } - } - - if (role === C.SEND_ROLE) { - try { - const sdpAnswer = await this._startPresenter(sdpOffer); - return resolve(sdpAnswer); - } - catch (err) { - return reject(this._handleError(LOG_PREFIX, err, role, userId)); - } - } - }); - } - - _startPresenter (sdpOffer) { - return new Promise(async (resolve, reject) => { - try { - const retSource = await this.mcs.publish(this.mcsUserId, this._meetingId, 'WebRtcEndpoint', {descriptor: sdpOffer, targetBitrate: SCREENSHARE_TARGET_BITRATE }); - - this._presenterEndpoint = retSource.sessionId; - sharedScreens[this._voiceBridge] = this._presenterEndpoint; - let presenterSdpAnswer = retSource.answer; - await this.flushCandidatesQueue(this._presenterEndpoint, this._presenterCandidatesQueue); - - this.mcs.on('MediaEvent' + this._presenterEndpoint, (event) => { - this.mediaStateWebRtc(event, this._id) - }); - - Logger.info(LOG_PREFIX, "MCS publish for user", this.mcsUserId, "returned", this._presenterEndpoint); - - let sendVideoPort = MediaHandler.getVideoPort(); - let rtpSdpOffer = MediaHandler.generateVideoSdp(localIpAddress, sendVideoPort); - - const retRtp = await this.mcs.subscribe(this.mcsUserId, sharedScreens[this._voiceBridge], 'RtpEndpoint', { descriptor: rtpSdpOffer, keyframeInterval: KEYFRAME_INTERVAL}); - - this._ffmpegEndpoint = retRtp.sessionId; - rtpEndpoints[this._voiceBridge] = this._ffmpegEndpoint; - - let recvVideoPort = retRtp.answer.match(/m=video\s(\d*)/)[1]; - this._rtpParams = MediaHandler.generateTranscoderParams(kurentoIp, localIpAddress, - sendVideoPort, recvVideoPort, this._meetingId, "stream_type_video", C.RTP_TO_RTMP, "copy", this.userId, this._voiceBridge); - - this.mcs.on('MediaEvent' + this._ffmpegEndpoint, this.mediaStateRtp.bind(this)); - - Logger.info(LOG_PREFIX, "MCS subscribe for user", this.mcsUserId, "returned", this._ffmpegEndpoint); - - return resolve(presenterSdpAnswer); - } - catch (err) { - Logger.error(LOG_PREFIX, "MCS publish returned error =>", err); - return reject(this._handleError(LOG_PREFIX, err)); - } - finally { - this.mcs.once('ServerState' + this._presenterEndpoint, this.serverState.bind(this)); - } - }); - } - - _startViewer(connectionId, voiceBridge, sdpOffer, userId, presenterEndpoint) { - return new Promise(async (resolve, reject) => { - Logger.info(LOG_PREFIX, "Starting viewer", userId, "for voiceBridge", this._voiceBridge); - let sdpAnswer; - - this._viewersCandidatesQueue[userId] = []; - - try { - const retSource = await this.mcs.subscribe(this.mcsUserId, sharedScreens[voiceBridge], 'WebRtcEndpoint', {descriptor: sdpOffer}); - - this._viewersEndpoint[userId] = retSource.sessionId; - sdpAnswer = retSource.answer; - await this.flushCandidatesQueue(this._viewersEndpoint[userId], this._viewersCandidatesQueue[userId]); - - this.mcs.on('MediaEvent' + this._viewersEndpoint[userId], (event) => { - this.mediaStateWebRtc(event, connectionId); - }); - - Logger.info(LOG_PREFIX, "MCS subscribe returned for user", this.mcsUserId, "returned", this._viewersEndpoint[userId], "at userId", userId); - return resolve(sdpAnswer); - } - catch (err) { - Logger.error(LOG_PREFIX, "MCS publish returned error =>", err); - return reject(this._handleError(LOG_PREFIX, err)); - } - }); - } - - stop () { - return new Promise(async (resolve, reject) => { - try { - if (this._status === C.MEDIA_STOPPED) { - return resolve(); - } - Logger.info('[screnshare] Stopping and releasing endpoints for MCS user', this.mcsUserId); - await this._stopScreensharing(); - this._status = C.MEDIA_STOPPED; - - Logger.info(LOG_PREFIX, "Leaving mcs room"); - await this.mcs.leave(this._meetingId, this.mcsUserId); - delete sharedScreens[this._presenterEndpoint]; - this._candidatesQueue = null; - this._presenterEndpoint = null; - this._ffmpegEndpoint = null; - if (this.isRecorded) { - this.sendStopShareEvent(); - } - return resolve(); - } - catch (err) { - this._handleError(LOG_PREFIX, err); - Logger.error(LOG_PREFIX, 'MCS returned an error when trying to leave =>', err); - return resolve(); - } - }); - } - - _stopScreensharing() { - return new Promise(async (resolve, reject) => { - try { - Logger.info(LOG_PREFIX, "Stopping screensharing with status", this._status); - const isTranscoderAvailable = await this._BigBlueButtonGW.isChannelAvailable(C.TO_BBB_TRANSCODE_SYSTEM_CHAN); - const strm = Messaging.generateStopTranscoderRequestMessage(this._meetingId, this._meetingId); - - - if (isTranscoderAvailable) { - // Interoperability: capturing 1.1 stop_transcoder_reply messages - this._BigBlueButtonGW.once(C.STOP_TRANSCODER_REPLY+this._meetingId, async (payload) => { - const meetingId = payload[C.MEETING_ID]; - await this._stopRtmpBroadcast(meetingId); - return resolve(); - }); - - // Capturing stop transcoder responses from the 2x model - this._BigBlueButtonGW.once(C.STOP_TRANSCODER_RESP_2x+this._meetingId, async (payload) => { - const meetingId = payload[C.MEETING_ID_2x]; - await this._stopRtmpBroadcast(meetingId); - return resolve(); - }); - - this._BigBlueButtonGW.publish(strm, C.TO_BBB_TRANSCODE_SYSTEM_CHAN, function(error) {}); - } - - // Either the transcoder is not available or screensharing couldn't be - // correctly started - if (this._status != C.MEDIA_STARTED || !isTranscoderAvailable) { - this._stopRtmpBroadcast(this._meetingId); - return resolve(); - } - } - catch (err) { - this._handleError(LOG_PREFIX, err); - Logger.error(err); - resolve(); - } - }); - } - - async _onRtpMediaFlowing() { - if (!this._rtmpBroadcastStarted) { - Logger.info(LOG_PREFIX, "RTP Media FLOWING for meeting", this._meetingId); - const isTranscoderAvailable = await this._BigBlueButtonGW.isChannelAvailable(C.TO_BBB_TRANSCODE_SYSTEM_CHAN); - const strm = Messaging.generateStartTranscoderRequestMessage(this._meetingId, this._meetingId, this._rtpParams); - - // Checking if transcoder is avaiable; if so, transposes the stream to RTMP - if (isTranscoderAvailable) { - // Interoperability: capturing 1.1 start_transcoder_reply messages - this._BigBlueButtonGW.once(C.START_TRANSCODER_REPLY+this._meetingId, (payload) => { - let meetingId = payload[C.MEETING_ID]; - let output = payload["params"].output; - this._startRtmpBroadcast(meetingId, output); - }); - - // Capturing stop transcoder responses from the 2x model - this._BigBlueButtonGW.once(C.START_TRANSCODER_RESP_2x+this._meetingId, (payload) => { - let meetingId = payload[C.MEETING_ID_2x]; - let output = payload["params"].output; - this._startRtmpBroadcast(meetingId, output); - }); - - this._BigBlueButtonGW.publish(strm, C.TO_BBB_TRANSCODE_SYSTEM_CHAN, function(error) {}); - } else { - // transcoder is not available, pure WebRTC environment - this._startRtmpBroadcast(this._meetingId); - } - - if (this._status != C.MEDIA_STARTED) { - Logger.info(LOG_PREFIX, 'webRTC started flowing for meeting', this._meetingId); - if (this.isRecorded) { - this.startRecording(); - } - this._status = C.MEDIA_STARTED; - } - } - }; - - _stopRtmpBroadcast (meetingId) { - return new Promise((resolve, reject) => { - Logger.info(LOG_PREFIX, "_stopRtmpBroadcast for meeting", meetingId); - let timestamp = now.format('hhmmss'); - let dsrstom = Messaging.generateScreenshareRTMPBroadcastStoppedEvent2x(this._voiceBridge, - this._voiceBridge, this._streamUrl, this._vw, this._vh, timestamp); - this._BigBlueButtonGW.publish(dsrstom, C.FROM_VOICE_CONF_SYSTEM_CHAN); - resolve(); - }); - } - - _startRtmpBroadcast (meetingId, output) { - Logger.info(LOG_PREFIX, "_startRtmpBroadcast for meeting", + meetingId); - let timestamp = now.format('hhmmss'); - this._streamUrl = MediaHandler.generateStreamUrl(localIpAddress, meetingId, output); - let dsrbstam = Messaging.generateScreenshareRTMPBroadcastStartedEvent2x(this._voiceBridge, - this._voiceBridge, this._streamUrl, this._vw, this._vh, timestamp); - - this._BigBlueButtonGW.publish(dsrbstam, C.FROM_VOICE_CONF_SYSTEM_CHAN, function(error) {}); - this._rtmpBroadcastStarted = true; - } - - _onRtpMediaNotFlowing() { - Logger.warn(LOG_PREFIX, "TODO RTP NOT_FLOWING"); - } - - async stopViewer(id) { - let viewer = this._viewersEndpoint[id]; - Logger.info(LOG_PREFIX, 'Releasing endpoints for', viewer); - - if (viewer) { - try { - await this.mcs.unsubscribe(this.mcsUserId, this.viewer); - this._viewersCandidatesQueue[id] = null; - this._viewersEndpoint[id] = null; - return; - } - catch (err) { - this._handleError(LOG_PREFIX, err); - Logger.error(LOG_PREFIX, 'MCS returned error when trying to unsubscribe', err); - return; - } - } - } -}; diff --git a/labs/bbb-webrtc-sfu/lib/utils/Logger.js b/labs/bbb-webrtc-sfu/lib/utils/Logger.js deleted file mode 100644 index 51f9ccc863..0000000000 --- a/labs/bbb-webrtc-sfu/lib/utils/Logger.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -const Winston = require('winston'); -const Logger = new Winston.Logger(); -const config = require('config'); -const path = require('path'); -Winston.transports.DailyRotateFile = require('winston-daily-rotate-file'); - -const LOG_CONFIG = config.get('log') || {}; -const { level } = LOG_CONFIG; -let filename = LOG_CONFIG.filename; - -Logger.configure({ - levels: { error: 0, warn: 1, info: 2, verbose: 3, debug: 4, trace: 5 }, - colors: { - error: 'red', - warn: 'yellow', - info: 'green', - verbose: 'cyan', - debug: 'magenta', - trace: 'gray' - }, -}); - -Logger.add(Winston.transports.Console, { - timestamp:true, - prettyPrint: false, - humanReadableUnhandledException: true, - colorize: true, - handleExceptions: false, - silent: false, - stringify: (obj) => JSON.stringify(obj), - level, -}); - - -if (filename) { - Logger.add(Winston.transports.DailyRotateFile, { - filename, - prettyPrint: true, - datePattern: '.yyyy-dd-MM', - prepend: false, - stringify: (obj) => JSON.stringify(obj), // single lines - level, - }); -} - -module.exports = Logger; diff --git a/labs/bbb-webrtc-sfu/lib/utils/Utils.js b/labs/bbb-webrtc-sfu/lib/utils/Utils.js deleted file mode 100644 index 9faed84b89..0000000000 --- a/labs/bbb-webrtc-sfu/lib/utils/Utils.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * * @classdesc - * * Utils class for bbb-webrtc-sfu - * * @constructor - * */ - - -/* - * hrTime - * Gets monotonic system time in milliseconds - */ - -exports.hrTime = function () { - let t = process.hrtime(); - - return t[0]*1000 + parseInt(t[1]/1000000); -} diff --git a/labs/bbb-webrtc-sfu/lib/video/VideoManager.js b/labs/bbb-webrtc-sfu/lib/video/VideoManager.js deleted file mode 100755 index b61d2c6f5c..0000000000 --- a/labs/bbb-webrtc-sfu/lib/video/VideoManager.js +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Lucas Fialho Zawacki - * (C) Copyright 2017 Bigbluebutton - * - */ - -'use strict'; - -const BigBlueButtonGW = require('../bbb/pubsub/bbb-gw'); -const Video = require('./video'); -const BaseManager = require('../base/BaseManager'); -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); -const errors = require('../base/errors'); - -module.exports = class VideoManager extends BaseManager { - constructor (connectionChannel, additionalChannels, logPrefix) { - super(connectionChannel, additionalChannels, logPrefix); - this.sfuApp = C.VIDEO_APP; - this.messageFactory(this._onMessage); - } - - _findByIdAndRole (id, role) { - let sesh = null; - let keys = Object.keys(this._sessions); - keys.forEach((sessionId) => { - let session = this._sessions[sessionId]; - if (sessionId === (session.connectionId + id + '-' + role)) { - sesh = session; - } - }); - return sesh; - } - - setStreamAsRecorded (id) { - let video = this._findByIdAndRole(id, 'share'); - - if (video) { - Logger.info("[VideoManager] Setting ", id, " as recorded"); - video.setStreamAsRecorded(); - } else { - Logger.warn("[VideoManager] Tried to set stream to recorded but ", id, " has no session!"); - } - } - - async _onMessage (_message) { - let message = _message; - let connectionId = message.connectionId; - let sessionId; - let video; - let role = message.role? message.role : 'any'; - let cameraId = message.cameraId; - let shared = role === 'share' ? true : false; - let iceQueue; - - Logger.debug(this._logPrefix, 'Received message =>', message); - - if (!message.cameraId && message.id !== 'close') { - Logger.warn(this._logPrefix, 'Ignoring message with undefined.cameraId for session', sessionId); - return; - } - - cameraId += '-' + role; - - sessionId = connectionId + cameraId; - - if (message.cameraId) { - video = this._fetchSession(sessionId); - iceQueue = this._fetchIceQueue(sessionId); - } - - switch (message.id) { - case 'start': - Logger.info(this._logPrefix, 'Received message [' + message.id + '] from connection ' + sessionId); - - if (video) { - await this._stopSession(sessionId); - } - - video = new Video(this._bbbGW, message.meetingId, message.cameraId, shared, message.connectionId); - - this._sessions[sessionId] = video; - - try { - const sdpAnswer = await video.start(message.sdpOffer); - - // Empty ice queue after starting video - this._flushIceQueue(video, iceQueue); - - video.once(C.MEDIA_SERVER_OFFLINE, async (event) => { - const errorMessage = this._handleError(this._logPrefix, connectionId, message.cameraId, role, errors.MEDIA_SERVER_OFFLINE); - this._bbbGW.publish(JSON.stringify({ - ...errorMessage, - }), C.FROM_VIDEO); - }); - - this._bbbGW.publish(JSON.stringify({ - connectionId: connectionId, - type: 'video', - role: role, - id : 'startResponse', - cameraId: message.cameraId, - sdpAnswer : sdpAnswer - }), C.FROM_VIDEO); - } - catch (err) { - const errorMessage = this._handleError(this._logPrefix, connectionId, message.cameraId, role, err); - return this._bbbGW.publish(JSON.stringify({ - ...errorMessage - }), C.FROM_VIDEO); - } - break; - - case 'stop': - this._stopSession(sessionId); - break; - - case 'pause': - if (video) { - video.pause(message.state); - } - break; - - case 'onIceCandidate': - if (video && video.constructor === Video) { - video.onIceCandidate(message.candidate); - } else { - Logger.info(this._logPrefix, "Queueing ice candidate for later in video", cameraId); - iceQueue.push(message.candidate); - } - break; - - case 'close': - Logger.info(this._logPrefix, "Closing sessions of connection", connectionId); - this._killConnectionSessions(connectionId); - break; - - default: - const errorMessage = this._handleError(this._logPrefix, connectionId, null, null, errors.SFU_INVALID_REQUEST); - this._bbbGW.publish(JSON.stringify({ - ...errorMessage, - }), C.FROM_VIDEO); - break; - } - } -} diff --git a/labs/bbb-webrtc-sfu/lib/video/VideoProcess.js b/labs/bbb-webrtc-sfu/lib/video/VideoProcess.js deleted file mode 100644 index 04b7f78b4a..0000000000 --- a/labs/bbb-webrtc-sfu/lib/video/VideoProcess.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -const VideoManager= require('./VideoManager'); -const BaseProcess = require('../base/BaseProcess'); -const C = require('../bbb/messages/Constants'); - -const manager = new VideoManager(C.TO_VIDEO, [C.FROM_AKKA_APPS], C.VIDEO_MANAGER_PREFIX); -const newProcess = new BaseProcess(manager, C.VIDEO_PROCESS_PREFIX); - -newProcess.start(); diff --git a/labs/bbb-webrtc-sfu/lib/video/video.js b/labs/bbb-webrtc-sfu/lib/video/video.js deleted file mode 100644 index 81be7b83e1..0000000000 --- a/labs/bbb-webrtc-sfu/lib/video/video.js +++ /dev/null @@ -1,332 +0,0 @@ -'use strict'; - -const config = require('config'); -const kurentoUrl = config.get('kurentoUrl'); -const MCSApi = require('../mcs-core/lib/media/MCSApiStub'); -const C = require('../bbb/messages/Constants'); -const Logger = require('../utils/Logger'); -const Messaging = require('../bbb/messages/Messaging'); -const h264_sdp = require('../h264-sdp'); -const PREFERRED_H264_PROFILE = config.get('webcam-preferred-h264-profile'); -const BaseProvider = require('../base/BaseProvider'); -const FORCE_H264 = config.get('webcam-force-h264'); -const WEBCAM_TARGET_BITRATE = config.get('webcam-target-bitrate'); -const SHOULD_RECORD = config.get('recordWebcams'); -const LOG_PREFIX = "[video]"; - -let sharedWebcams = {}; - -module.exports = class Video extends BaseProvider { - constructor(_bbbGW, _meetingId, _id, _shared, _connectionId) { - super(); - this.sfuApp = C.VIDEO_APP; - this.mcs = new MCSApi(); - this.bbbGW = _bbbGW; - this.id = _id; - this.connectionId = _connectionId; - this.meetingId = _meetingId; - this.shared = _shared; - this.role = this.shared? 'share' : 'viewer' - this.streamName = this.connectionId + this.id + "-" + this.role; - this.mediaId = null; - this.iceQueue = null; - this.status = C.MEDIA_STOPPED; - this.recording = {}; - this.isRecorded = false; - this._recordingSubPath = 'recordings'; - this._cameraProfile = 'medium'; - - this.candidatesQueue = []; - this.notFlowingTimeout = null; - - this.bbbGW.once(C.RECORDING_STATUS_REPLY_MESSAGE_2x+this.meetingId, (payload) => { - Logger.info(LOG_PREFIX, "RecordingStatusReply userId:", payload.requestedBy, "recorded:", payload.recorded); - - if (payload.requestedBy === this.id && payload.recorded) { - this.isRecorded = true; - } - }); - } - - _randomTimeout (low, high) { - return parseInt(Math.random() * (high - low) + low); - } - - async onIceCandidate (_candidate) { - if (this.mediaId) { - try { - await this.flushCandidatesQueue(); - await this.mcs.addIceCandidate(this.mediaId, _candidate); - } - catch (err) { - this._handleError(LOG_PREFIX, err, this.role, this.id); - Logger.error(LOG_PREFIX, "ICE candidate could not be added to media controller.", err); - } - } - else { - this.candidatesQueue.push(_candidate); - } - }; - - async flushCandidatesQueue () { - return new Promise((resolve, reject) => { - if (this.mediaId) { - let iceProcedures = this.candidatesQueue.map((candidate) => { - this.mcs.addIceCandidate(this.mediaId, candidate); - }); - - return Promise.all(iceProcedures).then(() => { - this.candidatesQueue = []; - resolve(); - }).catch((err) => { - Logger.error(LOG_PREFIX, "ICE candidate could not be added to media controller.", err); - reject(this._handleError(LOG_PREFIX, err, this.role, this.id)); - }); - } - }); - } - - mediaState (event) { - let msEvent = event.event; - - switch (event.eventTag) { - - case "OnIceCandidate": - let candidate = msEvent.candidate; - Logger.debug(LOG_PREFIX, "Sending ICE candidate to user", this.streamName, "with candidate", candidate); - this.bbbGW.publish(JSON.stringify({ - connectionId: this.connectionId, - type: 'video', - role: this.role, - id : 'iceCandidate', - cameraId: this.id, - candidate: candidate - }), C.FROM_VIDEO); - break; - - case "MediaStateChanged": - break; - - case "MediaFlowOutStateChange": - case "MediaFlowInStateChange": - Logger.info(LOG_PREFIX, ' ' + msEvent.type + '[' + msEvent.state + ']' + ' for media session', event.id, "for video", this.streamName); - - if (msEvent.state === 'NOT_FLOWING' && this.status !== C.MEDIA_PAUSED) { - Logger.warn(LOG_PREFIX, "Setting up a timeout for", this.streamName); - if (!this.notFlowingTimeout) { - this.notFlowingTimeout = setTimeout(() => { - - if (this.shared) { - this.sendPlayStop(); - this.status = C.MEDIA_STOPPED; - clearTimeout(this.notFlowingTimeout); - delete this.notFlowingTimeout; - } - }, config.get('mediaFlowTimeoutDuration') + this._randomTimeout(-2000, 2000)); - } - } - else if (msEvent.state === 'FLOWING') { - if (this.notFlowingTimeout) { - Logger.warn(LOG_PREFIX, "Received a media flow before stopping", this.streamName); - clearTimeout(this.notFlowingTimeout); - delete this.notFlowingTimeout; - } - if (this.status !== C.MEDIA_STARTED) { - - // Record the video stream if it's the original being shared - if (this.shouldRecord()) { - this.startRecording(); - } - - this.sendPlayStart(); - - this.status = C.MEDIA_STARTED; - } - - } - break; - - default: Logger.warn(LOG_PREFIX, "Unrecognized event", event); - } - } - - sendPlayStart () { - this.bbbGW.publish(JSON.stringify({ - connectionId: this.connectionId, - type: 'video', - role: this.role, - id : 'playStart', - cameraId: this.id, - }), C.FROM_VIDEO); - } - - sendPlayStop () { - let userCamEvent = - Messaging.generateUserCamBroadcastStoppedEventMessage2x(this.meetingId, this.id, this.id); - this.bbbGW.publish(userCamEvent, function(error) {}); - - this.bbbGW.publish(JSON.stringify({ - connectionId: this.connectionId, - type: 'video', - role: this.role, - id : 'playStop', - cameraId: this.id, - }), C.FROM_VIDEO); - } - - sendGetRecordingStatusRequestMessage() { - let req = Messaging.generateRecordingStatusRequestMessage(this.meetingId, this.id); - - this.bbbGW.publish(req, C.TO_AKKA_APPS); - } - - shouldRecord () { - return this.isRecorded && this.shared; - } - - async startRecording() { - return new Promise(async (resolve, reject) => { - try { - const recordingName = this._cameraProfile + '-' + this.id; - const recordingPath = this.getRecordingPath(this.meetingId, this._recordingSubPath, recordingName); - this.recording = await this.mcs.startRecording(this.userId, this.mediaId, recordingPath); - this.mcs.on('MediaEvent' + this.recording.recordingId, this.recordingState.bind(this)); - this.sendStartShareEvent(); - resolve(this.recording); - } - catch (err) { - Logger.error(LOG_PREFIX, "Error on start recording with message", err); - reject(this._handleError(LOG_PREFIX, err, this.role, this.id)); - } - }); - } - - async stopRecording() { - await this.mcs.stopRecording(this.userId, this.mediaId, this.recording.recordingId); - this.sendStopShareEvent(); - this.recording = {}; - } - - recordingState(event) { - const msEvent = event.event; - Logger.info('[Recording]', msEvent.type, '[', msEvent.state, ']', 'for recording session', event.id, 'for video', this.streamName); - } - - start (sdpOffer) { - return new Promise(async (resolve, reject) => { - Logger.info(LOG_PREFIX, "Starting video instance for", this.streamName); - - // Force H264 - if (FORCE_H264) { - sdpOffer = h264_sdp.transform(sdpOffer, PREFERRED_H264_PROFILE); - } - - // Start the recording process - if (SHOULD_RECORD && this.shared) { - this.sendGetRecordingStatusRequestMessage(); - } - - try { - this.userId = await this.mcs.join(this.meetingId, 'SFU', {}); - Logger.info(LOG_PREFIX, "MCS join for", this.streamName, "returned", this.userId); - const sdpAnswer = await this._addMCSMedia(C.WEBRTC, sdpOffer); - this.mcs.on('MediaEvent' + this.mediaId, this.mediaState.bind(this)); - this.mcs.on('ServerState' + this.mediaId, this.serverState.bind(this)); - this.status = C.MEDIA_STARTING; - await this.flushCandidatesQueue(); - Logger.info(LOG_PREFIX, "MCS call for user", this.userId, "returned", this.mediaId); - return resolve(sdpAnswer); - } - catch (err) { - reject(this._handleError(LOG_PREFIX, err, this.role, this.id)); - } - }); - } - - _addMCSMedia (type, descriptor) { - return new Promise(async (resolve, reject) => { - try { - if (this.shared) { - let { answer, sessionId } = await this.mcs.publish(this.userId, this.meetingId, type, { descriptor, targetBitrate: WEBCAM_TARGET_BITRATE }); - this.mediaId = sessionId; - sharedWebcams[this.id] = this.mediaId; - return resolve(answer); - } - else if (sharedWebcams[this.id]) { - let { answer, sessionId } = await this.mcs.subscribe(this.userId, sharedWebcams[this.id], C.WEBRTC, { descriptor }); - this.mediaId = sessionId; - return resolve(answer); - } - } - catch (err) { - err = this._handleError(LOG_PREFIX, err, this.role, this.id) - reject(err); - } - }); - } - - async pause (state) { - const sourceId = sharedWebcams[this.id]; - const sinkId = this.mediaId; - - if (sourceId == null || sinkId == null) { - Logger.error(LOG_PREFIX, "Source or sink is null."); - return; - } - - // We want to pause the stream - try { - if (state && (this.status !== C.MEDIA_STARTING || this.status !== C.MEDIA_PAUSED)) { - await this.mcs.disconnect(sourceId, sinkId, 'VIDEO'); - this.status = C.MEDIA_PAUSED; - } - else if (!state && this.status === C.MEDIA_PAUSED) { //un-pause - await this.mcs.connect(sourceId, sinkId, 'VIDEO'); - this.status = C.MEDIA_STARTED; - } - } - catch (err) { - this._handleError(LOG_PREFIX, err, this.role, this.id); - } - } - - sendStartShareEvent() { - let shareCamEvent = Messaging.generateWebRTCShareEvent('StartWebRTCShareEvent', this.meetingId, this.recording.filename); - this.bbbGW.writeMeetingKey(this.meetingId, shareCamEvent, function(error) {}); - } - - sendStopShareEvent () { - let stopShareEvent = - Messaging.generateWebRTCShareEvent('StopWebRTCShareEvent', this.meetingId, this.recording.filename); - this.bbbGW.writeMeetingKey(this.meetingId, stopShareEvent, function(error) {}); - } - - async stop () { - return new Promise(async (resolve, reject) => { - Logger.info(LOG_PREFIX, 'Stopping video session', this.userId, 'at room', this.meetingId); - - try { - await this.mcs.leave(this.meetingId, this.userId); - - if (this.shouldRecord()) { - this.sendStopShareEvent(); - } - - if (this.shared) { - delete sharedWebcams[this.id]; - } - - if (this.notFlowingTimeout) { - clearTimeout(this.notFlowingTimeout); - delete this.notFlowingTimeout; - } - - delete this._candidatesQueue; - resolve(); - } - catch (err) { - reject(this._handleError(LOG_PREFIX, err, this.role, this.id)); - } - }); - } -}; diff --git a/labs/bbb-webrtc-sfu/package-lock.json b/labs/bbb-webrtc-sfu/package-lock.json deleted file mode 100644 index 667d138dcc..0000000000 --- a/labs/bbb-webrtc-sfu/package-lock.json +++ /dev/null @@ -1,443 +0,0 @@ -{ - "name": "bbb-webrtc-sfu", - "version": "0.0.5", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "1.0.3" - } - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "async": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.0.1.tgz", - "integrity": "sha1-twnMAoCpw28J9FNr6CPIOKkEniU=", - "requires": { - "lodash": "4.17.10" - } - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" - }, - "backoff": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.3.0.tgz", - "integrity": "sha1-7nx+OAk/kuRyhZ22NedlJFT8Ieo=" - }, - "bindings": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", - "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" - }, - "bufferutil": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.2.1.tgz", - "integrity": "sha1-N75dNuHgZJIiHmjUdLGsWOUQy9c=", - "requires": { - "bindings": "1.2.1", - "nan": "2.10.0" - } - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" - }, - "commander": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz", - "integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=" - }, - "config": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/config/-/config-1.30.0.tgz", - "integrity": "sha1-HWCp81NIoTwXV5jThOgaWhbDum4=", - "requires": { - "json5": "0.4.0", - "os-homedir": "1.0.2" - } - }, - "cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" - }, - "double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" - }, - "error-tojson": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/error-tojson/-/error-tojson-0.0.1.tgz", - "integrity": "sha1-p7GqlP/ADpB4wuuibiBL2Hzyy7k=" - }, - "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==" - }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "isbuffer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/isbuffer/-/isbuffer-0.0.0.tgz", - "integrity": "sha1-OMFG2d9Si4v5sHAcPUPPEt8/w5s=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "js-yaml": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", - "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", - "requires": { - "argparse": "1.0.10", - "esprima": "4.0.0" - } - }, - "json5": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz", - "integrity": "sha1-BUNS5MTIDIbAkjh31EneF2pzLI0=" - }, - "kurento-client": { - "version": "git+https://github.com/mconf/kurento-client-js.git#09986b21db44fdc015706d7b01bece1728094d4c", - "requires": { - "async": "2.0.1", - "error-tojson": "0.0.1", - "es6-promise": "4.2.4", - "extend": "3.0.1", - "inherits": "2.0.3", - "kurento-client-core": "git+https://github.com/mconf/kurento-client-core-js.git#3bfcff9cb21430a4f451239100b4c306b9705757", - "kurento-client-elements": "git+https://github.com/mconf/kurento-client-elements-js.git#8db04d5a31c299c9c6bacdfbc8fb358ad1c80fbb", - "kurento-client-filters": "github:Kurento/kurento-client-filters-js#67d43d4fca03c6002015448973c3e6d82e14cb3c", - "kurento-jsonrpc": "github:Kurento/kurento-jsonrpc-js#1cdb36884ca8f096d21c335f28129a5449214e7b", - "minimist": "1.2.0", - "promise": "7.1.1", - "promisecallback": "0.0.4", - "reconnect-ws": "github:KurentoForks/reconnect-ws#f287385d75861654528c352e60221f95c9209f8a" - }, - "dependencies": { - "kurento-client-core": { - "version": "git+https://github.com/mconf/kurento-client-core-js.git#3bfcff9cb21430a4f451239100b4c306b9705757" - }, - "kurento-client-elements": { - "version": "git+https://github.com/mconf/kurento-client-elements-js.git#8db04d5a31c299c9c6bacdfbc8fb358ad1c80fbb" - }, - "kurento-client-filters": { - "version": "github:Kurento/kurento-client-filters-js#67d43d4fca03c6002015448973c3e6d82e14cb3c" - }, - "kurento-jsonrpc": { - "version": "github:Kurento/kurento-jsonrpc-js#1cdb36884ca8f096d21c335f28129a5449214e7b", - "requires": { - "bufferutil": "1.2.1", - "inherits": "2.0.3", - "utf-8-validate": "1.2.2", - "ws": "1.1.5" - } - }, - "reconnect-core": { - "version": "github:KurentoForks/reconnect-core#921d43e91578abb2fb2613f585c010c1939cf734", - "requires": { - "backoff": "2.3.0" - } - }, - "reconnect-ws": { - "version": "github:KurentoForks/reconnect-ws#f287385d75861654528c352e60221f95c9209f8a", - "requires": { - "reconnect-core": "github:KurentoForks/reconnect-core#921d43e91578abb2fb2613f585c010c1939cf734", - "websocket-stream": "0.5.1" - } - }, - "ws": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", - "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", - "requires": { - "options": "0.0.6", - "ultron": "1.0.2" - } - } - } - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } - } - }, - "moment": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", - "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" - }, - "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" - }, - "nanoid": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.0.1.tgz", - "integrity": "sha512-BXapDjcA0QjUPLBqcC5EcvwB8LMOY7zXf3UqDGp93R73PoMOfipmRVxpTogAhtqViE/pJzP9bS+jGK31rzkE2w==" - }, - "options": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", - "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=" - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "promiscuous": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/promiscuous/-/promiscuous-0.6.0.tgz", - "integrity": "sha1-VAFM09Ysr+gx4zVJkMBf9beMiJI=", - "optional": true - }, - "promise": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", - "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=", - "requires": { - "asap": "2.0.6" - } - }, - "promisecallback": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/promisecallback/-/promisecallback-0.0.4.tgz", - "integrity": "sha1-uTTxPATkQ2IrTWbeTkLqX2zmbnQ=" - }, - "readable-id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/readable-id/-/readable-id-1.0.0.tgz", - "integrity": "sha512-RXwuv4YQNYByl+PpQVA4PR4H+aGTWOtpRx56LrxJ2Ypxf96u9WaIdKGOCqu7MPBt8iqQkYPQUk4KGbL55n78YQ==", - "requires": { - "nanoid": "1.0.1" - } - }, - "redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", - "requires": { - "double-ended-queue": "2.1.0-0", - "redis-commands": "1.3.1", - "redis-parser": "2.6.0" - } - }, - "redis-commands": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz", - "integrity": "sha1-gdgm9F+pyLIBH0zXoP5ZfSQdRCs=" - }, - "redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "sdp-transform": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.4.1.tgz", - "integrity": "sha512-dCReSJ6NtCW6goKkKBEHcIknBS1pKejnMw+S3p3jl0PALPFrbjbreCyQ+CABtFxl2GXD2/05+C2q2gZP23dUWQ==" - }, - "sip.js": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/sip.js/-/sip.js-0.7.5.tgz", - "integrity": "sha1-hqznBRWU+RtFUb24EgoWxEli06I=", - "requires": { - "promiscuous": "0.6.0", - "ws": "0.6.5" - }, - "dependencies": { - "nan": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/nan/-/nan-1.4.3.tgz", - "integrity": "sha1-xWtUBGmAY2lvWXQ1+RY8MSrqUAk=" - }, - "ws": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-0.6.5.tgz", - "integrity": "sha1-8AhEAByjk7ADaB/zKDjnKlYNr9Q=", - "requires": { - "nan": "1.4.3", - "options": "0.0.6", - "ultron": "1.0.2" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "tinycolor": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tinycolor/-/tinycolor-0.0.1.tgz", - "integrity": "sha1-MgtaUtg6u1l42Bo+iH1K77FaYWQ=" - }, - "ultron": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", - "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=" - }, - "utf-8-validate": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.2.2.tgz", - "integrity": "sha1-i7hxpHQeCFxwSHynrNvX1tNgKes=", - "requires": { - "bindings": "1.2.1", - "nan": "2.4.0" - }, - "dependencies": { - "nan": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.4.0.tgz", - "integrity": "sha1-+zxZ1F/k7/4hXwuJD4rfbrMtIjI=" - } - } - }, - "websocket-stream": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-0.5.1.tgz", - "integrity": "sha1-YizR8FZvuEzgpNb4VFJvPcTXDkg=", - "requires": { - "isbuffer": "0.0.0", - "through": "2.3.8", - "ws": "0.4.32" - }, - "dependencies": { - "nan": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-1.0.0.tgz", - "integrity": "sha1-riT4hQgY1mL8q1rPfzuVv6oszzg=" - }, - "ws": { - "version": "0.4.32", - "resolved": "https://registry.npmjs.org/ws/-/ws-0.4.32.tgz", - "integrity": "sha1-eHphVEFPPJntg8V3IVOyD+sM7DI=", - "requires": { - "commander": "2.1.0", - "nan": "1.0.0", - "options": "0.0.6", - "tinycolor": "0.0.1" - } - } - } - }, - "winston": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.2.tgz", - "integrity": "sha512-4S/Ad4ZfSNl8OccCLxnJmNISWcm2joa6Q0YGDxlxMzH0fgSwWsjMt+SmlNwCqdpaPg3ev1HKkMBsIiXeSUwpbA==", - "requires": { - "async": "1.0.0", - "colors": "1.0.3", - "cycle": "1.0.3", - "eyes": "0.1.8", - "isstream": "0.1.2", - "stack-trace": "0.0.10" - }, - "dependencies": { - "async": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" - } - } - }, - "winston-daily-rotate-file": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-1.7.2.tgz", - "integrity": "sha1-ZQK/opeCT9mC2l5WR8dThXjS+aA=", - "requires": { - "mkdirp": "0.5.1" - } - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.2", - "ultron": "1.1.1" - }, - "dependencies": { - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - } - } - } - } -} diff --git a/labs/bbb-webrtc-sfu/package.json b/labs/bbb-webrtc-sfu/package.json deleted file mode 100644 index 18d8b264ae..0000000000 --- a/labs/bbb-webrtc-sfu/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "bbb-webrtc-sfu", - "version": "0.0.5", - "private": true, - "scripts": { - "start": "node server.js" - }, - "dependencies": { - "config": "^1.30.0", - "js-yaml": "^3.11.0", - "kurento-client": "git+https://github.com/mconf/kurento-client-js.git#mconf", - "moment": "^2.22.1", - "readable-id": "^1.0.0", - "redis": "^2.8.0", - "sdp-transform": "^2.4.1", - "winston": "^2.4.2", - "winston-daily-rotate-file": "^1.7.2", - "ws": "^3.3.3", - "sip.js": "0.7.5" - }, - "optionalDependencies": {} -} diff --git a/labs/bbb-webrtc-sfu/server.js b/labs/bbb-webrtc-sfu/server.js deleted file mode 100755 index 0e04fad4c4..0000000000 --- a/labs/bbb-webrtc-sfu/server.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Lucas Fialho Zawacki - * Paulo Renato Lanzarin - * (C) Copyright 2017 Bigbluebutton - * - */ - -'use strict'; - -const ConnectionManager = require('./lib/connection-manager/ConnectionManager'); -const HttpServer = require('./lib/connection-manager/HttpServer'); -const server = new HttpServer(); -const WebsocketConnectionManager = require('./lib/connection-manager/WebsocketConnectionManager'); -const Logger = require('./lib/utils/Logger'); -const ProcessManager = require('./lib/ProcessManager.js'); -const PM = new ProcessManager(); - -PM.start(); - -const CM = new ConnectionManager(PM.screenshareProcess, PM.videoProcess); - -let websocketManager = new WebsocketConnectionManager(server.getServerObject(), '/bbb-webrtc-sfu'); - -CM.setHttpServer(server); -CM.addAdapter(websocketManager); - -CM.listen(() => { - Logger.info("[MainProcess] Server started"); -}); -- GitLab