Cleaning up deprecated kurento video and screensharing apps

This commit is contained in:
prlanzarin 2017-11-11 01:40:11 +00:00
parent 4dc8085648
commit f048d30ad6
41 changed files with 0 additions and 2184 deletions

View File

@ -1 +0,0 @@
node_modules/

View File

@ -1,2 +0,0 @@
kurentoUrl: "wss://HOST/kurento"
acceptSelfSignedCertificate: false

View File

@ -1,2 +0,0 @@
This folder contains a dummy self-signed certificate only for demo purposses,
**DON'T USE IT IN PRODUCTION**.

View File

@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCuf5QfyX2oDDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE0MDkyOTA5NDczNVoXDTE1MDkyOTA5NDczNVowRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMJOyOHJ+rJWJEQ7P7kKoWa31ff7hKNZxF6sYE5lFi3pBYWIY6kTN/iUaxJLROFo
FhoC/M/STY76rIryix474v/6cRoG8N+GQBEn4IAP1UitWzVO6pVvBaIt5IKlhhfm
YA1IMweCd03vLcaHTddNmFDBTks7QDwfenTaR5VjKYc3OtEhcG8dgLAnOjbbk2Hr
8wter2IeNgkhya3zyoXnTLT8m8IMg2mQaJs62Xlo9gs56urvVDWG4rhdGybj1uwU
ZiDYyP4CFCUHS6UVt12vADP8vjbwmss2ScGsIf0NjaU+MpSdEbB82z4b2NiN8Wq+
rFA/JbvyeoWWHMoa7wkVs1MCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAYLRwV9fo
AOhJfeK199Tv6oXoNSSSe10pVLnYxPcczCVQ4b9SomKFJFbmwtPVGi6w3m+8mV7F
9I2WKyeBHzmzfW2utZNupVybxgzEjuFLOVytSPdsB+DcJomOi8W/Cf2Vk8Wykb/t
Ctr1gfOcI8rwEGKxm279spBs0u1snzoLyoimbMbiXbC82j1IiN3Jus08U07m/j7N
hRBCpeHjUHT3CRpvYyTRnt+AyBd8BiyJB7nWmcNI1DksXPfehd62MAFS9e1ZE+dH
Aavg/U8VpS7pcCQcPJvIJ2hehrt8L6kUk3YUYqZ0OeRZK27f2R5+wFlDF33esm3N
dCSsLJlXyqAQFg==
-----END CERTIFICATE-----

View File

@ -1,16 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMJOyOHJ+rJWJEQ7P7kKoWa31ff7hKNZxF6sYE5l
Fi3pBYWIY6kTN/iUaxJLROFoFhoC/M/STY76rIryix474v/6cRoG8N+GQBEn4IAP
1UitWzVO6pVvBaIt5IKlhhfmYA1IMweCd03vLcaHTddNmFDBTks7QDwfenTaR5Vj
KYc3OtEhcG8dgLAnOjbbk2Hr8wter2IeNgkhya3zyoXnTLT8m8IMg2mQaJs62Xlo
9gs56urvVDWG4rhdGybj1uwUZiDYyP4CFCUHS6UVt12vADP8vjbwmss2ScGsIf0N
jaU+MpSdEbB82z4b2NiN8Wq+rFA/JbvyeoWWHMoa7wkVs1MCAwEAAaAAMA0GCSqG
SIb3DQEBCwUAA4IBAQBMszYHMpklgTF/3h1zAzKXUD9NrtZp8eWhL06nwVjQX8Ai
EaCUiW0ypstokWcH9+30chd2OD++67NbxYUEucH8HrKpOoy6gs5L/mqgQ9Npz3OT
TB1HI4kGtpVuUQ5D7L0596tKzMX/CgW/hRcHWl+PDkwGhQs1qZcJ8QN+YP6AkRrO
5sDdDB/BLrB9PtBQbPrYIQcHQ7ooYWz/G+goqRxzZ6rt0aU2uAB6l7c82ADLAqFJ
qlw+xqVzEETVfqM5TXKK/wV3hgm4oSX5Q4SHLKF94ODOkWcnV4nfIKz7y+5XcQ3p
PrGimI1br07okC5rO9cgLCR0Ks20PPFcM0FvInW/
-----END CERTIFICATE REQUEST-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAwk7I4cn6slYkRDs/uQqhZrfV9/uEo1nEXqxgTmUWLekFhYhj
qRM3+JRrEktE4WgWGgL8z9JNjvqsivKLHjvi//pxGgbw34ZAESfggA/VSK1bNU7q
lW8Foi3kgqWGF+ZgDUgzB4J3Te8txodN102YUMFOSztAPB96dNpHlWMphzc60SFw
bx2AsCc6NtuTYevzC16vYh42CSHJrfPKhedMtPybwgyDaZBomzrZeWj2Cznq6u9U
NYbiuF0bJuPW7BRmINjI/gIUJQdLpRW3Xa8AM/y+NvCayzZJwawh/Q2NpT4ylJ0R
sHzbPhvY2I3xar6sUD8lu/J6hZYcyhrvCRWzUwIDAQABAoIBACwt56TW3MZxqZtN
8WYsUZheUispJ/ZQMcLo5JjOiSV1Jwk+gpJtyTse291z+bxagzP02/CQu4u32UVa
cmE0cp+LHO4zB8964dREwdm8P91fdS6Au/uwG5LNZniCFCQZAFvkv52Ef4XbzQen
uf4rKWerHBck6K0C5z/sZXxE6KtScE2ZLUmkhO0nkHM6MA6gFk2OMnB+oDTOWWPt
1mlreQlzuMYG/D4axviRYrOSYCE5Qu1SOw/DEOLQqqeBjQrKtAyOlFHZsIR6lBfe
KHMChPUcYIwaowt2DcqH/A+AFXRtaifa6DvH8Yul+2vAp47UEpaenVfM5bpN33XV
EzerjtECgYEA+xiXzblek67iQgRpc9eHSoqs4iRLhae8s8kpAG51Jz46Je+Dmium
XV769oiUGUxBeoUb7ryW+4MOzHJaA1BfGejQSvwLIB9e4cnikqnAArcqbcAcOCL1
aYYDiSmSmN/AokNZlPKEBFXP9bzXrU9smQJWNTHlcRl7JXfnwF+jwNsCgYEAxhpE
SBr9vlUVHNh/S6C5i80NIYg6jCy2FgsmuzEqmcqV0pTyzegmq8bru+QmuvoUj2o4
nVv4J9d1fLF6ECUVk9aK8UdJOOB6hAfurOdJCArgrsY/9t4uDzXfbPCdfSNQITE0
XgeNGQX1EzvwwkBmyZKk0kLIr3syP8ZCWfXDROkCgYBR+dF1pJMv++R6UR5sZ20P
9P5ERj0xwXVl7MKqFWXCDhrFz9BTQPTrftrIKgbPy4mFCnf4FTHlov/t11dzxYWG
2+9Ey8yGDDfZ1yNVZn39ZPdBJXsRCLi+XrZAzYXCyyoEz6ArdJGNKMbgH2r6dfeq
bIzgiQ2zQvJlZSQQNiksCQKBgCgwzAmU8EXdHRttEOZXBU3HnBJhgP9PUuHGAWWY
4/uvjhXbAiekIbRX9xt3fiQQ+HrgIfxK3F246K0TlKAR5f7IWAf7Xm+bmz+OHG4X
vklTa6IJtpBvIwkS9PE1H75zm54gTW+GOKoK+12bm4zNZA0hIy9FPVHcvKUTpAJ8
SdGBAoGAHLtJnB1NO4EgO6WtLQMXt7HrIbup8eZi8/82gC3422C+ooKIrYQ07qSw
nBOO/G0OB4yd6vCE2x5+TWSSCYGgG5A8aIv5qP76RP4hovGHxG/y2tfotw5UuOrh
nFWlTP4Urs8PeykvK9ao8r/T8BnPIC16U6ENYvAc0mRlFA2j1GA=
-----END RSA PRIVATE KEY-----

