ref(stats): process stats through one pub/sub
Instead of passing stats through UI then VideoLayout then the SmallVideo, pass stats directly to what uses it--ConnectionIndicator. This also bypasses adding the stats to the store, as they do not seem to be something that needs to be shared or stored app-wide just yet.
This commit is contained in:
parent
1d90826098
commit
44bbd26c96
|
@ -48,6 +48,7 @@ import {
|
||||||
trackAdded,
|
trackAdded,
|
||||||
trackRemoved
|
trackRemoved
|
||||||
} from './react/features/base/tracks';
|
} from './react/features/base/tracks';
|
||||||
|
import { statsEmitter } from './react/features/connection-indicator';
|
||||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||||
import {
|
import {
|
||||||
mediaPermissionPromptVisibilityChanged,
|
mediaPermissionPromptVisibilityChanged,
|
||||||
|
@ -64,8 +65,6 @@ const ConferenceErrors = JitsiMeetJS.errors.conference;
|
||||||
const TrackEvents = JitsiMeetJS.events.track;
|
const TrackEvents = JitsiMeetJS.events.track;
|
||||||
const TrackErrors = JitsiMeetJS.errors.track;
|
const TrackErrors = JitsiMeetJS.errors.track;
|
||||||
|
|
||||||
const ConnectionQualityEvents = JitsiMeetJS.events.connectionQuality;
|
|
||||||
|
|
||||||
const eventEmitter = new EventEmitter();
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
let room;
|
let room;
|
||||||
|
@ -1726,16 +1725,7 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
room.on(ConnectionQualityEvents.LOCAL_STATS_UPDATED,
|
statsEmitter.startListeningForStats(room);
|
||||||
(stats) => {
|
|
||||||
APP.UI.updateLocalStats(stats.connectionQuality, stats);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
room.on(ConnectionQualityEvents.REMOTE_STATS_UPDATED,
|
|
||||||
(id, stats) => {
|
|
||||||
APP.UI.updateRemoteStats(id, stats.connectionQuality, stats);
|
|
||||||
});
|
|
||||||
|
|
||||||
room.addCommandListener(this.commands.defaults.ETHERPAD, ({value}) => {
|
room.addCommandListener(this.commands.defaults.ETHERPAD, ({value}) => {
|
||||||
APP.UI.initEtherpad(value);
|
APP.UI.initEtherpad(value);
|
||||||
|
|
|
@ -972,25 +972,6 @@ UI.hideStats = function () {
|
||||||
VideoLayout.hideStats();
|
VideoLayout.hideStats();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Update local connection quality statistics.
|
|
||||||
* @param {number} percent
|
|
||||||
* @param {object} stats
|
|
||||||
*/
|
|
||||||
UI.updateLocalStats = function (percent, stats) {
|
|
||||||
VideoLayout.updateLocalConnectionStats(percent, stats);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update connection quality statistics for remote user.
|
|
||||||
* @param {string} id user id
|
|
||||||
* @param {number} percent
|
|
||||||
* @param {object} stats
|
|
||||||
*/
|
|
||||||
UI.updateRemoteStats = function (id, percent, stats) {
|
|
||||||
VideoLayout.updateConnectionStats(id, percent, stats);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark video as interrupted or not.
|
* Mark video as interrupted or not.
|
||||||
* @param {boolean} interrupted if video is interrupted
|
* @param {boolean} interrupted if video is interrupted
|
||||||
|
|
|
@ -37,7 +37,7 @@ function LocalVideo(VideoLayout, emitter) {
|
||||||
this.setDisplayName();
|
this.setDisplayName();
|
||||||
|
|
||||||
this.addAudioLevelIndicator();
|
this.addAudioLevelIndicator();
|
||||||
this.updateConnectionIndicator();
|
this.updateIndicators();
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
||||||
|
|
|
@ -48,7 +48,7 @@ function RemoteVideo(user, VideoLayout, emitter) {
|
||||||
this.hasRemoteVideoMenu = false;
|
this.hasRemoteVideoMenu = false;
|
||||||
this._supportsRemoteControl = false;
|
this._supportsRemoteControl = false;
|
||||||
this.addRemoteVideoContainer();
|
this.addRemoteVideoContainer();
|
||||||
this.updateConnectionIndicator();
|
this.updateIndicators();
|
||||||
this.setDisplayName();
|
this.setDisplayName();
|
||||||
this.bindHoverHandler();
|
this.bindHoverHandler();
|
||||||
this.flipX = false;
|
this.flipX = false;
|
||||||
|
@ -632,18 +632,6 @@ RemoteVideo.prototype.addRemoteStreamElement = function (stream) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RemoteVideo.prototype.updateResolution = function (resolution) {
|
|
||||||
this.updateConnectionIndicator({ resolution });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates this video framerate indication.
|
|
||||||
* @param framerate the value to update
|
|
||||||
*/
|
|
||||||
RemoteVideo.prototype.updateFramerate = function (framerate) {
|
|
||||||
this.updateConnectionIndicator({ framerate });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the display name for the given video span id.
|
* Sets the display name for the given video span id.
|
||||||
*
|
*
|
||||||
|
|
|
@ -84,13 +84,14 @@ function SmallVideo(VideoLayout) {
|
||||||
this.disableUpdateView = false;
|
this.disableUpdateView = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Statistics to display within the connection indicator. With new updates,
|
* The current state of the user's bridge connection. The value should be
|
||||||
* only changed values are updated through assignment to a new reference.
|
* a string as enumerated in the library's participantConnectionStatus
|
||||||
|
* constants.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @type {object}
|
* @type {string|null}
|
||||||
*/
|
*/
|
||||||
this._cachedConnectionStats = {};
|
this._connectionStatus = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the ConnectionIndicator's popover is hovered. Modifies
|
* Whether or not the ConnectionIndicator's popover is hovered. Modifies
|
||||||
|
@ -260,18 +261,6 @@ SmallVideo.prototype.bindHoverHandler = function () {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the data for the indicator
|
|
||||||
* @param id the id of the indicator
|
|
||||||
* @param percent the percent for connection quality
|
|
||||||
* @param object the data
|
|
||||||
*/
|
|
||||||
SmallVideo.prototype.updateConnectionStats = function (percent, object) {
|
|
||||||
const newStats = Object.assign({}, object, { percent });
|
|
||||||
|
|
||||||
this.updateConnectionIndicator(newStats);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unmounts the ConnectionIndicator component.
|
* Unmounts the ConnectionIndicator component.
|
||||||
|
|
||||||
|
@ -289,7 +278,8 @@ SmallVideo.prototype.removeConnectionIndicator = function () {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
SmallVideo.prototype.updateConnectionStatus = function (connectionStatus) {
|
SmallVideo.prototype.updateConnectionStatus = function (connectionStatus) {
|
||||||
this.updateConnectionIndicator({ connectionStatus });
|
this._connectionStatus = connectionStatus;
|
||||||
|
this.updateIndicators();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -741,21 +731,6 @@ SmallVideo.prototype.initBrowserSpecificProperties = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates or updates the connection indicator. Updates the previously known
|
|
||||||
* statistics about the participant's connection.
|
|
||||||
*
|
|
||||||
* @param {Object} newStats - New statistics to merge with previously known
|
|
||||||
* statistics about the participant's connection.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
SmallVideo.prototype.updateConnectionIndicator = function (newStats = {}) {
|
|
||||||
this._cachedConnectionStats
|
|
||||||
= Object.assign({}, this._cachedConnectionStats, newStats);
|
|
||||||
|
|
||||||
this.updateIndicators();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the React element responsible for showing connection status, dominant
|
* Updates the React element responsible for showing connection status, dominant
|
||||||
* speaker, and raised hand icons. Uses instance variables to get the necessary
|
* speaker, and raised hand icons. Uses instance variables to get the necessary
|
||||||
|
@ -775,11 +750,12 @@ SmallVideo.prototype.updateIndicators = function () {
|
||||||
<div>
|
<div>
|
||||||
{ this._showConnectionIndicator
|
{ this._showConnectionIndicator
|
||||||
? <ConnectionIndicator
|
? <ConnectionIndicator
|
||||||
|
connectionStatus = { this._connectionStatus }
|
||||||
iconSize = { iconSize }
|
iconSize = { iconSize }
|
||||||
isLocalVideo = { this.isLocal }
|
isLocalVideo = { this.isLocal }
|
||||||
onHover = { this._onPopoverHover }
|
onHover = { this._onPopoverHover }
|
||||||
showMoreLink = { this.isLocal }
|
showMoreLink = { this.isLocal }
|
||||||
stats = { this._cachedConnectionStats } />
|
userID = { this.id } />
|
||||||
: null }
|
: null }
|
||||||
{ this._showRaisedHand
|
{ this._showRaisedHand
|
||||||
? <RaisedHandIndicator iconSize = { iconSize } /> : null }
|
? <RaisedHandIndicator iconSize = { iconSize } /> : null }
|
||||||
|
|
|
@ -211,6 +211,11 @@ var VideoLayout = {
|
||||||
if (largeVideo && !largeVideo.id) {
|
if (largeVideo && !largeVideo.id) {
|
||||||
this.updateLargeVideo(APP.conference.getMyUserId(), true);
|
this.updateLargeVideo(APP.conference.getMyUserId(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: replace this call with a generic update call once SmallVideo
|
||||||
|
// only contains a ReactElement. Then remove this call once the
|
||||||
|
// Filmstrip is fully in React.
|
||||||
|
localVideoThumbnail.updateIndicators();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -766,60 +771,6 @@ var VideoLayout = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates local stats
|
|
||||||
* @param percent
|
|
||||||
* @param object
|
|
||||||
*/
|
|
||||||
updateLocalConnectionStats (percent, object) {
|
|
||||||
const { framerate, resolution } = object;
|
|
||||||
|
|
||||||
// FIXME overwrites 'lib-jitsi-meet' internal object
|
|
||||||
// Why library internal objects are passed as event's args ?
|
|
||||||
object.resolution = resolution[APP.conference.getMyUserId()];
|
|
||||||
object.framerate = framerate[APP.conference.getMyUserId()];
|
|
||||||
localVideoThumbnail.updateConnectionStats(percent, object);
|
|
||||||
|
|
||||||
Object.keys(resolution).forEach(function (id) {
|
|
||||||
if (APP.conference.isLocalId(id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let resolutionValue = resolution[id];
|
|
||||||
let remoteVideo = remoteVideos[id];
|
|
||||||
|
|
||||||
if (resolutionValue && remoteVideo) {
|
|
||||||
remoteVideo.updateResolution(resolutionValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(framerate).forEach(function (id) {
|
|
||||||
if (APP.conference.isLocalId(id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const framerateValue = framerate[id];
|
|
||||||
const remoteVideo = remoteVideos[id];
|
|
||||||
|
|
||||||
if (framerateValue && remoteVideo) {
|
|
||||||
remoteVideo.updateFramerate(framerateValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates remote stats.
|
|
||||||
* @param id the id associated with the stats
|
|
||||||
* @param percent the connection quality percent
|
|
||||||
* @param object the stats data
|
|
||||||
*/
|
|
||||||
updateConnectionStats (id, percent, object) {
|
|
||||||
let remoteVideo = remoteVideos[id];
|
|
||||||
if (remoteVideo) {
|
|
||||||
remoteVideo.updateConnectionStats(percent, object);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the connection indicator
|
* Hides the connection indicator
|
||||||
* @param id
|
* @param id
|
||||||
|
|
|
@ -5,6 +5,8 @@ import JitsiPopover from '../../../../modules/UI/util/JitsiPopover';
|
||||||
import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
|
import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
|
||||||
import { ConnectionStatsTable } from '../../connection-stats';
|
import { ConnectionStatsTable } from '../../connection-stats';
|
||||||
|
|
||||||
|
import statsEmitter from '../statsEmitter';
|
||||||
|
|
||||||
declare var $: Object;
|
declare var $: Object;
|
||||||
declare var interfaceConfig: Object;
|
declare var interfaceConfig: Object;
|
||||||
|
|
||||||
|
@ -57,6 +59,14 @@ class ConnectionIndicator extends Component {
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
/**
|
||||||
|
* The current condition of the user's connection, matching one of the
|
||||||
|
* enumerated values in the library.
|
||||||
|
*
|
||||||
|
* @type {JitsiParticipantConnectionStatus}
|
||||||
|
*/
|
||||||
|
connectionStatus: React.PropTypes.string,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the displays stats are for local video.
|
* Whether or not the displays stats are for local video.
|
||||||
*/
|
*/
|
||||||
|
@ -73,26 +83,16 @@ class ConnectionIndicator extends Component {
|
||||||
*/
|
*/
|
||||||
showMoreLink: React.PropTypes.bool,
|
showMoreLink: React.PropTypes.bool,
|
||||||
|
|
||||||
/**
|
|
||||||
* An object that contains statistics related to connection quality.
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* bandwidth: Object,
|
|
||||||
* bitrate: Object,
|
|
||||||
* connectionStatus: String,
|
|
||||||
* framerate: Object,
|
|
||||||
* packetLoss: Object,
|
|
||||||
* percent: Number,
|
|
||||||
* resolution: Object,
|
|
||||||
* transport: Array
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
stats: React.PropTypes.object,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to obtain translated strings.
|
* Invoked to obtain translated strings.
|
||||||
*/
|
*/
|
||||||
t: React.PropTypes.func
|
t: React.PropTypes.func,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user ID associated with the displayed connection indication and
|
||||||
|
* stats.
|
||||||
|
*/
|
||||||
|
userID: React.PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,10 +121,19 @@ class ConnectionIndicator extends Component {
|
||||||
*
|
*
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
showMoreStats: false
|
showMoreStats: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache of the stats received from subscribing to stats emitting.
|
||||||
|
* The keys should be the name of the stat. With each stat update,
|
||||||
|
* updates stats are mixed in with cached stats and a new stats
|
||||||
|
* object is set in state.
|
||||||
|
*/
|
||||||
|
stats: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once for every instance.
|
// Bind event handlers so they are only bound once for every instance.
|
||||||
|
this._onStatsUpdated = this._onStatsUpdated.bind(this);
|
||||||
this._onToggleShowMore = this._onToggleShowMore.bind(this);
|
this._onToggleShowMore = this._onToggleShowMore.bind(this);
|
||||||
this._setRootElement = this._setRootElement.bind(this);
|
this._setRootElement = this._setRootElement.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -136,6 +145,9 @@ class ConnectionIndicator extends Component {
|
||||||
* returns {void}
|
* returns {void}
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
statsEmitter.subscribeToClientStats(
|
||||||
|
this.props.userID, this._onStatsUpdated);
|
||||||
|
|
||||||
this.popover = new JitsiPopover($(this._rootElement), {
|
this.popover = new JitsiPopover($(this._rootElement), {
|
||||||
content: this._renderStatisticsTable(),
|
content: this._renderStatisticsTable(),
|
||||||
skin: 'black',
|
skin: 'black',
|
||||||
|
@ -153,7 +165,14 @@ class ConnectionIndicator extends Component {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
* returns {void}
|
* returns {void}
|
||||||
*/
|
*/
|
||||||
componentDidUpdate() {
|
componentDidUpdate(prevProps) {
|
||||||
|
if (prevProps.userID !== this.props.userID) {
|
||||||
|
statsEmitter.unsubscribeToClientStats(
|
||||||
|
this.props.userID, this._onStatsUpdated);
|
||||||
|
statsEmitter.subscribeToClientStats(
|
||||||
|
this.props.userID, this._onStatsUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
this.popover.updateContent(this._renderStatisticsTable());
|
this.popover.updateContent(this._renderStatisticsTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +183,9 @@ class ConnectionIndicator extends Component {
|
||||||
* returns {void}
|
* returns {void}
|
||||||
*/
|
*/
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
statsEmitter.unsubscribeToClientStats(
|
||||||
|
this.props.userID, this._onStatsUpdated);
|
||||||
|
|
||||||
this.popover.forceHide();
|
this.popover.forceHide();
|
||||||
this.popover.remove();
|
this.popover.remove();
|
||||||
}
|
}
|
||||||
|
@ -186,6 +208,30 @@ class ConnectionIndicator extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked when new connection stats associated with the passed in
|
||||||
|
* user ID are available. Will update the component's display of current
|
||||||
|
* statistics.
|
||||||
|
*
|
||||||
|
* @param {Object} stats - Connection stats from the library.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onStatsUpdated(stats = {}) {
|
||||||
|
const { connectionQuality } = stats;
|
||||||
|
const newPercentageState = typeof connectionQuality === 'undefined'
|
||||||
|
? {} : { percent: connectionQuality };
|
||||||
|
const newStats = Object.assign(
|
||||||
|
{},
|
||||||
|
this.state.stats,
|
||||||
|
stats,
|
||||||
|
newPercentageState);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
stats: newStats
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback to invoke when the show more link in the popover content is
|
* Callback to invoke when the show more link in the popover content is
|
||||||
* clicked. Sets the state which will determine if the popover should show
|
* clicked. Sets the state which will determine if the popover should show
|
||||||
|
@ -204,7 +250,7 @@ class ConnectionIndicator extends Component {
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
_renderIcon() {
|
_renderIcon() {
|
||||||
switch (this.props.stats.connectionStatus) {
|
switch (this.props.connectionStatus) {
|
||||||
case JitsiParticipantConnectionStatus.INTERRUPTED:
|
case JitsiParticipantConnectionStatus.INTERRUPTED:
|
||||||
return (
|
return (
|
||||||
<span className = 'connection_lost'>
|
<span className = 'connection_lost'>
|
||||||
|
@ -218,7 +264,7 @@ class ConnectionIndicator extends Component {
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
default: {
|
default: {
|
||||||
const { percent } = this.props.stats;
|
const { percent } = this.state.stats;
|
||||||
const width = QUALITY_TO_WIDTH.find(x => percent >= x.percent);
|
const width = QUALITY_TO_WIDTH.find(x => percent >= x.percent);
|
||||||
const iconWidth = width && width.width
|
const iconWidth = width && width.width
|
||||||
? { width: width && width.width } : {};
|
? { width: width && width.width } : {};
|
||||||
|
@ -253,7 +299,7 @@ class ConnectionIndicator extends Component {
|
||||||
packetLoss,
|
packetLoss,
|
||||||
resolution,
|
resolution,
|
||||||
transport
|
transport
|
||||||
} = this.props.stats;
|
} = this.state.stats;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConnectionStatsTable
|
<ConnectionStatsTable
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
export * from './components';
|
export * from './components';
|
||||||
|
|
||||||
|
export { default as statsEmitter } from './statsEmitter';
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
import JitsiMeetJS from '../base/lib-jitsi-meet';
|
||||||
|
|
||||||
|
declare var APP: Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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}
|
||||||
|
*/
|
||||||
|
startListeningForStats(conference) {
|
||||||
|
const { connectionQuality } = JitsiMeetJS.events;
|
||||||
|
|
||||||
|
conference.on(connectionQuality.LOCAL_STATS_UPDATED,
|
||||||
|
stats => this._onStatsUpdated(stats));
|
||||||
|
|
||||||
|
conference.on(connectionQuality.REMOTE_STATS_UPDATED,
|
||||||
|
(id, stats) => this._emitStatsUpdate(id, stats));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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}
|
||||||
|
*/
|
||||||
|
subscribeToClientStats(id, callback) {
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
unsubscribeToClientStats(id, callback) {
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
_emitStatsUpdate(id, stats = {}) {
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @param {Object} stats - Connection stats for the local user as provided
|
||||||
|
* by the library.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onStatsUpdated(stats) {
|
||||||
|
const allUserFramerates = stats.framerate;
|
||||||
|
const allUserResolutions = stats.resolution;
|
||||||
|
|
||||||
|
const currentUserId = APP.conference.getMyUserId();
|
||||||
|
const currentUserFramerate = allUserFramerates[currentUserId];
|
||||||
|
const currentUserResolution = allUserResolutions[currentUserId];
|
||||||
|
|
||||||
|
// FIXME resolution and framerate are hashes keyed off of user ids with
|
||||||
|
// stat values. Receivers of stats expect resolution and framerate to
|
||||||
|
// be primatives, not hashes, so overwrites the 'lib-jitsi-meet' stats
|
||||||
|
// objects.
|
||||||
|
stats.framerate = currentUserFramerate;
|
||||||
|
stats.resolution = currentUserResolution;
|
||||||
|
|
||||||
|
this._emitStatsUpdate(currentUserId, stats);
|
||||||
|
|
||||||
|
Object.keys(allUserFramerates)
|
||||||
|
.filter(id => id !== currentUserId)
|
||||||
|
.forEach(id => {
|
||||||
|
const framerate = allUserFramerates[id];
|
||||||
|
|
||||||
|
if (framerate) {
|
||||||
|
this._emitStatsUpdate(id, { framerate });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(allUserResolutions)
|
||||||
|
.filter(id => id !== currentUserId)
|
||||||
|
.forEach(id => {
|
||||||
|
const resolution = allUserResolutions[id];
|
||||||
|
|
||||||
|
if (resolution) {
|
||||||
|
this._emitStatsUpdate(id, { resolution });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default statsEmitter;
|
Loading…
Reference in New Issue