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

ReceiveVideoController.js 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import { getLogger } from '@jitsi/logger';
  2. import isEqual from 'lodash.isequal';
  3. import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
  4. import { MediaType } from '../../service/RTC/MediaType';
  5. const logger = getLogger(__filename);
  6. const MAX_HEIGHT = 2160;
  7. const LASTN_UNLIMITED = -1;
  8. /**
  9. * This class translates the legacy signaling format between the client and the bridge (that affects bandwidth
  10. * allocation) to the new format described here https://github.com/jitsi/jitsi-videobridge/blob/master/doc/allocation.md
  11. */
  12. class ReceiverVideoConstraints {
  13. /**
  14. * Creates a new instance.
  15. *
  16. * @param {number} lastN - Number of videos to be requested from the bridge.
  17. */
  18. constructor(lastN) {
  19. // The number of videos requested from the bridge.
  20. this._lastN = lastN ?? LASTN_UNLIMITED;
  21. // The number representing the maximum video height the local client should receive from the bridge/peer.
  22. this._maxFrameHeight = MAX_HEIGHT;
  23. this._receiverVideoConstraints = {
  24. constraints: {},
  25. defaultConstraints: { 'maxHeight': this._maxFrameHeight },
  26. lastN: this._lastN
  27. };
  28. }
  29. /**
  30. * Returns the receiver video constraints that need to be sent on the bridge channel or to the remote peer.
  31. */
  32. get constraints() {
  33. this._receiverVideoConstraints.lastN = this._lastN;
  34. if (Object.keys(this._receiverVideoConstraints.constraints)?.length) {
  35. /* eslint-disable no-unused-vars */
  36. for (const [ key, value ] of Object.entries(this._receiverVideoConstraints.constraints)) {
  37. value.maxHeight = this._maxFrameHeight;
  38. }
  39. } else {
  40. this._receiverVideoConstraints.defaultConstraints = { 'maxHeight': this._maxFrameHeight };
  41. }
  42. return this._receiverVideoConstraints;
  43. }
  44. /**
  45. * Updates the lastN field of the ReceiverVideoConstraints sent to the bridge.
  46. *
  47. * @param {number} value
  48. * @returns {boolean} Returns true if the the value has been updated, false otherwise.
  49. */
  50. updateLastN(value) {
  51. const changed = this._lastN !== value;
  52. if (changed) {
  53. this._lastN = value;
  54. logger.debug(`Updating ReceiverVideoConstraints lastN(${value})`);
  55. }
  56. return changed;
  57. }
  58. /**
  59. * Updates the resolution (height requested) in the contraints field of the ReceiverVideoConstraints
  60. * sent to the bridge.
  61. *
  62. * @param {number} maxFrameHeight
  63. * @requires {boolean} Returns true if the the value has been updated, false otherwise.
  64. */
  65. updateReceiveResolution(maxFrameHeight) {
  66. const changed = this._maxFrameHeight !== maxFrameHeight;
  67. if (changed) {
  68. this._maxFrameHeight = maxFrameHeight;
  69. logger.debug(`Updating receive maxFrameHeight: ${maxFrameHeight}`);
  70. }
  71. return changed;
  72. }
  73. /**
  74. * Updates the receiver constraints sent to the bridge.
  75. *
  76. * @param {Object} videoConstraints
  77. * @returns {boolean} Returns true if the the value has been updated, false otherwise.
  78. */
  79. updateReceiverVideoConstraints(videoConstraints) {
  80. const changed = !isEqual(this._receiverVideoConstraints, videoConstraints);
  81. if (changed) {
  82. this._receiverVideoConstraints = videoConstraints;
  83. logger.debug(`Updating ReceiverVideoConstraints ${JSON.stringify(videoConstraints)}`);
  84. }
  85. return changed;
  86. }
  87. }
  88. /**
  89. * This class manages the receive video contraints for a given {@link JitsiConference}. These constraints are
  90. * determined by the application based on how the remote video streams need to be displayed. This class is responsible
  91. * for communicating these constraints to the bridge over the bridge channel.
  92. */
  93. export default class ReceiveVideoController {
  94. /**
  95. * Creates a new instance for a given conference.
  96. *
  97. * @param {JitsiConference} conference the conference instance for which the new instance will be managing
  98. * the receive video quality constraints.
  99. * @param {RTC} rtc the rtc instance which is responsible for initializing the bridge channel.
  100. */
  101. constructor(conference, rtc) {
  102. this._conference = conference;
  103. this._rtc = rtc;
  104. const { config } = conference.options;
  105. // The number of videos requested from the bridge, -1 represents unlimited or all available videos.
  106. this._lastN = config?.startLastN ?? (config?.channelLastN || LASTN_UNLIMITED);
  107. // The number representing the maximum video height the local client should receive from the bridge.
  108. this._maxFrameHeight = MAX_HEIGHT;
  109. /**
  110. * The map that holds the max frame height requested per remote source for p2p connection.
  111. *
  112. * @type Map<string, number>
  113. */
  114. this._sourceReceiverConstraints = new Map();
  115. // The default receiver video constraints.
  116. this._receiverVideoConstraints = new ReceiverVideoConstraints(this._lastN);
  117. this._conference.on(
  118. JitsiConferenceEvents._MEDIA_SESSION_STARTED,
  119. session => this._onMediaSessionStarted(session));
  120. }
  121. /**
  122. * Returns a map of all the remote source names and the corresponding max frame heights.
  123. *
  124. * @param {JingleSessionPC} mediaSession - the media session.
  125. * @param {number} maxFrameHeight - the height to be requested for remote sources.
  126. * @returns
  127. */
  128. _getDefaultSourceReceiverConstraints(mediaSession, maxFrameHeight) {
  129. const height = maxFrameHeight ?? MAX_HEIGHT;
  130. const remoteVideoTracks = mediaSession.peerconnection?.getRemoteTracks(null, MediaType.VIDEO) || [];
  131. const receiverConstraints = new Map();
  132. for (const track of remoteVideoTracks) {
  133. receiverConstraints.set(track.getSourceName(), height);
  134. }
  135. return receiverConstraints;
  136. }
  137. /**
  138. * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media
  139. * session. The preferred receive frameHeight is applied on the media session.
  140. *
  141. * @param {JingleSessionPC} mediaSession - the started media session.
  142. * @returns {void}
  143. * @private
  144. */
  145. _onMediaSessionStarted(mediaSession) {
  146. if (mediaSession.isP2P) {
  147. mediaSession.setReceiverVideoConstraint(this._getDefaultSourceReceiverConstraints(mediaSession));
  148. } else {
  149. this._rtc.setReceiverVideoConstraints(this._receiverVideoConstraints.constraints);
  150. }
  151. }
  152. /**
  153. * Returns the lastN value for the conference.
  154. *
  155. * @returns {number}
  156. */
  157. getLastN() {
  158. return this._lastN;
  159. }
  160. /**
  161. * Selects a new value for "lastN". The requested amount of videos are going to be delivered after the value is
  162. * in effect. Set to -1 for unlimited or all available videos.
  163. *
  164. * @param {number} value the new value for lastN.
  165. * @returns {void}
  166. */
  167. setLastN(value) {
  168. if (this._lastN !== value) {
  169. this._lastN = value;
  170. if (this._receiverVideoConstraints.updateLastN(value)) {
  171. this._rtc.setReceiverVideoConstraints(this._receiverVideoConstraints.constraints);
  172. }
  173. }
  174. }
  175. /**
  176. * Sets the maximum video resolution the local participant should receive from remote participants.
  177. *
  178. * @param {number|undefined} maxFrameHeight - the new value.
  179. * @returns {void}
  180. */
  181. setPreferredReceiveMaxFrameHeight(maxFrameHeight) {
  182. this._maxFrameHeight = maxFrameHeight;
  183. for (const session of this._conference.getMediaSessions()) {
  184. if (session.isP2P) {
  185. session.setReceiverVideoConstraint(this._getDefaultSourceReceiverConstraints(session, maxFrameHeight));
  186. } else if (this._receiverVideoConstraints.updateReceiveResolution(maxFrameHeight)) {
  187. this._rtc.setReceiverVideoConstraints(this._receiverVideoConstraints.constraints);
  188. }
  189. }
  190. }
  191. /**
  192. * Sets the receiver constraints for the conference.
  193. *
  194. * @param {Object} constraints The video constraints.
  195. */
  196. setReceiverConstraints(constraints) {
  197. if (!constraints) {
  198. return;
  199. }
  200. const isEndpointsFormat = Object.keys(constraints).includes('onStageEndpoints', 'selectedEndpoints');
  201. if (isEndpointsFormat) {
  202. throw new Error(
  203. '"onStageEndpoints" and "selectedEndpoints" are not supported when sourceNameSignaling is enabled.'
  204. );
  205. }
  206. const constraintsChanged = this._receiverVideoConstraints.updateReceiverVideoConstraints(constraints);
  207. if (constraintsChanged) {
  208. this._lastN = constraints.lastN ?? this._lastN;
  209. // Send the contraints on the bridge channel.
  210. this._rtc.setReceiverVideoConstraints(constraints);
  211. const p2pSession = this._conference.getMediaSessions().find(session => session.isP2P);
  212. if (!p2pSession) {
  213. return;
  214. }
  215. const mappedConstraints = Array.from(Object.entries(constraints.constraints))
  216. .map(constraint => {
  217. constraint[1] = constraint[1].maxHeight;
  218. return constraint;
  219. });
  220. this._sourceReceiverConstraints = new Map(mappedConstraints);
  221. // Send the receiver constraints to the peer through a "content-modify" message.
  222. p2pSession.setReceiverVideoConstraint(this._sourceReceiverConstraints);
  223. }
  224. }
  225. }