View File

@ -1,253 +0,0 @@
// Global stuff
var mediaPipelines = {};
var sharedWebcams = {};
// TODO Later
// var loadBalancer = require('')
const kurento = require('kurento-client');
const config = require('config');
const kurentoUrl = config.get('kurentoUrl');
const EventEmitter = require('events').EventEmitter;
const inherits = require('util').inherits;
if (config.get('acceptSelfSignedCertificate')) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED=0;
}
var kurentoClient = null;
function getKurentoClient(callback) {
if (kurentoClient !== null) {
return callback(null, kurentoClient);
}
kurento(kurentoUrl, function(error, _kurentoClient) {
if (error) {
console.log("Could not find media server at address " + kurentoUrl);
return callback("Could not find media server at address" + kurentoUrl + ". Exiting with error " + error);
}
console.log(" [server] Initiating kurento client. Connecting to: " + kurentoUrl);
kurentoClient = _kurentoClient;
callback(null, kurentoClient);
});
}
function getMediaPipeline(id, callback) {
console.log(' [media] Creating media pipeline for ' + id);
if (mediaPipelines[id]) {
console.log(' [media] Pipeline already exists.');
callback(null, mediaPipelines[id]);
} else {
kurentoClient.create('MediaPipeline', function(err, pipeline) {
mediaPipelines[id] = pipeline;
return callback(err, pipeline);
});
}
}
function Video(_ws, _id, _shared) {
var ws = _ws;
var id = _id;
var shared = _shared;
var webRtcEndpoint = null;
var notFlowingTimeout = null;
var notFlowingTimer = 15000;
EventEmitter.call(this);
var candidatesQueue = [];
this.onIceCandidate = function(_candidate) {
var candidate = kurento.getComplexType('IceCandidate')(_candidate);
if (webRtcEndpoint) {
webRtcEndpoint.addIceCandidate(candidate);
}
else {
candidatesQueue.push(candidate);
}
};
this.start = function(sdpOffer, callback) {
var self = this;
getKurentoClient(function(error, kurentoClient) {
if (error) {
return callback(error);
}
getMediaPipeline(id, function(error, pipeline) {
if (error) {
return callback(error);
}
createMediaElements(pipeline, function(error, _webRtcEndpoint) {
if (error) {
pipeline.release();
return callback(error);
}
while(candidatesQueue.length) {
var candidate = candidatesQueue.shift();
_webRtcEndpoint.addIceCandidate(candidate);
}
var flowInOut = function(event) {
console.log(' [=] ' + event.type + ' for endpoint ' + id);
if (event.state === 'NOT_FLOWING' && event.type === 'MediaFlowInStateChange') {
console.log(" [-] Media not flowing ");
notFlowingTimeout = setTimeout(function() {
console.log(" Timeout! sending playStop for id " + id);
ws.sendMessage({ id : 'playStop', cameraId : id });
}, notFlowingTimer);
} else if (event.state === 'FLOWING' && event.type === 'MediaFlowInStateChange') {
console.log(" [o] Media flowing ");
self.emit("READY");
if (notFlowingTimeout) {
clearTimeout(notFlowingTimeout);
notFlowingTimeout = null;
} else{
ws.sendMessage({ id : 'playStart', cameraId : id });
}
}
};
_webRtcEndpoint.on('MediaFlowInStateChange', flowInOut);
_webRtcEndpoint.on('MediaFlowOutStateChange', flowInOut);
_webRtcEndpoint.on('MediaStateChanged', (e) => { self.emit("READY"); console.log(id); console.log(e)} );
connectMediaElements(_webRtcEndpoint, function(error) {
if (error) {
pipeline.release();
return callback(error);
}
// It's a user sharing a webcam
if (shared) {
sharedWebcams[id] = _webRtcEndpoint;
}
// Store our endpoint
webRtcEndpoint = _webRtcEndpoint;
_webRtcEndpoint.on('OnIceCandidate', function(event) {
var candidate = kurento.getComplexType('IceCandidate')(event.candidate);
ws.sendMessage({ id : 'iceCandidate', cameraId: id, candidate : candidate });
});
_webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) {
if (error) {
pipeline.release();
return callback(error);
}
return callback(null, sdpAnswer);
});
_webRtcEndpoint.gatherCandidates(function(error) {
if (error) {
return callback(error);
}
});
});
});
});
});
};
var createMediaElements = function(pipeline, callback) {
console.log(" [webrtc] Creating webrtc endpoint");
pipeline.create('WebRtcEndpoint', function(error, _webRtcEndpoint) {
if (error) {
return callback(error);
}
webRtcEndpoint = _webRtcEndpoint;
console.log(" [webrtc] Created webRtcEndpoint => " + webRtcEndpoint.id);
return callback(null, _webRtcEndpoint);
});
};
var connectMediaElements = function(webRtcEndpoint, callback) {
// User is sharing webcam (sendOnly connection from the client)
if (shared) {
console.log(" [webrtc] User has shared the webcam, no connection needed");
// Dont connect this, just create the webrtcEndpoint
// webRtcEndpoint.connect(webRtcEndpoint, callback);
return callback(null);
} else {
console.log(" [webrtc] User wants to receive webcam ");
if (sharedWebcams[id]) {
var wRtc = sharedWebcams[id];
wRtc.connect(webRtcEndpoint, function(error) {
console.log(" [webrtc] Connected " + wRtc.id + " => " + webRtcEndpoint.id);
if (error) {
return callback(error);
}
return callback(null);
});
}
}
};
this.stop = function() {
console.log(' [stop] Releasing webrtc endpoint for ' + id);
if (webRtcEndpoint) {
webRtcEndpoint.release();
webRtcEndpoint = null;
} else {
console.log(" [webRtcEndpoint] PLEASE DONT TRY STOPPING THINGS TWICE");
}
if (shared) {
console.log(' [stop] Webcam is shared, releasing ' + id);
if (mediaPipelines[id]) {
console.log( '[stop] Releasing pipeline ' + id);
mediaPipelines[id].release();
} else {
console.log(" [mediaPipeline] PLEASE DONT TRY STOPPING THINGS TWICE");
}
delete mediaPipelines[id];
delete sharedWebcams[id];
}
delete candidatesQueue;
};
return this;
};
inherits(Video, EventEmitter);
module.exports = Video;

View File

@ -1,18 +0,0 @@
/*
* Simple wrapper around the ws library
*
*/
var ws = require('ws');
ws.prototype.sendMessage = function(json) {
return this.send(JSON.stringify(json), function(error) {
if(error)
console.log(' [server] Websocket error "' + error + '" on message "' + json.id + '"');
});
};
module.exports = ws;

View File

@ -1,20 +0,0 @@
{
"name": "bbb-html5-video-kurento-bridge",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "nodejs server.js",
"postinstall": "npm start"
},
"dependencies": {
"cookie-parser": "^1.3.5",
"express": "~4.12.4",
"express-session": "~1.10.3",
"ws": "~1.0.1",
"kurento-client": "6.6.0"
},
"devDependencies": {
"config": "^1.26.1",
"js-yaml": "^3.8.3"
}
}

View File

