選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

subscriber.js 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // @flow
  2. import debounce from 'lodash/debounce';
  3. import { _handleParticipantError } from '../base/conference';
  4. import { getParticipantCount } from '../base/participants';
  5. import { StateListenerRegistry } from '../base/redux';
  6. import { reportError } from '../base/util';
  7. import { shouldDisplayTileView } from '../video-layout';
  8. import { setMaxReceiverVideoQuality } from './actions';
  9. import { VIDEO_QUALITY_LEVELS } from './constants';
  10. import { getReceiverVideoQualityLevel } from './functions';
  11. import logger from './logger';
  12. import { getMinHeightForQualityLvlMap } from './selector';
  13. declare var APP: Object;
  14. /**
  15. * StateListenerRegistry provides a reliable way of detecting changes to selected
  16. * endpoints state and dispatching additional actions. The listener is debounced
  17. * so that the client doesn't end up sending too many bridge messages when the user is
  18. * scrolling through the thumbnails prompting updates to the selected endpoints.
  19. */
  20. StateListenerRegistry.register(
  21. /* selector */ state => state['features/video-layout'].selectedEndpoints,
  22. /* listener */ debounce((selectedEndpoints, store) => {
  23. _updateReceiverVideoConstraints(store);
  24. }, 1000));
  25. /**
  26. * StateListenerRegistry provides a reliable way of detecting changes to
  27. * lastn state and dispatching additional actions.
  28. */
  29. StateListenerRegistry.register(
  30. /* selector */ state => state['features/base/lastn'].lastN,
  31. /* listener */ (lastN, store) => {
  32. _updateReceiverVideoConstraints(store);
  33. });
  34. /**
  35. * StateListenerRegistry provides a reliable way of detecting changes to
  36. * maxReceiverVideoQuality and preferredVideoQuality state and dispatching additional actions.
  37. */
  38. StateListenerRegistry.register(
  39. /* selector */ state => {
  40. const {
  41. maxReceiverVideoQuality,
  42. preferredVideoQuality
  43. } = state['features/video-quality'];
  44. return {
  45. maxReceiverVideoQuality,
  46. preferredVideoQuality
  47. };
  48. },
  49. /* listener */ (currentState, store, previousState = {}) => {
  50. const { maxReceiverVideoQuality, preferredVideoQuality } = currentState;
  51. const changedPreferredVideoQuality = preferredVideoQuality !== previousState.preferredVideoQuality;
  52. const changedReceiverVideoQuality = maxReceiverVideoQuality !== previousState.maxReceiverVideoQuality;
  53. if (changedPreferredVideoQuality) {
  54. _setSenderVideoConstraint(preferredVideoQuality, store);
  55. typeof APP !== 'undefined' && APP.API.notifyVideoQualityChanged(preferredVideoQuality);
  56. }
  57. changedReceiverVideoQuality && _updateReceiverVideoConstraints(store);
  58. });
  59. /**
  60. * Implements a state listener in order to calculate max receiver video quality.
  61. */
  62. StateListenerRegistry.register(
  63. /* selector */ state => {
  64. const { reducedUI } = state['features/base/responsive-ui'];
  65. const _shouldDisplayTileView = shouldDisplayTileView(state);
  66. const thumbnailSize = state['features/filmstrip']?.tileViewDimensions?.thumbnailSize;
  67. const participantCount = getParticipantCount(state);
  68. return {
  69. displayTileView: _shouldDisplayTileView,
  70. participantCount,
  71. reducedUI,
  72. thumbnailHeight: thumbnailSize?.height
  73. };
  74. },
  75. /* listener */ ({ displayTileView, participantCount, reducedUI, thumbnailHeight }, { dispatch, getState }) => {
  76. const state = getState();
  77. const { maxReceiverVideoQuality } = state['features/video-quality'];
  78. const { maxFullResolutionParticipants = 2 } = state['features/base/config'];
  79. let newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.ULTRA;
  80. if (reducedUI) {
  81. newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.LOW;
  82. } else if (displayTileView && !Number.isNaN(thumbnailHeight)) {
  83. newMaxRecvVideoQuality = getReceiverVideoQualityLevel(thumbnailHeight, getMinHeightForQualityLvlMap(state));
  84. // Override HD level calculated for the thumbnail height when # of participants threshold is exceeded
  85. if (maxReceiverVideoQuality !== newMaxRecvVideoQuality && maxFullResolutionParticipants !== -1) {
  86. const override
  87. = participantCount > maxFullResolutionParticipants
  88. && newMaxRecvVideoQuality > VIDEO_QUALITY_LEVELS.STANDARD;
  89. logger.info(`Video quality level for thumbnail height: ${thumbnailHeight}, `
  90. + `is: ${newMaxRecvVideoQuality}, `
  91. + `override: ${String(override)}, `
  92. + `max full res N: ${maxFullResolutionParticipants}`);
  93. if (override) {
  94. newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.STANDARD;
  95. }
  96. }
  97. }
  98. if (maxReceiverVideoQuality !== newMaxRecvVideoQuality) {
  99. dispatch(setMaxReceiverVideoQuality(newMaxRecvVideoQuality));
  100. }
  101. }, {
  102. deepEquals: true
  103. });
  104. /**
  105. * Helper function for updating the preferred sender video constraint, based on the user preference.
  106. *
  107. * @param {number} preferred - The user preferred max frame height.
  108. * @returns {void}
  109. */
  110. function _setSenderVideoConstraint(preferred, { getState }) {
  111. const state = getState();
  112. const { conference } = state['features/base/conference'];
  113. if (!conference) {
  114. return;
  115. }
  116. logger.info(`Setting sender resolution to ${preferred}`);
  117. conference.setSenderVideoConstraint(preferred)
  118. .catch(error => {
  119. _handleParticipantError(error);
  120. reportError(error, `Changing sender resolution to ${preferred} failed.`);
  121. });
  122. }
  123. /**
  124. * Private helper to calculate the receiver video constraints and set them on the bridge channel.
  125. *
  126. * @param {*} store - The redux store.
  127. * @returns {void}
  128. */
  129. function _updateReceiverVideoConstraints({ getState }) {
  130. const state = getState();
  131. const { conference } = state['features/base/conference'];
  132. if (!conference) {
  133. return;
  134. }
  135. const { lastN } = state['features/base/lastn'];
  136. const { maxReceiverVideoQuality, preferredVideoQuality } = state['features/video-quality'];
  137. const { selectedEndpoints } = state['features/video-layout'];
  138. const maxFrameHeight = Math.min(maxReceiverVideoQuality, preferredVideoQuality);
  139. const receiverConstraints = {
  140. constraints: {},
  141. defaultConstraints: { 'maxHeight': VIDEO_QUALITY_LEVELS.LOW },
  142. lastN,
  143. onStageEndpoints: [],
  144. selectedEndpoints: []
  145. };
  146. if (!selectedEndpoints?.length) {
  147. return;
  148. }
  149. // Stage view.
  150. if (selectedEndpoints?.length === 1) {
  151. receiverConstraints.constraints[selectedEndpoints[0]] = { 'maxHeight': maxFrameHeight };
  152. receiverConstraints.onStageEndpoints = selectedEndpoints;
  153. // Tile view.
  154. } else {
  155. receiverConstraints.defaultConstraints = { 'maxHeight': maxFrameHeight };
  156. }
  157. logger.info(`Setting receiver video constraints to ${JSON.stringify(receiverConstraints)}`);
  158. try {
  159. conference.setReceiverConstraints(receiverConstraints);
  160. } catch (error) {
  161. _handleParticipantError(error);
  162. reportError(error, `Failed to set receiver video constraints ${JSON.stringify(receiverConstraints)}`);
  163. }
  164. }