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 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // @flow
  2. import {
  3. CONFERENCE_JOINED,
  4. DATA_CHANNEL_OPENED
  5. } from '../base/conference';
  6. import { getParticipantCount } from '../base/participants';
  7. import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
  8. import { shouldDisplayTileView } from '../video-layout';
  9. import { setPreferredVideoQuality, setMaxReceiverVideoQuality } from './actions';
  10. import { VIDEO_QUALITY_LEVELS } from './constants';
  11. import { getReceiverVideoQualityLevel } from './functions';
  12. import logger from './logger';
  13. import { getMinHeightForQualityLvlMap } from './selector';
  14. /**
  15. * Implements the middleware of the feature video-quality.
  16. *
  17. * @param {Store} store - The redux store.
  18. * @returns {Function}
  19. */
  20. MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
  21. if (action.type === DATA_CHANNEL_OPENED) {
  22. return _syncReceiveVideoQuality(getState, next, action);
  23. }
  24. const result = next(action);
  25. switch (action.type) {
  26. case CONFERENCE_JOINED: {
  27. if (navigator.product === 'ReactNative') {
  28. const { resolution } = getState()['features/base/config'];
  29. if (typeof resolution !== 'undefined') {
  30. dispatch(setPreferredVideoQuality(Number.parseInt(resolution, 10)));
  31. logger.info(`Configured preferred receiver video frame height to: ${resolution}`);
  32. }
  33. }
  34. break;
  35. }
  36. }
  37. return result;
  38. });
  39. /**
  40. * Implements a state listener in order to calculate max receiver video quality.
  41. */
  42. StateListenerRegistry.register(
  43. /* selector */ state => {
  44. const { reducedUI } = state['features/base/responsive-ui'];
  45. const _shouldDisplayTileView = shouldDisplayTileView(state);
  46. const thumbnailSize = state['features/filmstrip']?.tileViewDimensions?.thumbnailSize;
  47. const participantCount = getParticipantCount(state);
  48. return {
  49. displayTileView: _shouldDisplayTileView,
  50. participantCount,
  51. reducedUI,
  52. thumbnailHeight: thumbnailSize?.height
  53. };
  54. },
  55. /* listener */ ({ displayTileView, participantCount, reducedUI, thumbnailHeight }, { dispatch, getState }) => {
  56. const state = getState();
  57. const { maxReceiverVideoQuality } = state['features/video-quality'];
  58. const { maxFullResolutionParticipants = 2 } = state['features/base/config'];
  59. let newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.HIGH;
  60. if (reducedUI) {
  61. newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.LOW;
  62. } else if (displayTileView && !Number.isNaN(thumbnailHeight)) {
  63. newMaxRecvVideoQuality = getReceiverVideoQualityLevel(thumbnailHeight, getMinHeightForQualityLvlMap(state));
  64. // Override HD level calculated for the thumbnail height when # of participants threshold is exceeded
  65. if (maxReceiverVideoQuality !== newMaxRecvVideoQuality && maxFullResolutionParticipants !== -1) {
  66. const override
  67. = participantCount > maxFullResolutionParticipants
  68. && newMaxRecvVideoQuality > VIDEO_QUALITY_LEVELS.STANDARD;
  69. logger.info(`Video quality level for thumbnail height: ${thumbnailHeight}, `
  70. + `is: ${newMaxRecvVideoQuality}, `
  71. + `override: ${String(override)}, `
  72. + `max full res N: ${maxFullResolutionParticipants}`);
  73. if (override) {
  74. newMaxRecvVideoQuality = VIDEO_QUALITY_LEVELS.STANDARD;
  75. }
  76. }
  77. }
  78. if (maxReceiverVideoQuality !== newMaxRecvVideoQuality) {
  79. dispatch(setMaxReceiverVideoQuality(newMaxRecvVideoQuality));
  80. }
  81. }, {
  82. deepEquals: true
  83. });
  84. /**
  85. * Helper function for updating the preferred receiver video constraint, based
  86. * on the user preference and the internal maximum.
  87. *
  88. * @param {JitsiConference} conference - The JitsiConference instance for the
  89. * current call.
  90. * @param {number} preferred - The user preferred max frame height.
  91. * @param {number} max - The maximum frame height the application should
  92. * receive.
  93. * @returns {void}
  94. */
  95. function _setReceiverVideoConstraint(conference, preferred, max) {
  96. if (conference) {
  97. const value = Math.min(preferred, max);
  98. conference.setReceiverVideoConstraint(value);
  99. logger.info(`setReceiverVideoConstraint: ${value}`);
  100. }
  101. }
  102. /**
  103. * Helper function for updating the preferred sender video constraint, based
  104. * on the user preference.
  105. *
  106. * @param {JitsiConference} conference - The JitsiConference instance for the
  107. * current call.
  108. * @param {number} preferred - The user preferred max frame height.
  109. * @returns {void}
  110. */
  111. function _setSenderVideoConstraint(conference, preferred) {
  112. if (conference) {
  113. conference.setSenderVideoConstraint(preferred)
  114. .catch(err => {
  115. logger.error(`Changing sender resolution to ${preferred} failed - ${err} `);
  116. });
  117. }
  118. }
  119. /**
  120. * Sets the maximum receive video quality.
  121. *
  122. * @param {Function} getState - The redux function which returns the current redux state.
  123. * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
  124. * specified {@code action} to the specified {@code store}.
  125. * @param {Action} action - The redux action {@code DATA_CHANNEL_STATUS_CHANGED}
  126. * which is being dispatched in the specified {@code store}.
  127. * @private
  128. * @returns {Object} The value returned by {@code next(action)}.
  129. */
  130. function _syncReceiveVideoQuality(getState, next, action) {
  131. const state = getState();
  132. const {
  133. conference
  134. } = state['features/base/conference'];
  135. const {
  136. maxReceiverVideoQuality,
  137. preferredVideoQuality
  138. } = state['features/video-quality'];
  139. _setReceiverVideoConstraint(
  140. conference,
  141. preferredVideoQuality,
  142. maxReceiverVideoQuality);
  143. return next(action);
  144. }
  145. /**
  146. * Registers a change handler for state['features/base/conference'] to update
  147. * the preferred video quality levels based on user preferred and internal
  148. * settings.
  149. */
  150. StateListenerRegistry.register(
  151. /* selector */ state => {
  152. const { conference } = state['features/base/conference'];
  153. const {
  154. maxReceiverVideoQuality,
  155. preferredVideoQuality
  156. } = state['features/video-quality'];
  157. return {
  158. conference,
  159. maxReceiverVideoQuality,
  160. preferredVideoQuality
  161. };
  162. },
  163. /* listener */ (currentState, store, previousState = {}) => {
  164. const {
  165. conference,
  166. maxReceiverVideoQuality,
  167. preferredVideoQuality
  168. } = currentState;
  169. const changedConference = conference !== previousState.conference;
  170. const changedPreferredVideoQuality = preferredVideoQuality !== previousState.preferredVideoQuality;
  171. const changedMaxVideoQuality = maxReceiverVideoQuality !== previousState.maxReceiverVideoQuality;
  172. if (changedConference || changedPreferredVideoQuality || changedMaxVideoQuality) {
  173. _setReceiverVideoConstraint(conference, preferredVideoQuality, maxReceiverVideoQuality);
  174. }
  175. if (changedConference || changedPreferredVideoQuality) {
  176. _setSenderVideoConstraint(conference, preferredVideoQuality);
  177. }
  178. });