@ -1,194 +0,0 @@
/*
* Lucas Fialho Zawacki
* (C) Copyright 2017 Bigbluebutton
*
*/
'use strict';
var cookieParser = require('cookie-parser')
var express = require('express');
var session = require('express-session')
var ws = require('./lib/websocket');
var http = require('http');
var fs = require('fs');
var Video = require('./lib/video');
// Global variables
var app = express();
var sessions = {};
/*
* Management of sessions
*/
app.use(cookieParser());
var sessionHandler = session({
secret : 'Shawarma', rolling : true, resave : true, saveUninitialized : true
});
app.use(sessionHandler);
/*
* Server startup
*/
var server = http.createServer(app).listen(3002, function() {
console.log(' [*] Running bbb-html5 kurento video service.');
});
var wss = new ws.Server({
server : server,
path : '/html5video'
});
var clientId = 0;
wss.on('connection', function(ws) {
var sessionId;
var request = ws.upgradeReq;
var response = {
writeHead : {}
};
sessionHandler(request, response, function(err) {
sessionId = request.session.id + "_" + clientId++;
if (!sessions[sessionId]) {
sessions[sessionId] = {videos: {}, iceQueue: {}};
}
console.log('Connection received with sessionId ' + sessionId);
});
ws.on('error', function(error) {
console.log('Connection ' + sessionId + ' error');
// stop(sessionId);
});
ws.on('close', function() {
console.log('Connection ' + sessionId + ' closed');
stopSession(sessionId);
});
ws.on('message', function(_message) {
var message = JSON.parse(_message);
var video;
if (message.cameraId && sessions[sessionId].videos[message.cameraId]) {
video = sessions[sessionId].videos[message.cameraId];
}
switch (message.id) {
case 'start':
console.log('[' + message.id + '] connection ' + sessionId);
if (video) {
video.once('READY', function() {
console.log("Video is ready");
startVideo(message, ws, sessionId);
});
}
else {
startVideo(message, ws, sessionId);
}
break;
case 'stop':
console.log('[' + message.id + '] connection ' + sessionId);
if (video) {
video.stop(sessionId);
delete sessions[sessionId].videos[message.cameraId];
} else {
console.log(" [stop] Why is there no video on STOP?");
}
break;
case 'onIceCandidate':
onIceCandidate(sessionId, message.cameraId, message.candidate);
break;
default:
ws.sendMessage({ id : 'error', message : 'Invalid message ' + message });
break;
}
});
});
var stopSession = function(sessionId) {
if(typeof sessions[sessionId] === 'undefined') {
console.log(' [>] Session ' + sessionId + ' was already terminated');
return;
}
console.log(' [>] Stopping session ' + sessionId);
var videoIds = Object.keys(sessions[sessionId].videos);
for (var i = 0; i < videoIds.length; i++) {
var video = sessions[sessionId].videos[videoIds[i]];
if (video){
console.log(video);
console.log(videoIds[i]);
video.stop();
} else {
console.log("Stop session but video was null");
}
delete sessions[sessionId].videos[videoIds[i]];
}
delete sessions[sessionId];
}
var stopAll = function() {
console.log('\n [x] Stopping everything! ');
var sessionIds = Object.keys(sessions);
for (var i = 0; i < sessionIds.length; i++) {
stopSession(sessionIds[i]);
}
setTimeout(process.exit, 1000);
}
function onIceCandidate(sessionId, id, candidate) {
if (sessions[sessionId][id]) {
sessions[sessionId][id].onIceCandidate(candidate);
} else {
sessions[sessionId].iceQueue = sessions[sessionId].iceQueue || {};
sessions[sessionId].iceQueue[id] = sessions[sessionId].iceQueue[id] || [];
sessions[sessionId].iceQueue[id].push(candidate);
}
}
function startVideo(message, ws, sessionId) {
console.log('[' + message.id + '] connection ' + sessionId);
let video = new Video(ws, message.cameraId, message.cameraShared);
sessions[sessionId].videos[message.cameraId] = video;
video.start(message.sdpOffer, function(error, sdpAnswer) {
if (error) {
return ws.sendMessage({id : 'error', message : error });
}
// Get ice candidates that arrived before video was created
if (sessions[sessionId].iceQueue) {
var queue = sessions[sessionId].iceQueue[message.cameraId];
while (queue && queue.length > 0) {
video.onIceCandidate(queue.pop());
}
}
ws.sendMessage({id : 'startResponse', cameraId: message.cameraId, sdpAnswer : sdpAnswer});
});
}
process.on('SIGTERM', stopAll);
process.on('SIGINT', stopAll);

View File

@ -1 +0,0 @@
node_modules/

View File

@ -1,8 +0,0 @@
kurentoUrl: "KURENTOURL"
kurentoIp: "KURENTOIP"
localIpAddress: "HOST"
acceptSelfSignedCertificate: false
redisHost : "127.0.0.1"
redisPort : "6379"
minVideoPort: 30000
maxVideoPort: 33000

View File

@ -1 +0,0 @@
node --inspect --debug-brk server.js

View File

@ -1,2 +0,0 @@
This folder contains a dummy self-signed certificate only for demo purposses,
**DON'T USE IT IN PRODUCTION**.

View File

@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCuf5QfyX2oDDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE0MDkyOTA5NDczNVoXDTE1MDkyOTA5NDczNVowRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMJOyOHJ+rJWJEQ7P7kKoWa31ff7hKNZxF6sYE5lFi3pBYWIY6kTN/iUaxJLROFo
FhoC/M/STY76rIryix474v/6cRoG8N+GQBEn4IAP1UitWzVO6pVvBaIt5IKlhhfm
YA1IMweCd03vLcaHTddNmFDBTks7QDwfenTaR5VjKYc3OtEhcG8dgLAnOjbbk2Hr
8wter2IeNgkhya3zyoXnTLT8m8IMg2mQaJs62Xlo9gs56urvVDWG4rhdGybj1uwU
ZiDYyP4CFCUHS6UVt12vADP8vjbwmss2ScGsIf0NjaU+MpSdEbB82z4b2NiN8Wq+
rFA/JbvyeoWWHMoa7wkVs1MCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAYLRwV9fo
AOhJfeK199Tv6oXoNSSSe10pVLnYxPcczCVQ4b9SomKFJFbmwtPVGi6w3m+8mV7F
9I2WKyeBHzmzfW2utZNupVybxgzEjuFLOVytSPdsB+DcJomOi8W/Cf2Vk8Wykb/t
Ctr1gfOcI8rwEGKxm279spBs0u1snzoLyoimbMbiXbC82j1IiN3Jus08U07m/j7N
hRBCpeHjUHT3CRpvYyTRnt+AyBd8BiyJB7nWmcNI1DksXPfehd62MAFS9e1ZE+dH
Aavg/U8VpS7pcCQcPJvIJ2hehrt8L6kUk3YUYqZ0OeRZK27f2R5+wFlDF33esm3N
dCSsLJlXyqAQFg==
-----END CERTIFICATE-----

View File

