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.

SDPDiffer.js 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. const SDPUtil = require('./SDPUtil');
  2. function SDPDiffer(mySDP, otherSDP) {
  3. this.mySDP = mySDP;
  4. this.otherSDP = otherSDP;
  5. }
  6. /**
  7. * Returns map of MediaChannel that contains media contained in
  8. * 'mySDP', but not contained in 'otherSdp'. Mapped by channel idx.
  9. */
  10. SDPDiffer.prototype.getNewMedia = function() {
  11. // this could be useful in Array.prototype.
  12. function arrayEquals(array) {
  13. // if the other array is a falsy value, return
  14. if (!array) {
  15. return false;
  16. }
  17. // compare lengths - can save a lot of time
  18. if (this.length !== array.length) {
  19. return false;
  20. }
  21. for (let i = 0, l = this.length; i < l; i++) {
  22. // Check if we have nested arrays
  23. if (this[i] instanceof Array && array[i] instanceof Array) {
  24. // recurse into the nested arrays
  25. if (!this[i].equals(array[i])) {
  26. return false;
  27. }
  28. } else if (this[i] !== array[i]) {
  29. // Warning - two different object instances will never be
  30. // equal: {x:20} != {x:20}
  31. return false;
  32. }
  33. }
  34. return true;
  35. }
  36. const myMedias = this.mySDP.getMediaSsrcMap();
  37. const othersMedias = this.otherSDP.getMediaSsrcMap();
  38. const newMedia = {};
  39. Object.keys(othersMedias).forEach(othersMediaIdx => {
  40. const myMedia = myMedias[othersMediaIdx];
  41. const othersMedia = othersMedias[othersMediaIdx];
  42. if (!myMedia && othersMedia) {
  43. // Add whole channel
  44. newMedia[othersMediaIdx] = othersMedia;
  45. return;
  46. }
  47. // Look for new ssrcs across the channel
  48. Object.keys(othersMedia.ssrcs).forEach(ssrc => {
  49. if (Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {
  50. // Allocate channel if we've found ssrc that doesn't exist in
  51. // our channel
  52. if (!newMedia[othersMediaIdx]) {
  53. newMedia[othersMediaIdx] = {
  54. mediaindex: othersMedia.mediaindex,
  55. mid: othersMedia.mid,
  56. ssrcs: {},
  57. ssrcGroups: []
  58. };
  59. }
  60. newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
  61. }
  62. });
  63. // Look for new ssrc groups across the channels
  64. othersMedia.ssrcGroups.forEach(otherSsrcGroup => {
  65. // try to match the other ssrc-group with an ssrc-group of ours
  66. let matched = false;
  67. for (let i = 0; i < myMedia.ssrcGroups.length; i++) {
  68. const mySsrcGroup = myMedia.ssrcGroups[i];
  69. if (otherSsrcGroup.semantics === mySsrcGroup.semantics
  70. && arrayEquals.apply(otherSsrcGroup.ssrcs,
  71. [ mySsrcGroup.ssrcs ])) {
  72. matched = true;
  73. break;
  74. }
  75. }
  76. if (!matched) {
  77. // Allocate channel if we've found an ssrc-group that doesn't
  78. // exist in our channel
  79. if (!newMedia[othersMediaIdx]) {
  80. newMedia[othersMediaIdx] = {
  81. mediaindex: othersMedia.mediaindex,
  82. mid: othersMedia.mid,
  83. ssrcs: {},
  84. ssrcGroups: []
  85. };
  86. }
  87. newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);
  88. }
  89. });
  90. });
  91. return newMedia;
  92. };
  93. /**
  94. * TODO: document!
  95. */
  96. SDPDiffer.prototype.toJingle = function(modify) {
  97. const sdpMediaSsrcs = this.getNewMedia();
  98. let modified = false;
  99. Object.keys(sdpMediaSsrcs).forEach(mediaindex => {
  100. modified = true;
  101. const media = sdpMediaSsrcs[mediaindex];
  102. modify.c('content', { name: media.mid });
  103. modify.c('description',
  104. { xmlns: 'urn:xmpp:jingle:apps:rtp:1',
  105. media: media.mid });
  106. // FIXME: not completely sure this operates on blocks and / or handles
  107. // different ssrcs correctly
  108. // generate sources from lines
  109. Object.keys(media.ssrcs).forEach(ssrcNum => {
  110. const mediaSsrc = media.ssrcs[ssrcNum];
  111. modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
  112. modify.attrs({ ssrc: mediaSsrc.ssrc });
  113. // iterate over ssrc lines
  114. mediaSsrc.lines.forEach(line => {
  115. const idx = line.indexOf(' ');
  116. const kv = line.substr(idx + 1);
  117. modify.c('parameter');
  118. if (kv.indexOf(':') === -1) {
  119. modify.attrs({ name: kv });
  120. } else {
  121. const nv = kv.split(':', 2);
  122. const name = nv[0];
  123. const value = SDPUtil.filter_special_chars(nv[1]);
  124. modify.attrs({ name });
  125. modify.attrs({ value });
  126. }
  127. modify.up(); // end of parameter
  128. });
  129. modify.up(); // end of source
  130. });
  131. // generate source groups from lines
  132. media.ssrcGroups.forEach(ssrcGroup => {
  133. if (ssrcGroup.ssrcs.length) {
  134. modify.c('ssrc-group', {
  135. semantics: ssrcGroup.semantics,
  136. xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'
  137. });
  138. ssrcGroup.ssrcs.forEach(ssrc => {
  139. modify.c('source', { ssrc })
  140. .up(); // end of source
  141. });
  142. modify.up(); // end of ssrc-group
  143. }
  144. });
  145. modify.up(); // end of description
  146. modify.up(); // end of content
  147. });
  148. return modified;
  149. };
  150. module.exports = SDPDiffer;