Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

CodecSelection.js 7.7KB

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. // VP8 cannot be disabled and it will be the default codec when no preference is set.
  31. this.disabledCodec = options.disabledCodec === CodecMimeType.VP8
  32. ? undefined
  33. : this._getCodecMimeType(options.disabledCodec);
  34. // Check if the codec values passed are valid.
  35. const jvbCodec = this._getCodecMimeType(options.jvbCodec);
  36. const p2pCodec = this._getCodecMimeType(options.p2pCodec);
  37. this.jvbPreferredCodec = jvbCodec && this._isCodecSupported(jvbCodec) ? jvbCodec : CodecMimeType.VP8;
  38. this.p2pPreferredCodec = p2pCodec && this._isCodecSupported(p2pCodec) ? p2pCodec : CodecMimeType.VP8;
  39. this.enforcePreferredCodec = options.enforcePreferredCodec;
  40. // Do not prefer VP9 on Firefox because of the following bug.
  41. // https://bugzilla.mozilla.org/show_bug.cgi?id=1633876
  42. if (browser.isFirefox() && this.jvbPreferredCodec === CodecMimeType.VP9) {
  43. this.jvbPreferredCodec = CodecMimeType.VP8;
  44. }
  45. // Keep a list of participants that join the call with a non-preferred codec.
  46. // The call is upgraded to the preferred codec once that list is empty.
  47. this.nonPreferredParticipants = [];
  48. this.conference.on(
  49. JitsiConferenceEvents.USER_JOINED,
  50. this._onParticipantJoined.bind(this));
  51. this.conference.on(
  52. JitsiConferenceEvents.USER_LEFT,
  53. this._onParticipantLeft.bind(this));
  54. this.conference.on(
  55. JitsiConferenceEvents._MEDIA_SESSION_STARTED,
  56. session => this._onMediaSessionStared(session));
  57. }
  58. /**
  59. * Checks if a given string is a valid video codec mime type.
  60. *
  61. * @param {string} codec the codec string that needs to be validated.
  62. * @returns {CodecMimeType|null} mime type if valid, null otherwise.
  63. * @private
  64. */
  65. _getCodecMimeType(codec) {
  66. if (typeof codec === 'string') {
  67. return Object.values(CodecMimeType).find(value => value === codec.toLowerCase());
  68. }
  69. return null;
  70. }
  71. /**
  72. * Checks if the given codec is supported by the browser.
  73. *
  74. * @param {CodecMimeType} preferredCodec codec to be checked.
  75. * @returns {boolean} true if the given codec is supported, false otherwise.
  76. * @private
  77. */
  78. _isCodecSupported(preferredCodec) {
  79. // Skip the check on FF and RN because they do not support the getCapabilities API.
  80. // It is safe to assume both of them support all the codecs supported by Chrome.
  81. if (browser.isFirefox() || browser.isReactNative()) {
  82. return true;
  83. }
  84. return window.RTCRtpReceiver
  85. && window.RTCRtpReceiver.getCapabilities('video').codecs
  86. .some(codec => codec.mimeType.toLowerCase() === `video/${preferredCodec}`);
  87. }
  88. /**
  89. * Handles the {@link JitsiConferenceEvents._MEDIA_SESSION_STARTED} event. Codecs need to be
  90. * configured on the media session that is newly created.
  91. *
  92. * @param {JingleSessionPC} mediaSession media session that started.
  93. * @returns {void}
  94. * @private
  95. */
  96. _onMediaSessionStared(mediaSession) {
  97. const preferredCodec = mediaSession.isP2P ? this.p2pPreferredCodec : this.jvbPreferredCodec;
  98. const disabledCodec = this.disabledCodec && this._isCodecSupported(this.disabledCodec)
  99. ? this.disabledCodec
  100. : null;
  101. mediaSession.setVideoCodecs(preferredCodec, disabledCodec);
  102. }
  103. /**
  104. * Handles the {@link JitsiConferenceEvents.USER_JOINED} event. When a new user joins the call,
  105. * the codec types are compared and the codec configued on the peerconnection is updated when
  106. * needed.
  107. *
  108. * @param {string} id endpoint id of the newly joined user.
  109. * @returns {void}
  110. * @private
  111. */
  112. _onParticipantJoined(id) {
  113. const session = this.conference.jvbJingleSession;
  114. if (session && !this.enforcePreferredCodec) {
  115. const peerMediaInfo = session.signalingLayer.getPeerMediaInfo(id, MediaType.VIDEO);
  116. if (peerMediaInfo) {
  117. const newCodec = peerMediaInfo.codecType;
  118. const currentCodec = session.getConfiguredVideoCodec();
  119. // Add the participant to the list of participants that
  120. // don't support the preferred codec.
  121. if (newCodec !== this.jvbPreferredCodec) {
  122. this.nonPreferredParticipants.push(id);
  123. }
  124. logger.warn(`Current: ${currentCodec}, new: ${newCodec}`);
  125. if (newCodec
  126. && newCodec !== this.jvbPreferredCodec
  127. && newCodec !== currentCodec
  128. && this._isCodecSupported(newCodec)) {
  129. session.setVideoCodecs(newCodec);
  130. }
  131. }
  132. }
  133. }
  134. /**
  135. * Handles the {@link JitsiConferenceEvents.USER_LEFT} event. When a user leaves the call,
  136. * the codec configured on the peerconnection is updated to the preferred codec if all the
  137. * users that do not support the preferred codec have left the call.
  138. *
  139. * @param {string} id endpoint id of the user that has left the call.
  140. * @returns {void}
  141. * @private
  142. */
  143. _onParticipantLeft(id) {
  144. const session = this.conference.jvbJingleSession;
  145. if (session && !this.enforcePreferredCodec) {
  146. const index = this.nonPreferredParticipants.findIndex(participantId => participantId === id);
  147. if (index > -1) {
  148. this.nonPreferredParticipants.splice(index, 1);
  149. }
  150. // If all the participants that have joined the conference with a
  151. // non-preferred codec have left, switch to the preferred codec.
  152. if (!this.nonPreferredParticipants.length) {
  153. session.setVideoCodecs(this.jvbPreferredCodec);
  154. }
  155. }
  156. }
  157. /**
  158. * Returns the preferred codec for the conference.
  159. *
  160. * @returns {CodecMimeType} preferred codec.
  161. */
  162. getPreferredCodec() {
  163. return this.conference.isP2PActive() ? this.p2pPreferredCodec : this.jvbPreferredCodec;
  164. }
  165. }