@ -1,16 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMJOyOHJ+rJWJEQ7P7kKoWa31ff7hKNZxF6sYE5l
Fi3pBYWIY6kTN/iUaxJLROFoFhoC/M/STY76rIryix474v/6cRoG8N+GQBEn4IAP
1UitWzVO6pVvBaIt5IKlhhfmYA1IMweCd03vLcaHTddNmFDBTks7QDwfenTaR5Vj
KYc3OtEhcG8dgLAnOjbbk2Hr8wter2IeNgkhya3zyoXnTLT8m8IMg2mQaJs62Xlo
9gs56urvVDWG4rhdGybj1uwUZiDYyP4CFCUHS6UVt12vADP8vjbwmss2ScGsIf0N
jaU+MpSdEbB82z4b2NiN8Wq+rFA/JbvyeoWWHMoa7wkVs1MCAwEAAaAAMA0GCSqG
SIb3DQEBCwUAA4IBAQBMszYHMpklgTF/3h1zAzKXUD9NrtZp8eWhL06nwVjQX8Ai
EaCUiW0ypstokWcH9+30chd2OD++67NbxYUEucH8HrKpOoy6gs5L/mqgQ9Npz3OT
TB1HI4kGtpVuUQ5D7L0596tKzMX/CgW/hRcHWl+PDkwGhQs1qZcJ8QN+YP6AkRrO
5sDdDB/BLrB9PtBQbPrYIQcHQ7ooYWz/G+goqRxzZ6rt0aU2uAB6l7c82ADLAqFJ
qlw+xqVzEETVfqM5TXKK/wV3hgm4oSX5Q4SHLKF94ODOkWcnV4nfIKz7y+5XcQ3p
PrGimI1br07okC5rO9cgLCR0Ks20PPFcM0FvInW/
-----END CERTIFICATE REQUEST-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAwk7I4cn6slYkRDs/uQqhZrfV9/uEo1nEXqxgTmUWLekFhYhj
qRM3+JRrEktE4WgWGgL8z9JNjvqsivKLHjvi//pxGgbw34ZAESfggA/VSK1bNU7q
lW8Foi3kgqWGF+ZgDUgzB4J3Te8txodN102YUMFOSztAPB96dNpHlWMphzc60SFw
bx2AsCc6NtuTYevzC16vYh42CSHJrfPKhedMtPybwgyDaZBomzrZeWj2Cznq6u9U
NYbiuF0bJuPW7BRmINjI/gIUJQdLpRW3Xa8AM/y+NvCayzZJwawh/Q2NpT4ylJ0R
sHzbPhvY2I3xar6sUD8lu/J6hZYcyhrvCRWzUwIDAQABAoIBACwt56TW3MZxqZtN
8WYsUZheUispJ/ZQMcLo5JjOiSV1Jwk+gpJtyTse291z+bxagzP02/CQu4u32UVa
cmE0cp+LHO4zB8964dREwdm8P91fdS6Au/uwG5LNZniCFCQZAFvkv52Ef4XbzQen
uf4rKWerHBck6K0C5z/sZXxE6KtScE2ZLUmkhO0nkHM6MA6gFk2OMnB+oDTOWWPt
1mlreQlzuMYG/D4axviRYrOSYCE5Qu1SOw/DEOLQqqeBjQrKtAyOlFHZsIR6lBfe
KHMChPUcYIwaowt2DcqH/A+AFXRtaifa6DvH8Yul+2vAp47UEpaenVfM5bpN33XV
EzerjtECgYEA+xiXzblek67iQgRpc9eHSoqs4iRLhae8s8kpAG51Jz46Je+Dmium
XV769oiUGUxBeoUb7ryW+4MOzHJaA1BfGejQSvwLIB9e4cnikqnAArcqbcAcOCL1
aYYDiSmSmN/AokNZlPKEBFXP9bzXrU9smQJWNTHlcRl7JXfnwF+jwNsCgYEAxhpE
SBr9vlUVHNh/S6C5i80NIYg6jCy2FgsmuzEqmcqV0pTyzegmq8bru+QmuvoUj2o4
nVv4J9d1fLF6ECUVk9aK8UdJOOB6hAfurOdJCArgrsY/9t4uDzXfbPCdfSNQITE0
XgeNGQX1EzvwwkBmyZKk0kLIr3syP8ZCWfXDROkCgYBR+dF1pJMv++R6UR5sZ20P
9P5ERj0xwXVl7MKqFWXCDhrFz9BTQPTrftrIKgbPy4mFCnf4FTHlov/t11dzxYWG
2+9Ey8yGDDfZ1yNVZn39ZPdBJXsRCLi+XrZAzYXCyyoEz6ArdJGNKMbgH2r6dfeq
bIzgiQ2zQvJlZSQQNiksCQKBgCgwzAmU8EXdHRttEOZXBU3HnBJhgP9PUuHGAWWY
4/uvjhXbAiekIbRX9xt3fiQQ+HrgIfxK3F246K0TlKAR5f7IWAf7Xm+bmz+OHG4X
vklTa6IJtpBvIwkS9PE1H75zm54gTW+GOKoK+12bm4zNZA0hIy9FPVHcvKUTpAJ8
SdGBAoGAHLtJnB1NO4EgO6WtLQMXt7HrIbup8eZi8/82gC3422C+ooKIrYQ07qSw
nBOO/G0OB4yd6vCE2x5+TWSSCYGgG5A8aIv5qP76RP4hovGHxG/y2tfotw5UuOrh
nFWlTP4Urs8PeykvK9ao8r/T8BnPIC16U6ENYvAc0mRlFA2j1GA=
-----END RSA PRIVATE KEY-----

View File

@ -1,249 +0,0 @@
/*
* Lucas Fialho Zawacki
* Paulo Renato Lanzarin
* (C) Copyright 2017 Bigbluebutton
*
*/
'use strict'
const cookieParser = require('cookie-parser')
const express = require('express');
const session = require('express-session')
const wsModule = require('./websocket');
const http = require('http');
const fs = require('fs');
const BigBlueButtonGW = require('./bbb/pubsub/bbb-gw');
const MediaController = require('./media-controller');
var Screenshare = require('./screenshare');
var C = require('./bbb/messages/Constants');
// Global variables
module.exports = class ConnectionManager {
constructor (settings, logger) {
this._logger = logger;
this._clientId = 0;
this._app = express();
this._sessions = {};
this._screenshareSessions = {};
this._setupExpressSession();
this._setupHttpServer();
}
_setupExpressSession() {
this._app.use(cookieParser());
this._sessionHandler = session({
secret : 'Shawarma', rolling : true, resave : true, saveUninitialized : true
});
this._app.use(this._sessionHandler);
}
_setupHttpServer() {
let self = this;
/*
* Server startup
*/
this._httpServer = http.createServer(this._app).listen(3008, function() {
console.log(' [*] Running node-apps connection manager.');
});
/*
* Management of sessions
*/
this._wss = new wsModule.Server({
server : this._httpServer,
path : '/kurento-screenshare'
});
// TODO isolate this
this._bbbGW = new BigBlueButtonGW();
this._bbbGW.addSubscribeChannel(C.FROM_BBB_TRANSCODE_SYSTEM_CHAN, function(error, redisWrapper) {
if(error) {
console.log(' Could not connect to transcoder redis channel, finishing app...');
self._stopAll();
}
console.log(' [server] Successfully subscribed to redis channel');
});
this._wss.on('connection', self._onNewConnection.bind(self));
}
_onNewConnection(webSocket) {
let self = this;
let connectionId;
let request = webSocket.upgradeReq;
let sessionId;
let callerName;
let response = {
writeHead : {}
};
this._sessionHandler(request, response, function(err) {
connectionId = request.session.id + "_" + self._clientId++;
console.log('Connection received with connectionId ' + connectionId);
});
webSocket.on('error', function(error) {
console.log('Connection ' + connectionId + ' error');
self._stopSession(sessionId);
});
webSocket.on('close', function() {
console.log('Connection ' + connectionId + ' closed');
console.log(webSocket.presenter);
if (webSocket.presenter && self._screenshareSessions[sessionId]) { // if presenter // FIXME (this conditional was added to prevent screenshare stop when an iOS user quits)
console.log(" [CM] Stopping presenter " + sessionId);
self._stopSession(sessionId);
}
if (webSocket.viewer && typeof webSocket.session !== 'undefined') {
console.log(" [CM] Stopping viewer " + webSocket.viewerId);
webSocket.session._stopViewer(webSocket.viewerId);
}
});
webSocket.on('message', function(_message) {
let message = JSON.parse(_message);
let session;
// The sessionId is voiceBridge for screensharing sessions
sessionId = message.voiceBridge;
if(self._screenshareSessions[sessionId]) {
session = self._screenshareSessions[sessionId];
webSocket.session = session;
}
switch (message.id) {
case 'presenter':
// Checking if there's already a Screenshare session started
// because we shouldn't overwrite it
webSocket.presenter = true;
if (!self._screenshareSessions[message.voiceBridge]) {
self._screenshareSessions[message.voiceBridge] = {}
self._screenshareSessions[message.voiceBridge] = session;
}
//session.on('message', self._assembleSessionMessage.bind(self));
if(session) {
break;
}
session = new Screenshare(webSocket, connectionId, self._bbbGW,
sessionId, message.callerName, message.vh, message.vw,
message.internalMeetingId);
self._screenshareSessions[sessionId] = {}
self._screenshareSessions[sessionId] = session;
// starts presenter by sending sessionID, websocket and sdpoffer
session._startPresenter(connectionId, webSocket, message.sdpOffer, function(error, sdpAnswer) {
console.log(" Started presenter " + connectionId);
if (error) {
return webSocket.send(JSON.stringify({
id : 'presenterResponse',
response : 'rejected',
message : error
}));
}
webSocket.send(JSON.stringify({
id : 'presenterResponse',
response : 'accepted',
sdpAnswer : sdpAnswer
}));
console.log(" [websocket] Sending presenterResponse \n" + sdpAnswer);
});
break;
case 'viewer':
console.log("[viewer] Session output \n " + session);
webSocket.viewer = true;
webSocket.viewerId = message.callerName;
if (message.sdpOffer && message.voiceBridge) {
if (session) {
session._startViewer(webSocket, message.voiceBridge, message.sdpOffer, message.callerName, self._screenshareSessions[message.voiceBridge]._presenterEndpoint);
} else {
webSocket.sendMessage("voiceBridge not recognized");
webSocket.sendMessage(Object.keys(self._screenshareSessions));
webSocket.sendMessage(message.voiceBridge);
}
}
break;
case 'stop':
console.log('[' + message.id + '] connection ' + connectionId);
if (session) {
session._stop(sessionId);
} else {
console.log(" [stop] Why is there no session on STOP?");
}
break;
case 'onIceCandidate':
if (session) {
console.log(" [CM] What the fluff is happening");
session._onIceCandidate(message.candidate);
} else {
console.log(" [iceCandidate] Why is there no session on ICE CANDIDATE?");
}
break;
case 'ping':
webSocket.send(JSON.stringify({
id : 'pong',
response : 'accepted'
}));
break;
case 'viewerIceCandidate':
console.log("[viewerIceCandidate] Session output => " + session);
if (session) {
session._onViewerIceCandidate(message.candidate, message.callerName);
} else {
console.log("[iceCandidate] Why is there no session on ICE CANDIDATE?");
}
break;
default:
webSocket.sendMessage({ id : 'error', message : 'Invalid message ' + message });
break;
}
});
}
_stopSession(sessionId) {
console.log(' [>] Stopping session ' + sessionId);
let session = this._screenshareSessions[sessionId];
if(typeof session !== 'undefined' && typeof session._stop === 'function') {
session._stop();
}
delete this._screenshareSessions[sessionId];
}
_stopAll() {
console.log('\n [x] Stopping everything! ');
let sessionIds = Object.keys(this._screenshareSessions);
for (let i = 0; i < sessionIds.length; i++) {
this._stopSession(sessionIds[i]);
}
setTimeout(process.exit, 1000);
}
}

