2017-10-03 19:24:00 +00:00
|
|
|
// @flow
|
|
|
|
|
2017-07-13 18:44:12 +00:00
|
|
|
import _ from 'lodash';
|
|
|
|
|
2018-08-07 18:31:51 +00:00
|
|
|
import {
|
|
|
|
JitsiConnectionQualityEvents,
|
|
|
|
JitsiE2ePingEvents
|
|
|
|
} from '../base/lib-jitsi-meet';
|
2017-07-05 18:17:30 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains all the callbacks to be notified when stats are updated.
|
|
|
|
*
|
|
|
|
* {
|
|
|
|
* userId: Function[]
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
const subscribers = {};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A singleton that acts as a pub/sub service for connection stat updates.
|
|
|
|
*/
|
|
|
|
const statsEmitter = {
|
|
|
|
/**
|
|
|
|
* Have {@code statsEmitter} subscribe to stat updates from a given
|
|
|
|
* conference.
|
|
|
|
*
|
|
|
|
* @param {JitsiConference} conference - The conference for which
|
|
|
|
* {@code statsEmitter} should subscribe for stat updates.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2017-10-03 19:24:00 +00:00
|
|
|
startListeningForStats(conference: Object) {
|
2017-10-10 23:31:40 +00:00
|
|
|
conference.on(JitsiConnectionQualityEvents.LOCAL_STATS_UPDATED,
|
2017-07-13 18:44:12 +00:00
|
|
|
stats => this._onStatsUpdated(conference.myUserId(), stats));
|
2017-07-05 18:17:30 +00:00
|
|
|
|
2017-10-10 23:31:40 +00:00
|
|
|
conference.on(JitsiConnectionQualityEvents.REMOTE_STATS_UPDATED,
|
2017-07-05 18:17:30 +00:00
|
|
|
(id, stats) => this._emitStatsUpdate(id, stats));
|
2018-08-07 18:31:51 +00:00
|
|
|
|
|
|
|
conference.on(
|
|
|
|
JitsiE2ePingEvents.E2E_RTT_CHANGED,
|
|
|
|
(participant, e2eRtt) => {
|
|
|
|
const stats = {
|
|
|
|
e2eRtt,
|
|
|
|
region: participant.getProperty('region')
|
|
|
|
};
|
|
|
|
|
|
|
|
this._emitStatsUpdate(participant.getId(), stats);
|
|
|
|
});
|
2017-07-05 18:17:30 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a subscriber to be notified when stats are updated for a specified
|
|
|
|
* user id.
|
|
|
|
*
|
|
|
|
* @param {string} id - The user id whose stats updates are of interest.
|
|
|
|
* @param {Function} callback - The function to invoke when stats for the
|
|
|
|
* user have been updated.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2017-10-03 19:24:00 +00:00
|
|
|
subscribeToClientStats(id: ?string, callback: Function) {
|
2017-07-05 18:17:30 +00:00
|
|
|
if (!id) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!subscribers[id]) {
|
|
|
|
subscribers[id] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
subscribers[id].push(callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a subscriber that is listening for stats updates for a specified
|
|
|
|
* user id.
|
|
|
|
*
|
|
|
|
* @param {string} id - The user id whose stats updates are no longer of
|
|
|
|
* interest.
|
|
|
|
* @param {Function} callback - The function that is currently subscribed to
|
|
|
|
* stat updates for the specified user id.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2017-10-03 19:24:00 +00:00
|
|
|
unsubscribeToClientStats(id: string, callback: Function) {
|
2017-07-05 18:17:30 +00:00
|
|
|
if (!subscribers[id]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const filteredSubscribers = subscribers[id].filter(
|
|
|
|
subscriber => subscriber !== callback);
|
|
|
|
|
|
|
|
if (filteredSubscribers.length) {
|
|
|
|
subscribers[id] = filteredSubscribers;
|
|
|
|
} else {
|
|
|
|
delete subscribers[id];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Emit a stat update to all those listening for a specific user's
|
|
|
|
* connection stats.
|
|
|
|
*
|
|
|
|
* @param {string} id - The user id the stats are associated with.
|
|
|
|
* @param {Object} stats - New connection stats for the user.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2017-10-03 19:24:00 +00:00
|
|
|
_emitStatsUpdate(id: string, stats: Object = {}) {
|
2017-07-05 18:17:30 +00:00
|
|
|
const callbacks = subscribers[id] || [];
|
|
|
|
|
|
|
|
callbacks.forEach(callback => {
|
|
|
|
callback(stats);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Emit a stat update to all those listening for local stat updates. Will
|
|
|
|
* also update listeners of remote user stats of changes related to their
|
|
|
|
* stats.
|
|
|
|
*
|
2018-09-17 18:21:03 +00:00
|
|
|
* @param {string} localUserId - The user id for the local user.
|
2017-07-05 18:17:30 +00:00
|
|
|
* @param {Object} stats - Connection stats for the local user as provided
|
|
|
|
* by the library.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2018-09-17 18:21:03 +00:00
|
|
|
_onStatsUpdated(localUserId: string, stats: Object) {
|
2017-09-30 01:35:23 +00:00
|
|
|
const allUserFramerates = stats.framerate || {};
|
|
|
|
const allUserResolutions = stats.resolution || {};
|
2020-08-13 21:56:14 +00:00
|
|
|
const allUserCodecs = stats.codec || {};
|
2017-07-05 18:17:30 +00:00
|
|
|
|
2018-09-17 18:21:03 +00:00
|
|
|
// FIXME resolution and framerate are maps keyed off of user ids with
|
2017-07-05 18:17:30 +00:00
|
|
|
// stat values. Receivers of stats expect resolution and framerate to
|
2018-09-17 18:21:03 +00:00
|
|
|
// be primitives, not maps, so here we override the 'lib-jitsi-meet'
|
|
|
|
// stats objects.
|
2017-09-30 01:35:23 +00:00
|
|
|
const modifiedLocalStats = Object.assign({}, stats, {
|
2018-09-17 18:21:03 +00:00
|
|
|
framerate: allUserFramerates[localUserId],
|
2020-08-13 21:56:14 +00:00
|
|
|
resolution: allUserResolutions[localUserId],
|
|
|
|
codec: allUserCodecs[localUserId]
|
2017-09-30 01:35:23 +00:00
|
|
|
});
|
2017-07-05 18:17:30 +00:00
|
|
|
|
2018-09-17 18:21:03 +00:00
|
|
|
this._emitStatsUpdate(localUserId, modifiedLocalStats);
|
2017-07-05 18:17:30 +00:00
|
|
|
|
2017-07-13 18:44:12 +00:00
|
|
|
// Get all the unique user ids from the framerate and resolution stats
|
|
|
|
// and update remote user stats as needed.
|
|
|
|
const framerateUserIds = Object.keys(allUserFramerates);
|
|
|
|
const resolutionUserIds = Object.keys(allUserResolutions);
|
2020-08-13 21:56:14 +00:00
|
|
|
const codecUserIds = Object.keys(allUserCodecs);
|
2017-07-13 18:44:12 +00:00
|
|
|
|
2020-08-13 21:56:14 +00:00
|
|
|
_.union(framerateUserIds, resolutionUserIds, codecUserIds)
|
2018-09-17 18:21:03 +00:00
|
|
|
.filter(id => id !== localUserId)
|
2017-07-05 18:17:30 +00:00
|
|
|
.forEach(id => {
|
2017-07-13 18:44:12 +00:00
|
|
|
const remoteUserStats = {};
|
|
|
|
|
2017-07-05 18:17:30 +00:00
|
|
|
const framerate = allUserFramerates[id];
|
|
|
|
|
|
|
|
if (framerate) {
|
2017-07-13 18:44:12 +00:00
|
|
|
remoteUserStats.framerate = framerate;
|
2017-07-05 18:17:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const resolution = allUserResolutions[id];
|
|
|
|
|
|
|
|
if (resolution) {
|
2017-07-13 18:44:12 +00:00
|
|
|
remoteUserStats.resolution = resolution;
|
2017-07-05 18:17:30 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 21:56:14 +00:00
|
|
|
const codec = allUserCodecs[id];
|
|
|
|
|
|
|
|
if (codec) {
|
|
|
|
remoteUserStats.codec = codec;
|
|
|
|
}
|
|
|
|
|
2017-07-13 18:44:12 +00:00
|
|
|
this._emitStatsUpdate(id, remoteUserStats);
|
|
|
|
});
|
2017-07-05 18:17:30 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export default statsEmitter;
|