您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

actions.any.js 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // @flow
  2. import type { Dispatch } from 'redux';
  3. import { MEDIA_TYPE } from '../base/media';
  4. import {
  5. getDominantSpeakerParticipant,
  6. getLocalParticipant,
  7. getLocalScreenShareParticipant,
  8. getPinnedParticipant,
  9. getRemoteParticipants,
  10. getVirtualScreenshareParticipantByOwnerId
  11. } from '../base/participants';
  12. import { isStageFilmstripAvailable } from '../filmstrip/functions';
  13. import { getAutoPinSetting } from '../video-layout';
  14. import {
  15. SELECT_LARGE_VIDEO_PARTICIPANT,
  16. SET_LARGE_VIDEO_DIMENSIONS,
  17. UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION
  18. } from './actionTypes';
  19. /**
  20. * Action to select the participant to be displayed in LargeVideo based on the
  21. * participant id provided. If a participant id is not provided, the LargeVideo
  22. * participant will be selected based on a variety of factors: If there is a
  23. * dominant or pinned speaker, or if there are remote tracks, etc.
  24. *
  25. * @param {string} participant - The participant id of the user that needs to be
  26. * displayed on the large video.
  27. * @returns {Function}
  28. */
  29. export function selectParticipantInLargeVideo(participant: ?string) {
  30. return (dispatch: Dispatch<any>, getState: Function) => {
  31. const state = getState();
  32. if (isStageFilmstripAvailable(state, 2)) {
  33. return;
  34. }
  35. // Keep Etherpad open.
  36. if (state['features/etherpad'].editing) {
  37. return;
  38. }
  39. const participantId = participant ?? _electParticipantInLargeVideo(state);
  40. const largeVideo = state['features/large-video'];
  41. const remoteScreenShares = state['features/video-layout'].remoteScreenShares;
  42. let latestScreenshareParticipantId;
  43. if (remoteScreenShares && remoteScreenShares.length) {
  44. latestScreenshareParticipantId = remoteScreenShares[remoteScreenShares.length - 1];
  45. }
  46. // When trying to auto pin screenshare, always select the endpoint even though it happens to be
  47. // the large video participant in redux (for the reasons listed above in the large video selection
  48. // logic above). The auto pin screenshare logic kicks in after the track is added
  49. // (which updates the large video participant and selects all endpoints because of the auto tile
  50. // view mode). If the screenshare endpoint is not among the forwarded endpoints from the bridge,
  51. // it needs to be selected again at this point.
  52. if (participantId !== largeVideo.participantId || participantId === latestScreenshareParticipantId) {
  53. dispatch({
  54. type: SELECT_LARGE_VIDEO_PARTICIPANT,
  55. participantId
  56. });
  57. }
  58. };
  59. }
  60. /**
  61. * Updates the currently seen resolution of the video displayed on large video.
  62. *
  63. * @param {number} resolution - The current resolution (height) of the video.
  64. * @returns {{
  65. * type: UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION,
  66. * resolution: number
  67. * }}
  68. */
  69. export function updateKnownLargeVideoResolution(resolution: number) {
  70. return {
  71. type: UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION,
  72. resolution
  73. };
  74. }
  75. /**
  76. * Sets the dimenstions of the large video in redux.
  77. *
  78. * @param {number} height - The height of the large video.
  79. * @param {number} width - The width of the large video.
  80. * @returns {{
  81. * type: SET_LARGE_VIDEO_DIMENSIONS,
  82. * height: number,
  83. * width: number
  84. * }}
  85. */
  86. export function setLargeVideoDimensions(height, width) {
  87. return {
  88. type: SET_LARGE_VIDEO_DIMENSIONS,
  89. height,
  90. width
  91. };
  92. }
  93. /**
  94. * Returns the most recent existing remote video track.
  95. *
  96. * @param {Track[]} tracks - All current tracks.
  97. * @private
  98. * @returns {(Track|undefined)}
  99. */
  100. function _electLastVisibleRemoteVideo(tracks) {
  101. // First we try to get most recent remote video track.
  102. for (let i = tracks.length - 1; i >= 0; --i) {
  103. const track = tracks[i];
  104. if (!track.local && track.mediaType === MEDIA_TYPE.VIDEO) {
  105. return track;
  106. }
  107. }
  108. }
  109. /**
  110. * Returns the identifier of the participant who is to be on the stage and
  111. * should be displayed in {@code LargeVideo}.
  112. *
  113. * @param {Object} state - The Redux state from which the participant to be
  114. * displayed in {@code LargeVideo} is to be elected.
  115. * @private
  116. * @returns {(string|undefined)}
  117. */
  118. function _electParticipantInLargeVideo(state) {
  119. // If a participant is pinned, they will be shown in the LargeVideo (regardless of whether they are local or
  120. // remote) when the filmstrip on stage is disabled.
  121. let participant = getPinnedParticipant(state);
  122. if (participant) {
  123. return participant.id;
  124. }
  125. const autoPinSetting = getAutoPinSetting();
  126. if (autoPinSetting) {
  127. // when the setting auto_pin_latest_screen_share is true as spot does, prioritize local screenshare
  128. if (autoPinSetting === true) {
  129. const localScreenShareParticipant = getLocalScreenShareParticipant(state);
  130. if (localScreenShareParticipant) {
  131. return localScreenShareParticipant.id;
  132. }
  133. }
  134. // Pick the most recent remote screenshare that was added to the conference.
  135. const remoteScreenShares = state['features/video-layout'].remoteScreenShares;
  136. if (remoteScreenShares?.length) {
  137. return remoteScreenShares[remoteScreenShares.length - 1];
  138. }
  139. }
  140. // Next, pick the dominant speaker (other than self).
  141. participant = getDominantSpeakerParticipant(state);
  142. if (participant && !participant.local) {
  143. // Return the screensharing participant id associated with this endpoint if multi-stream is enabled and
  144. // auto_pin_latest_screen_share setting is disabled.
  145. const screenshareParticipant = getVirtualScreenshareParticipantByOwnerId(state, participant.id);
  146. return screenshareParticipant?.id ?? participant.id;
  147. }
  148. // In case this is the local participant.
  149. participant = undefined;
  150. // Next, pick the most recent participant with video.
  151. const tracks = state['features/base/tracks'];
  152. const videoTrack = _electLastVisibleRemoteVideo(tracks);
  153. if (videoTrack) {
  154. return videoTrack.participantId;
  155. }
  156. // Last, select the participant that joined last (other than poltergist or other bot type participants).
  157. const participants = [ ...getRemoteParticipants(state).values() ];
  158. for (let i = participants.length; i > 0 && !participant; i--) {
  159. const p = participants[i - 1];
  160. !p.botType && (participant = p);
  161. }
  162. if (participant) {
  163. return participant.id;
  164. }
  165. return getLocalParticipant(state)?.id;
  166. }