View File

@ -1,91 +0,0 @@
"use strict";
/**
* @classdesc
* Message constants for the communication with BigBlueButton
* @constructor
*/
function Constants () {
return {
// Media elements
WebRTC: "WebRtcEndpoint",
RTP: "RtpEndpoint",
AUDIO: "AUDIO",
VIDEO: "VIDEO",
ALL: "ALL",
// 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",
// RedisWrapper events
REDIS_MESSAGE : "redis_message",
// 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",
//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",
// 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",
// Screenshare 2x
CONFERENCE_NAME: "voiceConf",
SCREENSHARE_CONF: "screenshareConf",
STREAM_URL: "stream",
TIMESTAMP: "timestamp",
VIDEO_WIDTH: "vidWidth",
VIDEO_HEIGHT: "vidHeight",
// 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"
}
}
module.exports = Constants();

View File

@ -1,69 +0,0 @@
var Constants = require('./Constants.js');
// Messages
var OutMessage = require('./OutMessage.js');
var StartTranscoderRequestMessage =
require('./transcode/StartTranscoderRequestMessage.js')(Constants);
var StopTranscoderRequestMessage =
require('./transcode/StopTranscoderRequestMessage.js')(Constants);
var StartTranscoderSysReqMsg =
require('./transcode/StartTranscoderSysReqMsg.js')();
var StopTranscoderSysReqMsg =
require('./transcode/StopTranscoderSysReqMsg.js')();
var DeskShareRTMPBroadcastStartedEventMessage =
require('./screenshare/DeskShareRTMPBroadcastStartedEventMessage.js')(Constants);
var DeskShareRTMPBroadcastStoppedEventMessage =
require('./screenshare/DeskShareRTMPBroadcastStoppedEventMessage.js')(Constants);
var ScreenshareRTMPBroadcastStartedEventMessage2x =
require('./screenshare/ScreenshareRTMPBroadcastStartedEventMessage2x.js')(Constants);
var ScreenshareRTMPBroadcastStoppedEventMessage2x =
require('./screenshare/ScreenshareRTMPBroadcastStoppedEventMessage2x.js')(Constants);
/**
* @classdesc
* Messaging utils to assemble JSON/Redis BigBlueButton messages
* @constructor
*/
function Messaging() {}
Messaging.prototype.generateStartTranscoderRequestMessage =
function(meetingId, transcoderId, params) {
var statrm = new StartTranscoderSysReqMsg(meetingId, transcoderId, params);
return statrm.toJson();
}
Messaging.prototype.generateStopTranscoderRequestMessage =
function(meetingId, transcoderId) {
var stotrm = new StopTranscoderSysReqMsg(meetingId, transcoderId);
return stotrm.toJson();
}
Messaging.prototype.generateDeskShareRTMPBroadcastStartedEvent =
function(conferenceName, streamUrl, vw, vh, timestamp) {
var stadrbem = new DeskShareRTMPBroadcastStartedEventMessage(conferenceName, streamUrl, vw, vh, timestamp);
return stadrbem.toJson();
}
Messaging.prototype.generateDeskShareRTMPBroadcastStoppedEvent =
function(conferenceName, streamUrl, vw, vh, timestamp) {
var stodrbem = new DeskShareRTMPBroadcastStoppedEventMessage(conferenceName, streamUrl, vw, vh, timestamp);
return stodrbem.toJson();
}
Messaging.prototype.generateScreenshareRTMPBroadcastStartedEvent2x =
function(conferenceName, screenshareConf, streamUrl, vw, vh, timestamp) {
var stadrbem = new ScreenshareRTMPBroadcastStartedEventMessage2x(conferenceName, screenshareConf, streamUrl, vw, vh, timestamp);
return stadrbem.toJson();
}
Messaging.prototype.generateScreenshareRTMPBroadcastStoppedEvent2x =
function(conferenceName, screenshareConf, streamUrl, vw, vh, timestamp) {
var stodrbem = new ScreenshareRTMPBroadcastStoppedEventMessage2x(conferenceName, screenshareConf, streamUrl, vw, vh, timestamp);
return stodrbem.toJson();
}
module.exports = new Messaging();
module.exports.Constants = Constants;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,97 +0,0 @@
/**
* @classdesc
* Redis wrapper class for connecting to Redis channels
*/
/* Modules */
var redis = require('redis');
var config = require('config');
var Constants = require('../messages/Constants.js');
var util = require('util');
const EventEmitter = require('events').EventEmitter;
const _retryThreshold = 1000 * 60 * 60;
const _maxRetries = 10;
/* Public members */
var RedisWrapper = function(subpattern) {
// Redis PubSub client holders
this.redisCli = null;
this.redisPub = null;
// Pub and Sub channels/patterns
this.subpattern = subpattern;
EventEmitter.call(this);
}
util.inherits(RedisWrapper, EventEmitter);
RedisWrapper.prototype.startRedis = function(callback) {
var self = this;
if (this.redisCli) {
console.log(" [RedisWrapper] Redis Client already exists");
callback(false, this);
}
var options = {
host : config.get('redisHost'),
port : config.get('redisPort'),
//password: config.get('redis.password')
retry_strategy: redisRetry
};
this.redisCli = redis.createClient(options);
this.redisPub = redis.createClient(options);
console.log(" [RedisWrapper] Trying to subscribe to redis channel");
this.redisCli.on("psubscribe", function (channel, count) {
console.log(" [RedisWrapper] Successfully subscribed to pattern [" + channel + "]");
});
this.redisCli.on("pmessage", self.onMessage.bind(self));
this.redisCli.psubscribe(this.subpattern);
console.log(" [RedisWrapper] Started Redis client at " + options.host + ":" + options.port +
" for subscription pattern: " + this.subpattern);
callback(false, this);
};
RedisWrapper.prototype.stopRedis = function(callback) {
if (this.redisCli){
this.redisCli.quit();
}
callback(false);
};
RedisWrapper.prototype.publishToChannel = function(message, channel) {
if(this.redisPub) {
console.log(" [RedisWrapper] Sending message to channel [" + channel + "]: " + message);
this.redisPub.publish(channel, message);
}
};
RedisWrapper.prototype.onMessage = function(pattern, channel, message) {
console.log(" [RedisWrapper] Message received from channel [" + channel + "] : " + message);
// use event emitter to throw new message
this.emit(Constants.REDIS_MESSAGE, message);
}
/* Private members */
function redisRetry(options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
return new Error('The server refused the connection');
}
if (options.total_retry_time > _retryThreshold) {
return new Error('Retry time exhausted');
}
if (options.times_connected > _maxRetries) {
return undefined;
}
return Math.max(options.attempt * 100, 3000);
};
module.exports = RedisWrapper;

