Merge branch 'master' of https://github.com/bigbluebutton/bigbluebutton into bbb-2x-mconf

This commit is contained in:
Richard Alam 2017-05-08 08:51:31 -07:00
commit 44e3fb8a07
6 changed files with 143 additions and 204 deletions

View File

@ -1,9 +1,5 @@
var monitoredTracks = {}; var monitoredTracks = {};
function isFirefox() {
return typeof mozRTCPeerConnection !== 'undefined';
}
function arrayAverage(array) { function arrayAverage(array) {
var sum = 0; var sum = 0;
for( var i = 0; i < array.length; i++ ){ for( var i = 0; i < array.length; i++ ){
@ -22,180 +18,138 @@ function customGetStats(peer, mediaStreamTrack, callback, interval) {
var nomore = false; var nomore = false;
(function getPrivateStats() { (function getPrivateStats() {
var promise = _getStats(peer, mediaStreamTrack); var promise;
try {
promise = peer.getStats(mediaStreamTrack);
} catch (e) {
promise = Promise.reject(e);
}
promise.then(function(results) { promise.then(function(results) {
var result = {
audio: {}, var inbound = {};
//video: {}, var outbound = {};
// TODO remove the raw results var localCandidate = {};
results: results, var remoteCandidate = {};
nomore: function() {
nomore = true; results.forEach(function(res) {
if ((res.type == 'outbound-rtp' || res.type == 'outboundrtp') && res.mediaType == 'audio') {
outbound = res;
} }
if ((res.type == 'inbound-rtp' || res.type == 'inboundrtp') && res.mediaType == 'audio') {
inbound = res;
}
if (res.type == 'ssrc' && res.mediaType == 'audio') {
if (typeof (res.audioInputLevel) !== 'undefined') {
inbound = res;
outbound = res;
res.packetsSent = parseInt(res.packetsSent);
res.bytesSent = parseInt(res.bytesSent);
res.packetsLost = parseInt(res.packetsLost);
res.jitter = parseInt(res.googJitterReceived) / 1000;
res.packetsReceived = res.packetsSent - res.packetsLost;
}
}
if ((res.type == 'candidate-pair' && res.selected) ||
(res.type == 'googCandidatePair' && res.googActiveConnection == "true")) {
localCandidate = results[res.localCandidateId];
remoteCandidate = results[res.remoteCandidateId];
}
});
/*
console.log("Inbound:");
console.log(inbound);
console.log("Outbound:");
console.log(outbound);
*/
var audioStats = {
inboundTimestamp: inbound.timestamp,
packetsReceived: inbound.packetsReceived,
bytesReceived: inbound.bytesReceived,
packetsLost: inbound.packetsLost,
jitter: inbound.jitter,
outboundTimestamp: outbound.timestamp,
packetsSent: outbound.packetsSent,
bytesSent: outbound.bytesSent,
}; };
if (typeof globalObject.audio.statsArray === 'undefined') {
globalObject.audio.statsArray = [];
globalObject.audio.haveStats = false;
}
var statsArray = globalObject.audio.statsArray;
statsArray.push(audioStats);
while (statsArray.length > 12) {
statsArray.shift();
}
for (var key in results) { var firstAudioStats = statsArray[0];
var res = results[key]; var lastAudioStats = statsArray[statsArray.length - 1];
res.timestamp = typeof res.timestamp === 'object'? res.timestamp.getTime(): res.timestamp; var intervalPacketsLost = lastAudioStats.packetsLost - firstAudioStats.packetsLost;
res.packetsLost = typeof res.packetsLost === 'string'? parseInt(res.packetsLost): res.packetsLost; var intervalPacketsReceived = lastAudioStats.packetsReceived - firstAudioStats.packetsReceived;
res.packetsReceived = typeof res.packetsReceived === 'string'? parseInt(res.packetsReceived): res.packetsReceived; var intervalPacketsSent = lastAudioStats.packetsSent - firstAudioStats.packetsSent;
res.bytesReceived = typeof res.bytesReceived === 'string'? parseInt(res.bytesReceived): res.bytesReceived; var intervalBytesReceived = lastAudioStats.bytesReceived - firstAudioStats.bytesReceived;
var intervalBytesSent = lastAudioStats.bytesSent - firstAudioStats.bytesSent;
if ((res.mediaType == 'audio' || (res.type == 'ssrc' && res.googCodecName == 'opus')) && typeof res.bytesSent !== 'undefined') { var receivedInterval = lastAudioStats.inboundTimestamp - firstAudioStats.inboundTimestamp;
if (typeof globalObject.audio.prevSent === 'undefined') { var sentInterval = lastAudioStats.outboundTimestamp - firstAudioStats.outboundTimestamp;
globalObject.audio.prevSent = res;
}
var bytes = res.bytesSent - globalObject.audio.prevSent.bytesSent; var kbitsReceivedPerSecond = intervalBytesReceived * 8 / receivedInterval;
var diffTimestamp = res.timestamp - globalObject.audio.prevSent.timestamp; var kbitsSentPerSecond = intervalBytesSent * 8 / sentInterval;
var packetDuration = intervalPacketsSent / sentInterval * 1000;
var kilobytes = bytes / 1024; var r = undefined, mos = undefined;
var kbitsSentPerSecond = (kilobytes * 8) / (diffTimestamp / 1000.0); if (isNaN(intervalPacketsLost) && !globalObject.audio.haveStats) {
r = 100;
result.audio = merge(result.audio, { } else {
availableBandwidth: kilobytes, globalObject.audio.haveStats = true;
inputLevel: res.audioInputLevel, r = (Math.max(0, intervalPacketsReceived - intervalPacketsLost) / intervalPacketsReceived) * 100;
packetsSent: res.packetsSent, if (r > 100) {
bytesSent: res.bytesSent, r = 100;
kbitsSentPerSecond: kbitsSentPerSecond
});
globalObject.audio.prevSent = res;
} }
if ((res.mediaType == 'audio' || (res.type == 'ssrc' && res.googCodecName == 'opus')) && typeof res.bytesReceived !== 'undefined') { }
if (typeof globalObject.audio.prevReceived === 'undefined') { mos = 1 + (0.035) * r + (0.000007) * r * (r-60) * (100-r);
globalObject.audio.prevReceived = res;
globalObject.audio.consecutiveFlaws = 0;
globalObject.audio.globalBitrateArray = [ ];
}
var intervalPacketsLost = res.packetsLost - globalObject.audio.prevReceived.packetsLost; var intervalLossRate = 1 - (r / 100);
var intervalPacketsReceived = res.packetsReceived - globalObject.audio.prevReceived.packetsReceived; console.log("Interval loss rate: " + intervalLossRate);
var intervalBytesReceived = res.bytesReceived - globalObject.audio.prevReceived.bytesReceived; console.log("MOS: " + mos);
var intervalLossRate = intervalPacketsLost + intervalPacketsReceived == 0? 1: intervalPacketsLost / (intervalPacketsLost + intervalPacketsReceived);
var intervalBitrate = (intervalBytesReceived / interval) * 8;
var globalBitrate = arrayAverage(globalObject.audio.globalBitrateArray);
var intervalEstimatedLossRate;
if (isFirefox()) {
intervalEstimatedLossRate = Math.max(0, globalBitrate - intervalBitrate) / globalBitrate;
} else {
intervalEstimatedLossRate = intervalLossRate;
}
var flaws = intervalPacketsLost; result = {
if (flaws > 0) { audio: {
if (globalObject.audio.consecutiveFlaws > 0) { availableBandwidth: undefined,
flaws *= 2; bytesReceived: audioStats.bytesReceived,
} bytesSent: audioStats.bytesSent,
++globalObject.audio.consecutiveFlaws; delay: undefined,
} else { globalBitrate: undefined,
globalObject.audio.consecutiveFlaws = 0; inputLevel: undefined,
} intervalEstimatedLossRate: intervalLossRate,
var packetsLost = res.packetsLost + flaws; intervalLossRate: intervalLossRate,
var r = (Math.max(0, res.packetsReceived - packetsLost) / res.packetsReceived) * 100; jitter: audioStats.jitter,
if (r > 100) { jitterBuffer: undefined,
r = 100; kbitsReceivedPerSecond: kbitsReceivedPerSecond,
} kbitsSentPerSecond: kbitsSentPerSecond,
// https://freeswitch.org/stash/projects/FS/repos/freeswitch/browse/src/switch_rtp.c?at=refs%2Fheads%2Fv1.4#1671 mos: mos,
var mos = 1 + (0.035) * r + (0.000007) * r * (r-60) * (100-r); outputLevel: undefined,
packetDuration: packetDuration,
var bytes = res.bytesReceived - globalObject.audio.prevReceived.bytesReceived; packetsLost: audioStats.packetsLost,
var diffTimestamp = res.timestamp - globalObject.audio.prevReceived.timestamp; packetsReceived: audioStats.packetsReceived,
var packetDuration = diffTimestamp / (res.packetsReceived - globalObject.audio.prevReceived.packetsReceived); packetsSent: audioStats.packetsSent,
var kilobytes = bytes / 1024; r: r,
var kbitsReceivedPerSecond = (kilobytes * 8) / (diffTimestamp / 1000.0); },
video: {},
result.audio = merge(result.audio, { connectionType: {
availableBandwidth: kilobytes, local: {
outputLevel: res.audioOutputLevel, candidateType: localCandidate.candidateType,
packetsLost: res.packetsLost, ipAddress: localCandidate.ipAddress,
jitter: typeof res.googJitterReceived !== 'undefined'? res.googJitterReceived: res.jitter, transport: localCandidate.transport,
jitterBuffer: res.googJitterBufferMs, },
delay: res.googCurrentDelayMs, remote: {
packetsReceived: res.packetsReceived, candidateType: remoteCandidate.candidateType,
bytesReceived: res.bytesReceived, ipAddress: remoteCandidate.ipAddress,
kbitsReceivedPerSecond: kbitsReceivedPerSecond, transport: remoteCandidate.transport,
packetDuration: packetDuration, },
r: r, transport: localCandidate.transport
mos: mos, },
intervalLossRate: intervalLossRate, nomore: function() { nomore = true; }
intervalEstimatedLossRate: intervalEstimatedLossRate,
globalBitrate: globalBitrate
});
globalObject.audio.prevReceived = res;
globalObject.audio.globalBitrateArray.push(intervalBitrate);
// 12 is the number of seconds we use to calculate the global bitrate
if (globalObject.audio.globalBitrateArray.length > 12 / (interval / 1000)) {
globalObject.audio.globalBitrateArray.shift();
}
}
/*
// TODO make it work on Firefox
if (res.googCodecName == 'VP8') {
if (!globalObject.video.prevBytesSent)
globalObject.video.prevBytesSent = res.bytesSent;
var bytes = res.bytesSent - globalObject.video.prevBytesSent;
globalObject.video.prevBytesSent = res.bytesSent;
var kilobytes = bytes / 1024;
result.video = merge(result.video, {
availableBandwidth: kilobytes.toFixed(1),
googFrameHeightInput: res.googFrameHeightInput,
googFrameWidthInput: res.googFrameWidthInput,
googCaptureQueueDelayMsPerS: res.googCaptureQueueDelayMsPerS,
rtt: res.googRtt,
packetsLost: res.packetsLost,
packetsSent: res.packetsSent,
googEncodeUsagePercent: res.googEncodeUsagePercent,
googCpuLimitedResolution: res.googCpuLimitedResolution,
googNacksReceived: res.googNacksReceived,
googFrameRateInput: res.googFrameRateInput,
googPlisReceived: res.googPlisReceived,
googViewLimitedResolution: res.googViewLimitedResolution,
googCaptureJitterMs: res.googCaptureJitterMs,
googAvgEncodeMs: res.googAvgEncodeMs,
googFrameHeightSent: res.googFrameHeightSent,
googFrameRateSent: res.googFrameRateSent,
googBandwidthLimitedResolution: res.googBandwidthLimitedResolution,
googFrameWidthSent: res.googFrameWidthSent,
googFirsReceived: res.googFirsReceived,
bytesSent: res.bytesSent
});
}
if (res.type == 'VideoBwe') {
result.video.bandwidth = {
googActualEncBitrate: res.googActualEncBitrate,
googAvailableSendBandwidth: res.googAvailableSendBandwidth,
googAvailableReceiveBandwidth: res.googAvailableReceiveBandwidth,
googRetransmitBitrate: res.googRetransmitBitrate,
googTargetEncBitrate: res.googTargetEncBitrate,
googBucketDelay: res.googBucketDelay,
googTransmitBitrate: res.googTransmitBitrate
};
}
*/
// res.googActiveConnection means either STUN or TURN is used.
if (res.type == 'googCandidatePair' && res.googActiveConnection == 'true') {
result.connectionType = {
local: {
candidateType: res.googLocalCandidateType,
ipAddress: res.googLocalAddress
},
remote: {
candidateType: res.googRemoteCandidateType,
ipAddress: res.googRemoteAddress
},
transport: res.googTransportType
};
}
} }
callback(result); callback(result);
@ -210,29 +164,6 @@ function customGetStats(peer, mediaStreamTrack, callback, interval) {
}); });
})(); })();
// taken from http://blog.telenor.io/webrtc/2015/06/11/getstats-chrome-vs-firefox.html
function _getStats(pc, selector) {
if (navigator.mozGetUserMedia) {
return pc.getStats(selector);
}
return new Promise(function(resolve, reject) {
pc.getStats(function(response) {
var standardReport = {};
response.result().forEach(function(report) {
var standardStats = {
id: report.id,
timestamp: report.timestamp,
type: report.type
};
report.names().forEach(function(name) {
standardStats[name] = report.stat(name);
});
standardReport[standardStats.id] = standardStats;
});
resolve(standardReport);
}, selector, reject);
});
}
} }
function merge(mergein, mergeto) { function merge(mergein, mergeto) {
@ -246,7 +177,8 @@ function merge(mergein, mergeto) {
} }
function monitorTrackStart(peer, track, local) { function monitorTrackStart(peer, track, local) {
if (! monitoredTracks.hasOwnProperty(track.id)) { console.log("Starting stats monitoring on " + track.id);
if (!monitoredTracks[track.id]) {
monitoredTracks[track.id] = function() { console.log("Still didn't have any report for this track"); }; monitoredTracks[track.id] = function() { console.log("Still didn't have any report for this track"); };
customGetStats( customGetStats(
peer, peer,
@ -257,7 +189,6 @@ function monitorTrackStart(peer, track, local) {
} else { } else {
monitoredTracks[track.id] = results.nomore; monitoredTracks[track.id] = results.nomore;
results.audio.type = local? "local": "remote", results.audio.type = local? "local": "remote",
delete results.results;
BBB.webRTCMonitorUpdate(JSON.stringify(results)); BBB.webRTCMonitorUpdate(JSON.stringify(results));
console.log(JSON.stringify(results)); console.log(JSON.stringify(results));
} }
@ -270,9 +201,13 @@ function monitorTrackStart(peer, track, local) {
} }
function monitorTrackStop(trackId) { function monitorTrackStop(trackId) {
monitoredTracks[trackId](); if (typeof (monitoredTracks[trackId]) === "function") {
delete monitoredTracks[trackId]; monitoredTracks[trackId]();
console.log("Track removed, monitoredTracks.length = " + Object.keys(monitoredTracks).length); delete monitoredTracks[trackId];
console.log("Track removed, monitoredTracks.length = " + Object.keys(monitoredTracks).length);
} else {
console.log("Track is not monitored");
}
} }
function monitorTracksStart() { function monitorTracksStart() {
@ -289,12 +224,12 @@ function monitorTracksStart() {
monitorTrackStart(peer, track, true); monitorTrackStart(peer, track, true);
} }
} }
for (var streamId = 0; streamId < peer.getRemoteStreams().length; ++streamId) { /*for (var streamId = 0; streamId < peer.getRemoteStreams().length; ++streamId) {
for (var trackId = 0; trackId < peer.getRemoteStreams()[streamId].getAudioTracks().length; ++trackId) { for (var trackId = 0; trackId < peer.getRemoteStreams()[streamId].getAudioTracks().length; ++trackId) {
var track = peer.getRemoteStreams()[streamId].getAudioTracks()[trackId]; var track = peer.getRemoteStreams()[streamId].getAudioTracks()[trackId];
monitorTrackStart(peer, track, false); monitorTrackStart(peer, track, false);
} }
} }*/
}, 2000); }, 2000);
} }

View File

@ -20,10 +20,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
--> -->
<mx:HBox xmlns="flexlib.containers.*" xmlns:mx="http://www.adobe.com/2006/mxml" <mx:HBox xmlns="flexlib.containers.*" xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:mate="http://mate.asfusion.com/" visible="false" backgroundColor="0xCCCCCC" xmlns:mate="http://mate.asfusion.com/" visible="false" backgroundColor="0xCCCCCC"
cornerRadius="5" borderStyle="solid" cornerRadius="5" borderStyle="solid"
paddingBottom="3" paddingTop="3" paddingLeft="3" paddingRight="3" alpha="0" paddingBottom="3" paddingTop="3" paddingLeft="3" paddingRight="3" alpha="0"
xmlns:views="org.bigbluebutton.modules.whiteboard.views.*" xmlns:local="*"> xmlns:views="org.bigbluebutton.modules.whiteboard.views.*" xmlns:local="*">
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}" method="viewerMode" /> <mate:Listener type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}" method="viewerMode" />

View File

@ -1,16 +1,16 @@
import { getVoiceBridge } from '/imports/ui/components/audio/service';
import BaseAudioBridge from './base'; import BaseAudioBridge from './base';
export default class VertoBridge extends BaseAudioBridge { export default class VertoBridge extends BaseAudioBridge {
constructor(userData) { constructor(userData) {
super(); super();
const { const {
userId,
username, username,
voiceBridge, voiceBridge,
} = userData; } = userData;
this.voiceBridge = voiceBridge; this.voiceBridge = voiceBridge;
this.vertoUsername = 'FreeSWITCH User - ' + encodeURIComponent(username); this.vertoUsername = `${userId}-bbbID-${username}`;
} }
exitAudio(listenOnly) { exitAudio(listenOnly) {

View File

@ -6,6 +6,8 @@ import Storage from '/imports/ui/services/storage/session';
import Users from '/imports/api/users'; import Users from '/imports/api/users';
import { makeCall } from '/imports/ui/services/api'; import { makeCall } from '/imports/ui/services/api';
const CONNECTION_TIMEOUT = Meteor.settings.public.app.connectionTimeout;
class Auth { class Auth {
constructor() { constructor() {
this._meetingID = Storage.getItem('meetingID'); this._meetingID = Storage.getItem('meetingID');
@ -133,7 +135,7 @@ class Auth {
error: 500, error: 500,
description: 'Authentication timeout.', description: 'Authentication timeout.',
}); });
}, 5000); }, CONNECTION_TIMEOUT);
const didValidate = () => { const didValidate = () => {
this.loggedIn = true; this.loggedIn = true;

View File

@ -2,3 +2,4 @@
app: app:
# Flag for HTTPS. # Flag for HTTPS.
httpsConnection: false httpsConnection: false
connectionTimeout: 60000

View File

@ -2,3 +2,4 @@
app: app:
# Flag for HTTPS. # Flag for HTTPS.
httpsConnection: true httpsConnection: true
connectionTimeout: 5000