You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

statsEmitter.js 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // @flow
  2. import _ from 'lodash';
  3. import {
  4. JitsiConnectionQualityEvents
  5. } from '../base/lib-jitsi-meet';
  6. /**
  7. * Contains all the callbacks to be notified when stats are updated.
  8. *
  9. * {
  10. * userId: Function[]
  11. * }.
  12. */
  13. const subscribers = {};
  14. /**
  15. * A singleton that acts as a pub/sub service for connection stat updates.
  16. */
  17. const statsEmitter = {
  18. /**
  19. * Have {@code statsEmitter} subscribe to stat updates from a given
  20. * conference.
  21. *
  22. * @param {JitsiConference} conference - The conference for which
  23. * {@code statsEmitter} should subscribe for stat updates.
  24. * @returns {void}
  25. */
  26. startListeningForStats(conference: Object) {
  27. conference.on(JitsiConnectionQualityEvents.LOCAL_STATS_UPDATED,
  28. stats => this._onStatsUpdated(conference.myUserId(), stats));
  29. conference.on(JitsiConnectionQualityEvents.REMOTE_STATS_UPDATED,
  30. (id, stats) => this._emitStatsUpdate(id, stats));
  31. },
  32. /**
  33. * Add a subscriber to be notified when stats are updated for a specified
  34. * user id.
  35. *
  36. * @param {string} id - The user id whose stats updates are of interest.
  37. * @param {Function} callback - The function to invoke when stats for the
  38. * user have been updated.
  39. * @returns {void}
  40. */
  41. subscribeToClientStats(id: ?string, callback: Function) {
  42. if (!id) {
  43. return;
  44. }
  45. if (!subscribers[id]) {
  46. subscribers[id] = [];
  47. }
  48. subscribers[id].push(callback);
  49. },
  50. /**
  51. * Remove a subscriber that is listening for stats updates for a specified
  52. * user id.
  53. *
  54. * @param {string} id - The user id whose stats updates are no longer of
  55. * interest.
  56. * @param {Function} callback - The function that is currently subscribed to
  57. * stat updates for the specified user id.
  58. * @returns {void}
  59. */
  60. unsubscribeToClientStats(id: string, callback: Function) {
  61. if (!subscribers[id]) {
  62. return;
  63. }
  64. const filteredSubscribers = subscribers[id].filter(
  65. subscriber => subscriber !== callback);
  66. if (filteredSubscribers.length) {
  67. subscribers[id] = filteredSubscribers;
  68. } else {
  69. delete subscribers[id];
  70. }
  71. },
  72. /**
  73. * Emit a stat update to all those listening for a specific user's
  74. * connection stats.
  75. *
  76. * @param {string} id - The user id the stats are associated with.
  77. * @param {Object} stats - New connection stats for the user.
  78. * @returns {void}
  79. */
  80. _emitStatsUpdate(id: string, stats: Object = {}) {
  81. const callbacks = subscribers[id] || [];
  82. callbacks.forEach(callback => {
  83. callback(stats);
  84. });
  85. },
  86. /**
  87. * Emit a stat update to all those listening for local stat updates. Will
  88. * also update listeners of remote user stats of changes related to their
  89. * stats.
  90. *
  91. * @param {string} localUserId - The user id for the local user.
  92. * @param {Object} stats - Connection stats for the local user as provided
  93. * by the library.
  94. * @returns {void}
  95. */
  96. _onStatsUpdated(localUserId: string, stats: Object) {
  97. const allUserFramerates = stats.framerate || {};
  98. const allUserResolutions = stats.resolution || {};
  99. const allUserCodecs = stats.codec || {};
  100. // FIXME resolution and framerate are maps keyed off of user ids with
  101. // stat values. Receivers of stats expect resolution and framerate to
  102. // be primitives, not maps, so here we override the 'lib-jitsi-meet'
  103. // stats objects.
  104. const modifiedLocalStats = Object.assign({}, stats, {
  105. framerate: allUserFramerates[localUserId],
  106. resolution: allUserResolutions[localUserId],
  107. codec: allUserCodecs[localUserId]
  108. });
  109. this._emitStatsUpdate(localUserId, modifiedLocalStats);
  110. // Get all the unique user ids from the framerate and resolution stats
  111. // and update remote user stats as needed.
  112. const framerateUserIds = Object.keys(allUserFramerates);
  113. const resolutionUserIds = Object.keys(allUserResolutions);
  114. const codecUserIds = Object.keys(allUserCodecs);
  115. _.union(framerateUserIds, resolutionUserIds, codecUserIds)
  116. .filter(id => id !== localUserId)
  117. .forEach(id => {
  118. const remoteUserStats = {};
  119. const framerate = allUserFramerates[id];
  120. if (framerate) {
  121. remoteUserStats.framerate = framerate;
  122. }
  123. const resolution = allUserResolutions[id];
  124. if (resolution) {
  125. remoteUserStats.resolution = resolution;
  126. }
  127. const codec = allUserCodecs[id];
  128. if (codec) {
  129. remoteUserStats.codec = codec;
  130. }
  131. this._emitStatsUpdate(id, remoteUserStats);
  132. });
  133. }
  134. };
  135. export default statsEmitter;