View File

@ -1,105 +0,0 @@
/**
* @classdesc
* BigBlueButton redis gateway for bbb-screenshare node app
*/
/* Modules */
var C = require('../messages/Constants.js');
var RedisWrapper = require('./RedisWrapper.js');
var config = require('config');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
/* Public members */
var BigBlueButtonGW = function () {
this.redisClients = null
EventEmitter.call(this);
};
util.inherits(BigBlueButtonGW, EventEmitter);
BigBlueButtonGW.prototype.addSubscribeChannel = function (channel, callback) {
var self = this;
if (this.redisClients === null) {
this.redisClients = {};
}
if (this.redisClients[channel]) {
return callback(null, this.redisClients[channel]);
}
var wrobj = new RedisWrapper(channel);
this.redisClients[channel] = {};
this.redisClients[channel] = wrobj;
wrobj.startRedis(function(error, redisCli) {
if(error) {
console.log(" [BigBlueButtonGW] Could not start redis client for channel " + channel);
return callback(error);
}
console.log(" [BigBlueButtonGW] Added redis client to this.redisClients[" + channel + "]");
wrobj.on(C.REDIS_MESSAGE, self.incomingMessage.bind(self));
return callback(null, wrobj);
});
};
/**
* 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
*/
BigBlueButtonGW.prototype.incomingMessage = function (message) {
var msg = JSON.parse(message);
// Trying to parse both message types, 1x and 2x
if (msg.header) {
var header = msg.header;
var payload = msg.payload;
}
else if (msg.core) {
var header = msg.core.header;
var payload = msg.core.body;
}
if (header){
switch (header.name) {
// interoperability with 1.1
case C.START_TRANSCODER_REPLY:
this.emit(C.START_TRANSCODER_REPLY, payload);
break;
case C.STOP_TRANSCODER_REPLY:
this.emit(C.STOP_TRANSCODER_REPLY, payload);
break;
// 2x messages
case C.START_TRANSCODER_RESP_2x:
payload[C.MEETING_ID_2x] = header[C.MEETING_ID_2x];
this.emit(C.START_TRANSCODER_RESP_2x, payload);
break;
case C.STOP_TRANSCODER_RESP_2x:
payload[C.MEETING_ID_2x] = header[C.MEETING_ID_2x];
this.emit(C.STOP_TRANSCODER_RESP_2x, payload);
break;
default:
console.log(" [BigBlueButtonGW] Unknown Redis message with ID =>" + header.name);
}
}
};
BigBlueButtonGW.prototype.publish = function (message, channel, callback) {
for(var client in this.redisClients) {
if(typeof this.redisClients[client].publishToChannel === 'function') {
this.redisClients[client].publishToChannel(message, channel);
return callback(null);
}
}
return callback("Client not found");
};
module.exports = BigBlueButtonGW;

View File

@ -1,56 +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.
*/
var sdpTransform = require('sdp-transform');
exports.transform = function(sdp) {
var mediaIndex = 0;
var res = sdpTransform.parse(sdp);
var 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
res.media[mediaIndex].rtp = res.media[mediaIndex].rtp.filter(function(elem) {
return elem.codec === 'H264';
});
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);
};

View File

@ -1,141 +0,0 @@
'use strict'
const Constants = require('./bbb/messages/Constants.js');
const config = require('config');
const kurento = require('kurento-client');
const mediaServerClient = null;
var _mediaPipelines = {};
var _mediaElements= {};
function createMediaPipeline(id, callback) {
console.log(' [media] Creating media pipeline for ' + id);
getMediaServerClient(function (error, mediaServerClient) {
mediaServerClient.create('MediaPipeline', function(err, pipeline) {
if (error) {
console.log("Could not find media server at address " + kurentoUrl);
return callback(error);
}
return callback(null , pipeline);
});
});
};
function getMediaServerClient (callback) {
let kurentoUrl = config.get('kurentoUrl');
if (mediaServerClient) {
callback(null, mediaServerClient);
}
else {
kurento(kurentoUrl, function(error, _mediaServerClient) {
if (error) {
console.log("Could not find media server at address " + kurentoUrl);
return callback(error, null);
}
console.log(" [server] Initiating kurento client. Connecting to: " + kurentoUrl);
return callback(null, _mediaServerClient);
});
}
};
/* Public members */
module.exports = {
createMediaElement : function (conference, type, callback) {
let self = this;
self.getMediaPipeline(conference, function(error, pipeline) {
pipeline.create(type, function(error, mediaElement) {
if (error) {
return callback(error, null);
}
console.log(" [MediaController] Created [" + type + "] media element: " + mediaElement.id);
_mediaElements[mediaElement.id] = mediaElement;
return callback(null, mediaElement);
});
});
},
connectMediaElements : function (sourceId, sinkId, type, callback) {
let source = _mediaElements[sourceId];
let sink = _mediaElements[sinkId];
if (source && sink) {
if (type === 'ALL') {
source.connect(sink, function (error) {
return callback (error);
});
} else {
console.log(typeof source.connect);
source.connect(sink, type, function (error) {
return callback (error);
});
}
} else {
return callback ("Failed to connect " + type + ": " + sourceId + " to " + sinkId);
}
},
releaseMediaElement : function (elementId) {
let mediaElement = _mediaElements[elementId];
if (typeof mediaElement !== 'undefined' && typeof mediaElement.release === 'function') {
mediaElement.release();
}
},
releasePipeline: function (pipelineId) {
let MediaPipeline = _mediaPipelines[pipelineId];
if (typeof mediaElement !== 'undefined' && typeof mediaElement.release === 'function') {
mediaElement.release();
}
},
processOffer : function (elementId, sdpOffer, callback) {
let mediaElement = _mediaElements[elementId];
if (typeof mediaElement !== 'undefined' && typeof mediaElement.processOffer === 'function') {
mediaElement.processOffer (sdpOffer, function (error, sdpAnswer) {
return callback (error, sdpAnswer);
});
} else {
return callback (" [MediaController/processOffer] There is no element " + elementId, null);
}
},
getMediaPipeline : function(conference, callback) {
let self = this;
if (_mediaPipelines[conference]) {
console.log(' [media] Pipeline already exists. ' + JSON.stringify(_mediaPipelines, null, 2));
return callback(null, _mediaPipelines[conference]);
} else {
createMediaPipeline(conference, function(error, pipeline) {
_mediaPipelines[conference] = pipeline;
return callback(error, pipeline);
});
}
},
addIceCandidate : function (elementId, candidate) {
let mediaElement = _mediaElements[elementId];
if (typeof mediaElement !== 'undefined' && typeof mediaElement.addIceCandidate === 'function') {
mediaElement.addIceCandidate(candidate);
}
},
gatherCandidates : function (elementId, callback) {
let mediaElement = _mediaElements[elementId];
if (typeof mediaElement !== 'undefined' && typeof mediaElement.gatherCandidates === 'function') {
mediaElement.gatherCandidates(function (error) {
return callback(error);
});
} else {
return callback (" [MediaController/gatherCandidates] There is no element " + elementId, null);
}
},
};

