Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

SendVideoController.js 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { getLogger } from '@jitsi/logger';
  2. import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
  3. import RTCEvents from '../../service/RTC/RTCEvents';
  4. import FeatureFlags from '../flags/FeatureFlags';
  5. import MediaSessionEvents from '../xmpp/MediaSessionEvents';
  6. const logger = getLogger(__filename);
  7. /**
  8. * The class manages send video constraints across media sessions({@link JingleSessionPC}) which belong to
  9. * {@link JitsiConference}. It finds the lowest common value, between the local user's send preference and
  10. * the remote party's receive preference. Also this module will consider only the active session's receive value,
  11. * because local tracks are shared and while JVB may have no preference, the remote p2p may have and they may be totally
  12. * different.
  13. */
  14. export default class SendVideoController {
  15. /**
  16. * Creates new instance for a given conference.
  17. *
  18. * @param {JitsiConference} conference - the conference instance for which the new instance will be managing
  19. * the send video quality constraints.
  20. * @param {RTC} rtc - the rtc instance that is responsible for sending the messages on the bridge channel.
  21. */
  22. constructor(conference, rtc) {
  23. this._conference = conference;
  24. this._rtc = rtc;
  25. /**
  26. * Source name based sender constraints.
  27. * @type {Map<string, number>};
  28. */
  29. this._sourceSenderConstraints = new Map();
  30. this._conference.on(
  31. JitsiConferenceEvents._MEDIA_SESSION_STARTED,
  32. session => this._onMediaSessionStarted(session));
  33. this._conference.on(
  34. JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED,
  35. () => this._propagateSendMaxFrameHeight());
  36. this._rtc.on(
  37. RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED,
  38. videoConstraints => this._onSenderConstraintsReceived(videoConstraints));
  39. }
  40. /**
  41. * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media
  42. * session. It doesn't mean it's already active though. For example the JVB connection may be created after
  43. * the conference has entered the p2p mode already.
  44. *
  45. * @param {JingleSessionPC} mediaSession - the started media session.
  46. * @private
  47. */
  48. _onMediaSessionStarted(mediaSession) {
  49. mediaSession.addListener(
  50. MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED,
  51. session => {
  52. if (session === this._conference.getActiveMediaSession()) {
  53. this._propagateSendMaxFrameHeight();
  54. }
  55. });
  56. }
  57. /**
  58. * Propagates the video constraints if they have changed.
  59. *
  60. * @param {Object} videoConstraints - The sender video constraints received from the bridge.
  61. */
  62. _onSenderConstraintsReceived(videoConstraints) {
  63. if (FeatureFlags.isSourceNameSignalingEnabled()) {
  64. const { idealHeight, sourceName } = videoConstraints;
  65. const localVideoTracks = this._conference.getLocalVideoTracks() ?? [];
  66. for (const track of localVideoTracks) {
  67. // Propagate the sender constraint only if it has changed.
  68. if (track.getSourceName() === sourceName
  69. && (!this._sourceSenderConstraints.has(sourceName)
  70. || this._sourceSenderConstraints.get(sourceName) !== idealHeight)) {
  71. this._sourceSenderConstraints.set(sourceName, idealHeight);
  72. logger.debug(`Sender constraints for source:${sourceName} changed to idealHeight:${idealHeight}`);
  73. this._propagateSendMaxFrameHeight(sourceName);
  74. }
  75. }
  76. } else if (this._senderVideoConstraints?.idealHeight !== videoConstraints.idealHeight) {
  77. this._senderVideoConstraints = videoConstraints;
  78. this._propagateSendMaxFrameHeight();
  79. }
  80. }
  81. /**
  82. * Figures out the send video constraint as specified by {@link selectSendMaxFrameHeight} and sets it on all media
  83. * sessions for the reasons mentioned in this class description.
  84. *
  85. * @param {string} sourceName - The source for which sender constraints have changed.
  86. * @returns {Promise<void[]>}
  87. * @private
  88. */
  89. _propagateSendMaxFrameHeight(sourceName = null) {
  90. if (FeatureFlags.isSourceNameSignalingEnabled() && !sourceName) {
  91. throw new Error('sourceName missing for calculating the sendMaxHeight for video tracks');
  92. }
  93. const sendMaxFrameHeight = this.selectSendMaxFrameHeight(sourceName);
  94. const promises = [];
  95. if (sendMaxFrameHeight >= 0) {
  96. for (const session of this._conference.getMediaSessions()) {
  97. promises.push(session.setSenderVideoConstraint(sendMaxFrameHeight, sourceName));
  98. }
  99. }
  100. return Promise.all(promises);
  101. }
  102. /**
  103. * Selects the lowest common value for the local video send constraint by looking at local user's preference and
  104. * the active media session's receive preference set by the remote party.
  105. *
  106. * @param {string} sourceName - The source for which sender constraints have changed.
  107. * @returns {number|undefined}
  108. */
  109. selectSendMaxFrameHeight(sourceName = null) {
  110. if (FeatureFlags.isSourceNameSignalingEnabled() && !sourceName) {
  111. throw new Error('sourceName missing for calculating the sendMaxHeight for video tracks');
  112. }
  113. const activeMediaSession = this._conference.getActiveMediaSession();
  114. const remoteRecvMaxFrameHeight = activeMediaSession
  115. ? activeMediaSession.isP2P
  116. ? activeMediaSession.getRemoteRecvMaxFrameHeight()
  117. : sourceName ? this._sourceSenderConstraints.get(sourceName) : this._senderVideoConstraints?.idealHeight
  118. : undefined;
  119. if (this._preferredSendMaxFrameHeight >= 0 && remoteRecvMaxFrameHeight >= 0) {
  120. return Math.min(this._preferredSendMaxFrameHeight, remoteRecvMaxFrameHeight);
  121. } else if (remoteRecvMaxFrameHeight >= 0) {
  122. return remoteRecvMaxFrameHeight;
  123. }
  124. return this._preferredSendMaxFrameHeight;
  125. }
  126. /**
  127. * Sets local preference for max send video frame height.
  128. *
  129. * @param {number} maxFrameHeight - the new value to set.
  130. * @returns {Promise<void[]>} - resolved when the operation is complete.
  131. */
  132. setPreferredSendMaxFrameHeight(maxFrameHeight) {
  133. this._preferredSendMaxFrameHeight = maxFrameHeight;
  134. if (FeatureFlags.isSourceNameSignalingEnabled()) {
  135. const promises = [];
  136. for (const sourceName of this._sourceSenderConstraints.keys()) {
  137. promises.push(this._propagateSendMaxFrameHeight(sourceName));
  138. }
  139. return Promise.allSettled(promises);
  140. }
  141. return this._propagateSendMaxFrameHeight();
  142. }
  143. }