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 6.6KB

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