View File

@ -1,99 +0,0 @@
var config = require('config');
var kurento = require('kurento-client');
var Constants = require('./bbb/messages/Constants');
var kurentoClient = null;
var mediaPipelines = {};
module.exports.getKurentoClient = function(kurentoUrl, callback) {
if (kurentoClient !== null) {
return callback(null, kurentoClient);
}
kurento(kurentoUrl, function(error, _kurentoClient) {
if (error) {
console.log("Could not find media server at address " + kurentoUrl);
return callback("Could not find media server at address" + kurentoUrl + ". Exiting with error " + error);
}
console.log(" [MediaHandler] Initiating kurento client. Connecting to: " + kurentoUrl);
kurentoClient = _kurentoClient;
callback(null, kurentoClient);
});
}
module.exports.getMediaPipeline = function(id, callback) {
console.log(' [MediaHandler] Creating media pipeline for ' + id);
if (mediaPipelines[id]) {
console.log(' [media] Pipeline already exists.');
callback(null, mediaPipelines[id]);
} else {
kurentoClient.create('MediaPipeline', function(err, pipeline) {
mediaPipelines[id] = pipeline;
return callback(err, pipeline);
});
}
}
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) {
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;
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'));
}

View File

@ -1,347 +0,0 @@
/*
* Lucas Fialho Zawacki
* Paulo Renato Lanzarin
* (C) Copyright 2017 Bigbluebutton
*
*/
'use strict'
// Imports
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 MediaController = require('./media-controller');
// Global stuff
var sharedScreens = {};
var rtpEndpoints = {};
const kurento = require('kurento-client');
const config = require('config');
const kurentoUrl = config.get('kurentoUrl');
const kurentoIp = config.get('kurentoIp');
const localIpAddress = config.get('localIpAddress');
if (config.get('acceptSelfSignedCertificate')) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED=0;
}
module.exports = class Screenshare {
constructor(ws, id, bbbgw, voiceBridge, caller, vh, vw, meetingId) {
this._ws = ws;
this._id = id;
this._BigBlueButtonGW = bbbgw;
this._presenterEndpoint = null;
this._ffmpegRtpEndpoint = null;
this._voiceBridge = voiceBridge;
this._meetingId = meetingId;
this._caller = caller;
this._streamUrl = "";
this._vw = vw;
this._vh = vh;
this._candidatesQueue = [];
this._viewersEndpoint = [];
this._viewersCandidatesQueue = [];
}
// TODO isolate ICE
_onIceCandidate(_candidate) {
let candidate = kurento.getComplexType('IceCandidate')(_candidate);
if (this._presenterEndpoint) {
console.log(" [screenshare] Adding ICE candidate to presenter");
this._presenterEndpoint.addIceCandidate(candidate);
}
else {
this._candidatesQueue.push(candidate);
}
};
_onViewerIceCandidate(_candidate, callerName) {
console.log("onviewericecandidate callerName = " + callerName);
let candidate = kurento.getComplexType('IceCandidate')(_candidate);
if (this._viewersEndpoint[callerName]) {
this._viewersEndpoint[callerName].addIceCandidate(candidate);
}
else {
if (!this._viewersCandidatesQueue[callerName]) {
this._viewersCandidatesQueue[callerName] = [];
}
this._viewersCandidatesQueue[callerName].push(candidate);
}
}
_startViewer(ws, voiceBridge, sdp, callerName, presenterEndpoint, callback) {
let self = this;
let _callback = function(){};
console.log("startviewer callerName = " + callerName);
self._viewersCandidatesQueue[callerName] = [];
console.log("VIEWER VOICEBRIDGE: "+self._voiceBridge);
MediaController.createMediaElement(voiceBridge, C.WebRTC, function(error, webRtcEndpoint) {
if (error) {
console.log("Media elements error" + error);
return _callback(error);
}
self._viewersEndpoint[callerName] = webRtcEndpoint;
// QUEUES UP ICE CANDIDATES IF NEGOTIATION IS NOT YET READY
while(self._viewersCandidatesQueue[callerName].length) {
let candidate = self._viewersCandidatesQueue[callerName].shift();
MediaController.addIceCandidate(self._viewersEndpoint[callerName].id, candidate);
}
// CONNECTS TWO MEDIA ELEMENTS
MediaController.connectMediaElements(presenterEndpoint.id, self._viewersEndpoint[callerName].id, C.VIDEO, function(error) {
if (error) {
console.log("Media elements CONNECT error " + error);
//pipeline.release();
return _callback(error);
}
});
// ICE NEGOTIATION WITH THE ENDPOINT
self._viewersEndpoint[callerName].on('OnIceCandidate', function(event) {
let candidate = kurento.getComplexType('IceCandidate')(event.candidate); ws.sendMessage({ id : 'iceCandidate', candidate : candidate });
});
sdp = h264_sdp.transform(sdp);
// PROCESS A SDP OFFER
MediaController.processOffer(webRtcEndpoint.id, sdp, function(error, webRtcSdpAnswer) {
if (error) {
console.log(" [webrtc] processOffer error => " + error + " for SDP " + sdp);
//pipeline.release();
return _callback(error);
}
ws.sendMessage({id: "viewerResponse", sdpAnswer: webRtcSdpAnswer, response: "accepted"});
console.log(" Sent sdp message to client with callerName:" + callerName);
MediaController.gatherCandidates(webRtcEndpoint.id, function(error) {
if (error) {
return _callback(error);
}
self._viewersEndpoint[callerName].on('MediaFlowInStateChange', function(event) {
if (event.state === 'NOT_FLOWING') {
console.log(" NOT FLOWING ");
}
else if (event.state === 'FLOWING') {
console.log(" FLOWING ");
}
});
});
});
});
}
_startPresenter(id, ws, sdpOffer, callback) {
let self = this;
let _callback = callback;
// Force H264 on Firefox and Chrome
sdpOffer = h264_sdp.transform(sdpOffer);
console.log("Starting presenter for " + sdpOffer);
console.log("PRESENTER VOICEBRIDGE: " + self._voiceBridge);
MediaController.createMediaElement(self._voiceBridge, C.WebRTC, function(error, webRtcEndpoint) {
if (error) {
console.log("Media elements error" + error);
return _callback(error);
}
MediaController.createMediaElement(self._voiceBridge, C.RTP, function(error, rtpEndpoint) {
if (error) {
console.log("Media elements error" + error);
return _callback(error);
}
while(self._candidatesQueue.length) {
let candidate = self._candidatesQueue.shift();
MediaController.addIceCandidate(webRtcEndpoint.id, candidate);
}
MediaController.connectMediaElements(webRtcEndpoint.id, rtpEndpoint.id, C.VIDEO, function(error) {
if (error) {
console.log("Media elements CONNECT error " + error);
//pipeline.release();
return _callback(error);
}
// It's a user sharing a Screen
sharedScreens[id] = webRtcEndpoint;
rtpEndpoints[id] = rtpEndpoint;
// Store our endpoint
self._presenterEndpoint = webRtcEndpoint;
self._ffmpegRtpEndpoint = rtpEndpoint;
self._presenterEndpoint.on('OnIceCandidate', function(event) {
let candidate = kurento.getComplexType('IceCandidate')(event.candidate);
ws.sendMessage({ id : 'iceCandidate', cameraId: id, candidate : candidate });
});
MediaController.processOffer(webRtcEndpoint.id, sdpOffer, function(error, webRtcSdpAnswer) {
if (error) {
console.log(" [webrtc] processOffer error => " + error + " for SDP " + sdpOffer);
//pipeline.release();
return _callback(error);
}
let sendVideoPort = MediaHandler.getVideoPort();
let rtpSdpOffer = MediaHandler.generateVideoSdp(localIpAddress, sendVideoPort);
console.log(" [rtpendpoint] RtpEndpoint processing => " + rtpSdpOffer);
MediaController.gatherCandidates(webRtcEndpoint.id, function(error) {
if (error) {
return _callback(error);
}
MediaController.processOffer(rtpEndpoint.id, rtpSdpOffer, function(error, rtpSdpAnswer) {
if (error) {
console.log(" [rtpendpoint] processOffer error => " + error + " for SDP " + rtpSdpOffer);
//pipeline.release();
return _callback(error);
}
console.log(" [rtpendpoint] KMS answer SDP => " + rtpSdpAnswer);
let recvVideoPort = rtpSdpAnswer.match(/m=video\s(\d*)/)[1];
let rtpParams = MediaHandler.generateTranscoderParams(kurentoIp, localIpAddress,
sendVideoPort, recvVideoPort, self._meetingId, "stream_type_video", C.RTP_TO_RTMP, "copy", "caller");
self._ffmpegRtpEndpoint.on('MediaFlowInStateChange', function(event) {
if (event.state === 'NOT_FLOWING') {
self._onRtpMediaNotFlowing();
}
else if (event.state === 'FLOWING') {
self._onRtpMediaFlowing(self._meetingId, rtpParams);
}
});
return _callback(null, webRtcSdpAnswer);
});
});
});
});
});
});
};
_stop() {
console.log(' [stop] Releasing endpoints for ' + this._id);
this._stopScreensharing();
if (this._presenterEndpoint) {
MediaController.releaseMediaElement(this._presenterEndpoint.id);
this._presenterEndpoint = null;
} else {
console.log(" [webRtcEndpoint] PLEASE DONT TRY STOPPING THINGS TWICE");
}
if (this._ffmpegRtpEndpoint) {
MediaController.releaseMediaElement(this._ffmpegRtpEndpoint.id);
this._ffmpegRtpEndpoint = null;
} else {
console.log(" [rtpEndpoint] PLEASE DONT TRY STOPPING THINGS TWICE");
}
console.log(' [stop] Screen is shared, releasing ' + this._id);
delete sharedScreens[this._id];
delete this._candidatesQueue;
};
_stopScreensharing() {
let self = this;
let strm = Messaging.generateStopTranscoderRequestMessage(this._meetingId, this._meetingId);
self._BigBlueButtonGW.publish(strm, C.TO_BBB_TRANSCODE_SYSTEM_CHAN, function(error) {});
// Interoperability: capturing 1.1 stop_transcoder_reply messages
self._BigBlueButtonGW.once(C.STOP_TRANSCODER_REPLY, function(payload) {
let meetingId = payload[C.MEETING_ID];
self._stopRtmpBroadcast(meetingId);
});
// Capturing stop transcoder responses from the 2x model
self._BigBlueButtonGW.once(C.STOP_TRANSCODER_RESP_2x, function(payload) {
let meetingId = payload[C.MEETING_ID_2x];
self._stopRtmpBroadcast(meetingId);
});
}
_onRtpMediaFlowing(meetingId, rtpParams) {
console.log(" [screenshare] Media FLOWING for meeting => " + meetingId);
let self = this;
let strm = Messaging.generateStartTranscoderRequestMessage(meetingId, meetingId, rtpParams);
// Interoperability: capturing 1.1 start_transcoder_reply messages
self._BigBlueButtonGW.once(C.START_TRANSCODER_REPLY, function(payload) {
let meetingId = payload[C.MEETING_ID];
let output = payload["params"].output;
self._startRtmpBroadcast(meetingId, output);
});
// Capturing stop transcoder responses from the 2x model
self._BigBlueButtonGW.once(C.START_TRANSCODER_RESP_2x, function(payload) {
let meetingId = payload[C.MEETING_ID_2x];
let output = payload["params"].output;
self._startRtmpBroadcast(meetingId, output);
});
self._BigBlueButtonGW.publish(strm, C.TO_BBB_TRANSCODE_SYSTEM_CHAN, function(error) {});
};
_stopRtmpBroadcast (meetingId) {
console.log(" [screenshare] _stopRtmpBroadcast for meeting => " + meetingId);
let self = this;
if(self._meetingId === meetingId) {
// TODO correctly assemble this timestamp
let timestamp = now.format('hhmmss');
let dsrstom = Messaging.generateScreenshareRTMPBroadcastStoppedEvent2x(self._voiceBridge,
self._voiceBridge, self._streamUrl, self._vw, self._vh, timestamp);
self._BigBlueButtonGW.publish(dsrstom, C.FROM_VOICE_CONF_SYSTEM_CHAN, function(error) {});
}
}
_startRtmpBroadcast (meetingId, output) {
console.log(" [screenshare] _startRtmpBroadcast for meeting => " + meetingId);
var self = this;
if(self._meetingId === meetingId) {
// TODO correctly assemble this timestamp
let timestamp = now.format('hhmmss');
self._streamUrl = MediaHandler.generateStreamUrl(localIpAddress, meetingId, output);
let dsrbstam = Messaging.generateScreenshareRTMPBroadcastStartedEvent2x(self._voiceBridge,
self._voiceBridge, self._streamUrl, self._vw, self._vh, timestamp);
self._BigBlueButtonGW.publish(dsrbstam, C.FROM_VOICE_CONF_SYSTEM_CHAN, function(error) {});
}
}
_onRtpMediaNotFlowing() {
console.log(" [screenshare] TODO RTP NOT_FLOWING");
};
_stopViewer(id) {
let viewer = this._viewersEndpoint[id];
console.log(' [stop] Releasing endpoints for ' + id);
if (viewer) {
MediaController.releaseMediaElement(viewer.id);
this._viewersEndpoint[viewer.id] = null;
} else {
console.log(" [webRtcEndpoint] PLEASE DONT TRY STOPPING THINGS TWICE");
}
delete this._viewersCandidatesQueue[id];
};
};

