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.

SdpConsistency.js 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { getLogger } from "jitsi-meet-logger";
  2. const logger = getLogger(__filename);
  3. import * as transform from 'sdp-transform';
  4. import * as SDPUtil from "./SDPUtil";
  5. /**
  6. * Begin helper functions
  7. */
  8. /**
  9. * Given a video mline (as parsed from transform.parse),
  10. * return the single primary video ssrcs
  11. * @param {object} videoMLine the video MLine from which to extract the
  12. * primary video ssrc
  13. * @returns {number} the primary video ssrc
  14. */
  15. function getPrimarySsrc (videoMLine) {
  16. if (!videoMLine.ssrcs) {
  17. return;
  18. }
  19. let numSsrcs = videoMLine.ssrcs
  20. .map(ssrcInfo => ssrcInfo.id)
  21. .filter((ssrc, index, array) => array.indexOf(ssrc) === index)
  22. .length;
  23. if (numSsrcs === 1) {
  24. return videoMLine.ssrcs[0].id;
  25. } else {
  26. let findGroup = (mLine, groupName) => {
  27. return mLine
  28. .ssrcGroups
  29. .filter(group => group.semantics === groupName)[0];
  30. };
  31. // Look for a SIM or FID group
  32. if (videoMLine.ssrcGroups) {
  33. let simGroup = findGroup(videoMLine, "SIM");
  34. if (simGroup) {
  35. return SDPUtil.parseGroupSsrcs(simGroup)[0];
  36. }
  37. let fidGroup = findGroup(videoMLine, "FID");
  38. if (fidGroup) {
  39. return SDPUtil.parseGroupSsrcs(fidGroup)[0];
  40. }
  41. }
  42. }
  43. }
  44. /**
  45. * End helper functions
  46. */
  47. /**
  48. * Handles the work of keeping video ssrcs consistent across multiple
  49. * o/a cycles, making it such that all stream operations can be
  50. * kept local and do not need to be signaled.
  51. * NOTE: This only keeps the 'primary' video ssrc consistent: meaning
  52. * the primary video stream
  53. */
  54. export default class SdpConsistency {
  55. /**
  56. * Constructor
  57. */
  58. constructor () {
  59. this.clearSsrcCache();
  60. }
  61. /**
  62. * Clear the cached primary and primary rtx ssrcs so that
  63. * they will not be used for the next call to
  64. * makeVideoPrimarySsrcsConsistent
  65. */
  66. clearSsrcCache () {
  67. this.cachedPrimarySsrc = null;
  68. }
  69. /**
  70. * Explicitly set the primary ssrc to be used in
  71. * makeVideoPrimarySsrcsConsistent
  72. * @param {number} primarySsrc the primarySsrc to be used
  73. * in future calls to makeVideoPrimarySsrcsConsistent
  74. */
  75. setPrimarySsrc (primarySsrc) {
  76. this.cachedPrimarySsrc = primarySsrc;
  77. }
  78. /**
  79. * Given an sdp string, either:
  80. * 1) record the primary video and primary rtx ssrcs to be
  81. * used in future calls to makeVideoPrimarySsrcsConsistent or
  82. * 2) change the primary and primary rtx ssrcs in the given sdp
  83. * to match the ones previously cached
  84. * @param {string} sdpStr the sdp string to (potentially)
  85. * change to make the video ssrcs consistent
  86. * @returns {string} a (potentially) modified sdp string
  87. * with ssrcs consistent with this class' cache
  88. */
  89. makeVideoPrimarySsrcsConsistent (sdpStr) {
  90. let parsedSdp = transform.parse(sdpStr);
  91. let videoMLine =
  92. parsedSdp.media.find(mLine => mLine.type === "video");
  93. if (videoMLine.direction === "inactive") {
  94. logger.info("Sdp-consistency doing nothing, " +
  95. "video mline is inactive");
  96. return sdpStr;
  97. }
  98. if (videoMLine.direction === "recvonly") {
  99. // If the mline is recvonly, we'll add the primary
  100. // ssrc as a recvonly ssrc
  101. videoMLine.ssrcs = videoMLine.ssrcs || [];
  102. if (this.cachedPrimarySsrc) {
  103. videoMLine.ssrcs.push({
  104. id: this.cachedPrimarySsrc,
  105. attribute: "cname",
  106. value: "recvonly-" + this.cachedPrimarySsrc
  107. });
  108. } else {
  109. logger.error("No SSRC found for the recvonly video stream!");
  110. }
  111. } else {
  112. let newPrimarySsrc = getPrimarySsrc(videoMLine);
  113. if (!newPrimarySsrc) {
  114. logger.info("Sdp-consistency couldn't parse new primary ssrc");
  115. return sdpStr;
  116. }
  117. if (!this.cachedPrimarySsrc) {
  118. this.cachedPrimarySsrc = newPrimarySsrc;
  119. logger.info("Sdp-consistency caching primary ssrc " +
  120. this.cachedPrimarySsrc);
  121. } else {
  122. logger.info("Sdp-consistency replacing new ssrc " +
  123. newPrimarySsrc + " with cached " + this.cachedPrimarySsrc);
  124. videoMLine.ssrcs.forEach(ssrcInfo => {
  125. if (ssrcInfo.id === newPrimarySsrc) {
  126. ssrcInfo.id = this.cachedPrimarySsrc;
  127. }
  128. });
  129. if (videoMLine.ssrcGroups) {
  130. videoMLine.ssrcGroups.forEach(group => {
  131. if (group.semantics === "FID") {
  132. let fidGroupSsrcs = SDPUtil.parseGroupSsrcs(group);
  133. let primarySsrc = fidGroupSsrcs[0];
  134. let rtxSsrc = fidGroupSsrcs[1];
  135. if (primarySsrc === newPrimarySsrc) {
  136. group.ssrcs =
  137. this.cachedPrimarySsrc + " " +
  138. rtxSsrc;
  139. }
  140. }
  141. });
  142. }
  143. }
  144. }
  145. return transform.write(parsedSdp);
  146. }
  147. }