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

CodecSelection.js 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import { getLogger } from 'jitsi-meet-logger';
  2. import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
  3. import CodecMimeType from '../../service/RTC/CodecMimeType';
  4. import * as MediaType from '../../service/RTC/MediaType';
  5. import browser from '../browser';
  6. const logger = getLogger(__filename);
  7. /**
  8. * This class handles the codec selection mechanism for the conference based on the config.js settings.
  9. * The preferred codec is selected based on the settings and the list of codecs supported by the browser.
  10. * The preferred codec is published in presence which is then used by the other endpoints in the
  11. * conference to pick a supported codec at join time and when the call transitions between p2p and jvb
  12. * connections.
  13. */
  14. export class CodecSelection {
  15. /**
  16. * Creates a new instance for a given conference.
  17. *
  18. * @param {JitsiConference} conference the conference instance
  19. * @param {*} options
  20. * @param {string} options.disabledCodec the codec that needs to be disabled.
  21. * @param {boolean} options.enforcePreferredCodec whether codec preference has to be
  22. * enforced even when an endpoints that doesn't support the preferred codec joins the call.
  23. * Falling back to the standard codec will be skipped when this option is true, endpoints
  24. * that do not support the preferred codec may not be able to encode/decode video when this happens.
  25. * @param {string} options.jvbCodec the codec that is preferred on jvb connection.
  26. * @param {string} options.p2pCodec the codec that is preferred on p2p connection.
  27. */
  28. constructor(conference, options) {
  29. this.conference = conference;
  30. this.options = options;
  31. // VP8 cannot be disabled and it will be the default codec when no preference is set.
  32. this.disabledCodec = options.disabledCodec === CodecMimeType.VP8
  33. ? undefined
  34. : this._getCodecMimeType(options.disabledCodec);
  35. // Check if the codec values passed are valid.
  36. const jvbCodec = this._getCodecMimeType(options.jvbCodec);
  37. const p2pCodec = this._getCodecMimeType(options.p2pCodec);
  38. this.jvbPreferredCodec = jvbCodec && this._isCodecSupported(jvbCodec) ? jvbCodec : CodecMimeType.VP8;
  39. this.p2pPreferredCodec = p2pCodec && this._isCodecSupported(p2pCodec) ? p2pCodec : CodecMimeType.VP8;
  40. logger.debug(`Codec preferences for the conference are JVB: ${this.jvbPreferredCodec},
  41. P2P: ${this.p2pPreferredCodec}`);
  42. // Do not prefer VP9 on Firefox because of the following bug.
  43. // https://bugzilla.mozilla.org/show_bug.cgi?id=1633876
  44. if (browser.isFirefox() && this.jvbPreferredCodec === CodecMimeType.VP9) {
  45. this.jvbPreferredCodec = CodecMimeType.VP8;
  46. }
  47. // Keep a list of participants that join the call with a non-preferred codec.
  48. // The call is upgraded to the preferred codec once that list is empty.
  49. this.nonPreferredParticipants = [];
  50. this.conference.on(
  51. JitsiConferenceEvents.USER_JOINED,
  52. this._onParticipantJoined.bind(this));
  53. this.conference.on(
  54. JitsiConferenceEvents.USER_LEFT,
  55. this._onParticipantLeft.bind(this));
  56. this.conference.on(
  57. JitsiConferenceEvents._MEDIA_SESSION_STARTED,
  58. session => this._onMediaSessionStared(session));
  59. }
  60. /**
  61. * Checks if a given string is a valid video codec mime type.
  62. *
  63. * @param {string} codec the codec string that needs to be validated.
  64. * @returns {CodecMimeType|null} mime type if valid, null otherwise.
  65. * @private
  66. */
  67. _getCodecMimeType(codec) {
  68. if (typeof codec === 'string') {
  69. return Object.values(CodecMimeType).find(value => value === codec.toLowerCase());
  70. }
  71. return null;
  72. }
  73. /**
  74. * Checks if the given codec is supported by the browser.
  75. *
  76. * @param {CodecMimeType} preferredCodec codec to be checked.
  77. * @returns {boolean} true if the given codec is supported, false otherwise.
  78. * @private
  79. */
  80. _isCodecSupported(preferredCodec) {
  81. // Skip the check on FF and RN because they do not support the getCapabilities API.
  82. // It is safe to assume both of them support all the codecs supported by Chrome.
  83. if (browser.isFirefox() || browser.isReactNative()) {
  84. return true;
  85. }
  86. return window.RTCRtpReceiver
  87. && window.RTCRtpReceiver.getCapabilities('video').codecs
  88. .some(codec => codec.mimeType.toLowerCase() === `video/${preferredCodec}`);
  89. }
  90. /**
  91. * Handles the {@link JitsiConferenceEvents._MEDIA_SESSION_STARTED} event. Codecs need to be
  92. * configured on the media session that is newly created.
  93. *
  94. * @param {JingleSessionPC} mediaSession media session that started.
  95. * @returns {void}
  96. * @private
  97. */
  98. _onMediaSessionStared(mediaSession) {
  99. const preferredCodec = mediaSession.isP2P ? this.p2pPreferredCodec : this.jvbPreferredCodec;
  100. const disabledCodec = this.disabledCodec && this._isCodecSupported(this.disabledCodec)
  101. ? this.disabledCodec
  102. : null;
  103. mediaSession.setVideoCodecs(preferredCodec, disabledCodec);
  104. }
  105. /**
  106. * Handles the {@link JitsiConferenceEvents.USER_JOINED} event. When a new user joins the call,
  107. * the codec types are compared and the codec configued on the peerconnection is updated when
  108. * needed.
  109. *
  110. * @param {string} id endpoint id of the newly joined user.
  111. * @returns {void}
  112. * @private
  113. */
  114. _onParticipantJoined(id) {
  115. const session = this.conference.jvbJingleSession;
  116. if (session && !this.options.enforcePreferredCodec) {
  117. const peerMediaInfo = session.signalingLayer.getPeerMediaInfo(id, MediaType.VIDEO);
  118. if (!peerMediaInfo) {
  119. return;
  120. }
  121. const newCodec = peerMediaInfo.codecType;
  122. const currentCodec = session.getConfiguredVideoCodec();
  123. if (newCodec
  124. && newCodec !== this.jvbPreferredCodec
  125. && newCodec !== currentCodec
  126. && this._isCodecSupported(newCodec)) {
  127. // Add the participant to the list of participants that don't support the preferred codec.
  128. this.nonPreferredParticipants.push(id);
  129. session.setVideoCodecs(newCodec);
  130. }
  131. }
  132. }
  133. /**
  134. * Handles the {@link JitsiConferenceEvents.USER_LEFT} event. When a user leaves the call,
  135. * the codec configured on the peerconnection is updated to the preferred codec if all the
  136. * users that do not support the preferred codec have left the call.
  137. *
  138. * @param {string} id endpoint id of the user that has left the call.
  139. * @returns {void}
  140. * @private
  141. */
  142. _onParticipantLeft(id) {
  143. const session = this.conference.jvbJingleSession;
  144. if (session && !this.options.enforcePreferredCodec) {
  145. const index = this.nonPreferredParticipants.findIndex(participantId => participantId === id);
  146. if (index > -1) {
  147. this.nonPreferredParticipants.splice(index, 1);
  148. }
  149. // If all the participants that have joined the conference with a
  150. // non-preferred codec have left, switch to the preferred codec.
  151. if (!this.nonPreferredParticipants.length) {
  152. session.setVideoCodecs(this.jvbPreferredCodec);
  153. }
  154. }
  155. }
  156. /**
  157. * Returns the preferred codec for the conference.
  158. *
  159. * @returns {CodecMimeType} preferred codec.
  160. */
  161. getPreferredCodec() {
  162. return this.conference.isP2PActive() ? this.p2pPreferredCodec : this.jvbPreferredCodec;
  163. }
  164. }