View File

@ -1,18 +0,0 @@
/*
* Simple wrapper around the ws library
*
*/
var ws = require('ws');
ws.prototype.sendMessage = function(json) {
return this.send(JSON.stringify(json), function(error) {
if(error)
console.log(' [server] Websocket error "' + error + '" on message "' + json.id + '"');
});
};
module.exports = ws;

View File

@ -1,24 +0,0 @@
{
"name": "bbb-screenshare-video-kurento-bridge",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "nodejs server.js",
"postinstall": "npm start"
},
"dependencies": {
"cookie-parser": "^1.3.5",
"express": "~4.12.4",
"express-session": "~1.10.3",
"kurento-client": "6.6.0",
"moment": "*",
"redis": "^2.6.2",
"sdp-transform": "*",
"uuid": "^3.1.0",
"ws": "~1.0.1"
},
"devDependencies": {
"config": "^1.26.1",
"js-yaml": "^3.8.3"
}
}

View File

@ -1,11 +0,0 @@
/*
* Paulo Renato Lanzarin
* (C) Copyright 2017 Bigbluebutton
*
*/
const ConnectionManager = require('./lib/ConnectionManager');
const CM = new ConnectionManager();
process.on('SIGTERM', CM._stopAll.bind(CM));
process.on('SIGINT', CM._stopAll.bind(CM));