You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CodecSelection.js 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { getLogger } from '@jitsi/logger';
  2. import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
  3. import CodecMimeType from '../../service/RTC/CodecMimeType';
  4. import { 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. this.enforcePreferredCodec = options.enforcePreferredCodec;
  32. // VP8 cannot be disabled since it the default codec.
  33. this.p2pDisabledCodec = options.p2pDisabledCodec !== CodecMimeType.VP8
  34. && this._isCodecSupported(options.p2pDisabledCodec)
  35. && options.p2pDisabledCodec;
  36. this.jvbDisabledCodec = options.jvbDisabledCodec !== CodecMimeType.VP8
  37. && this._isCodecSupported(options.jvbDisabledCodec)
  38. && options.jvbDisabledCodec;
  39. // Determine the preferred codecs.
  40. this.p2pPreferredCodec = this._isCodecSupported(options.p2pPreferredCodec)
  41. && options.p2pPreferredCodec !== options.p2pDisabledCodec
  42. ? options.p2pPreferredCodec
  43. : CodecMimeType.VP8;
  44. this.jvbPreferredCodec = this._isCodecSupported(options.jvbPreferredCodec)
  45. && options.jvbPreferredCodec !== options.jvbDisabledCodec
  46. ? options.jvbPreferredCodec
  47. : CodecMimeType.VP8;
  48. logger.debug(`Codec preferences for the conference are JVB: preferred=${this.jvbPreferredCodec},`
  49. + `disabled=${this.jvbDisabledCodec} P2P: preferred=${this.p2pPreferredCodec},`
  50. + `disabled=${this.p2pDisabledCodec}`);
  51. this.conference.on(
  52. JitsiConferenceEvents.USER_JOINED,
  53. () => this._selectPreferredCodec());
  54. this.conference.on(
  55. JitsiConferenceEvents.USER_LEFT,
  56. () => this._selectPreferredCodec());
  57. this.conference.on(
  58. JitsiConferenceEvents._MEDIA_SESSION_STARTED,
  59. session => this._selectPreferredCodec(session));
  60. }
  61. /**
  62. * Checks if the given codec is supported by the browser.
  63. *
  64. * @param {CodecMimeType} preferredCodec codec to be checked.
  65. * @returns {boolean} true if the given codec is supported, false otherwise.
  66. * @private
  67. */
  68. _isCodecSupported(preferredCodec) {
  69. if (!preferredCodec) {
  70. return false;
  71. }
  72. if (preferredCodec === CodecMimeType.VP9 && !this.enforcePreferredCodec && !browser.supportsVP9()) {
  73. return false;
  74. }
  75. // Skip the check on FF because it does not support the getCapabilities API.
  76. // It is safe to assume that Firefox supports all the codecs supported by Chrome.
  77. if (browser.isFirefox()) {
  78. return true;
  79. }
  80. return window.RTCRtpReceiver
  81. && window.RTCRtpReceiver.getCapabilities
  82. && window.RTCRtpReceiver.getCapabilities('video').codecs
  83. .some(codec => codec.mimeType.toLowerCase() === `video/${preferredCodec}`);
  84. }
  85. /**
  86. * Sets the codec on the media session based on the preferred/disabled codec setting and the supported codecs
  87. * published by the remote participants in their presence.
  88. *
  89. * @param {JingleSessionPC} mediaSession session for which the codec selection has to be made.
  90. */
  91. _selectPreferredCodec(mediaSession) {
  92. const session = mediaSession ? mediaSession : this.conference.jvbJingleSession;
  93. if (!session) {
  94. return;
  95. }
  96. const preferredCodec = session.isP2P ? this.p2pPreferredCodec : this.jvbPreferredCodec;
  97. const disabledCodec = session.isP2P ? this.p2pDisabledCodec : this.jvbDisabledCodec;
  98. const currentCodec = session?.peerconnection.getConfiguredVideoCodec();
  99. let selectedCodec = preferredCodec ?? currentCodec;
  100. if (!this.enforcePreferredCodec) {
  101. const remoteParticipants = this.conference.getParticipants().map(participant => participant.getId());
  102. const remoteCodecs = remoteParticipants?.map(remote => {
  103. const peerMediaInfo = session._signalingLayer.getPeerMediaInfo(remote, MediaType.VIDEO);
  104. return peerMediaInfo?.codecType;
  105. });
  106. const nonPreferredCodecs = remoteCodecs.filter(codec => codec !== selectedCodec && codec !== disabledCodec);
  107. // Find the fallback codec when there are endpoints in the call that don't have the same preferred codec
  108. // set.
  109. if (nonPreferredCodecs.length) {
  110. // Always prefer VP8 as that is the default codec supported on all client types.
  111. selectedCodec = nonPreferredCodecs.find(codec => codec === CodecMimeType.VP8)
  112. ?? nonPreferredCodecs.find(codec => this._isCodecSupported(codec));
  113. }
  114. }
  115. if (selectedCodec !== currentCodec || !session?.peerconnection.isVideoCodecDisabled(disabledCodec)) {
  116. session.setVideoCodecs(selectedCodec, disabledCodec);
  117. }
  118. }
  119. /**
  120. * Returns the preferred codec for the conference. The preferred codec for the JVB media session
  121. * is the one that gets published in presence and a comparision is made whenever a participant joins
  122. * or leaves the call.
  123. *
  124. * @returns {CodecMimeType} preferred codec.
  125. */
  126. getPreferredCodec() {
  127. return this.jvbPreferredCodec;
  128. }
  129. }