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.

middleware.js 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // @flow
  2. import { jitsiLocalStorage } from '@jitsi/js-utils';
  3. import { getAmplitudeIdentity } from '../analytics';
  4. import { CONFERENCE_UNIQUE_ID_SET, E2E_RTT_CHANGED, getConferenceOptions, getRoomName } from '../base/conference';
  5. import { LIB_WILL_INIT } from '../base/lib-jitsi-meet/actionTypes';
  6. import { DOMINANT_SPEAKER_CHANGED, getLocalParticipant } from '../base/participants';
  7. import { MiddlewareRegistry } from '../base/redux';
  8. import { ADD_FACE_EXPRESSION } from '../face-landmarks/actionTypes';
  9. import RTCStats from './RTCStats';
  10. import { canSendRtcstatsData, isRtcstatsEnabled } from './functions';
  11. import logger from './logger';
  12. /**
  13. * Middleware which intercepts lib-jitsi-meet initialization and conference join in order init the
  14. * rtcstats-client.
  15. *
  16. * @param {Store} store - The redux store.
  17. * @returns {Function}
  18. */
  19. MiddlewareRegistry.register(store => next => action => {
  20. const state = store.getState();
  21. const config = state['features/base/config'];
  22. const { analytics } = config;
  23. switch (action.type) {
  24. case LIB_WILL_INIT: {
  25. if (isRtcstatsEnabled(state)) {
  26. // RTCStats "proxies" WebRTC functions such as GUM and RTCPeerConnection by rewriting the global
  27. // window functions. Because lib-jitsi-meet uses references to those functions that are taken on
  28. // init, we need to add these proxies before it initializes, otherwise lib-jitsi-meet will use the
  29. // original non proxy versions of these functions.
  30. try {
  31. // Default poll interval is 1000ms and standard stats will be used, if not provided in the config.
  32. const pollInterval = analytics.rtcstatsPollInterval || 1000;
  33. const useLegacy = analytics.rtcstatsUseLegacy || false;
  34. // Initialize but don't connect to the rtcstats server wss, as it will start sending data for all
  35. // media calls made even before the conference started.
  36. RTCStats.init({
  37. endpoint: analytics.rtcstatsEndpoint,
  38. useLegacy,
  39. pollInterval
  40. });
  41. } catch (error) {
  42. logger.error('Failed to initialize RTCStats: ', error);
  43. }
  44. }
  45. break;
  46. }
  47. case CONFERENCE_UNIQUE_ID_SET: {
  48. if (canSendRtcstatsData(state)) {
  49. // Once the conference started connect to the rtcstats server and send data.
  50. try {
  51. RTCStats.connect();
  52. const localParticipant = getLocalParticipant(state);
  53. const options = getConferenceOptions(state);
  54. // Unique identifier for a conference session, not to be confused with meeting name
  55. // i.e. If all participants leave a meeting it will have a different value on the next join.
  56. const { conference } = action;
  57. const meetingUniqueId = conference && conference.getMeetingUniqueId();
  58. // The current implementation of rtcstats-server is configured to send data to amplitude, thus
  59. // we add identity specific information so we can correlate on the amplitude side. If amplitude is
  60. // not configured an empty object will be sent.
  61. // The current configuration of the conference is also sent as metadata to rtcstats server.
  62. // This is done in order to facilitate queries based on different conference configurations.
  63. // e.g. Find all RTCPeerConnections that connect to a specific shard or were created in a
  64. // conference with a specific version.
  65. // XXX(george): we also want to be able to correlate between rtcstats and callstats, so we're
  66. // appending the callstats user name (if it exists) to the display name.
  67. const displayName = options.statisticsId
  68. || options.statisticsDisplayName
  69. || jitsiLocalStorage.getItem('callStatsUserName');
  70. RTCStats.sendIdentityData({
  71. ...getAmplitudeIdentity(),
  72. ...options,
  73. endpointId: localParticipant?.id,
  74. confName: getRoomName(state),
  75. displayName,
  76. meetingUniqueId
  77. });
  78. } catch (error) {
  79. // If the connection failed do not impact jitsi-meet just silently fail.
  80. logger.error('RTCStats connect failed with: ', error);
  81. }
  82. }
  83. break;
  84. }
  85. case DOMINANT_SPEAKER_CHANGED: {
  86. if (canSendRtcstatsData(state)) {
  87. const { id, previousSpeakers } = action.participant;
  88. RTCStats.sendDominantSpeakerData({ dominantSpeakerEndpoint: id,
  89. previousSpeakers });
  90. }
  91. break;
  92. }
  93. case E2E_RTT_CHANGED: {
  94. if (canSendRtcstatsData(state)) {
  95. const { participant, rtt } = action.e2eRtt;
  96. RTCStats.sendE2eRttData({
  97. remoteEndpointId: participant.getId(),
  98. rtt,
  99. remoteRegion: participant.getProperty('region')
  100. });
  101. }
  102. break;
  103. }
  104. case ADD_FACE_EXPRESSION: {
  105. if (canSendRtcstatsData(state)) {
  106. const { duration, faceExpression } = action;
  107. RTCStats.sendFaceExpressionData({
  108. duration,
  109. faceExpression
  110. });
  111. }
  112. break;
  113. }
  114. }
  115. return next(action);
  116. });