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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. var 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 only media not contained in <tt>otherSdp</tt>. Mapped by channel idx.
  8. * @param otherSdp the other SDP to check ssrc with.
  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. // compare lengths - can save a lot of time
  17. if (this.length != array.length)
  18. return false;
  19. for (var i = 0, l=this.length; i < l; i++) {
  20. // Check if we have nested arrays
  21. if (this[i] instanceof Array && array[i] instanceof Array) {
  22. // recurse into the nested arrays
  23. if (!this[i].equals(array[i]))
  24. return false;
  25. }
  26. else if (this[i] != array[i]) {
  27. // Warning - two different object instances will never be equal: {x:20} != {x:20}
  28. return false;
  29. }
  30. }
  31. return true;
  32. }
  33. var myMedias = this.mySDP.getMediaSsrcMap();
  34. var othersMedias = this.otherSDP.getMediaSsrcMap();
  35. var newMedia = {};
  36. Object.keys(othersMedias).forEach(function(othersMediaIdx) {
  37. var myMedia = myMedias[othersMediaIdx];
  38. var othersMedia = othersMedias[othersMediaIdx];
  39. if(!myMedia && othersMedia) {
  40. // Add whole channel
  41. newMedia[othersMediaIdx] = othersMedia;
  42. return;
  43. }
  44. // Look for new ssrcs accross the channel
  45. Object.keys(othersMedia.ssrcs).forEach(function(ssrc) {
  46. if(Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {
  47. // Allocate channel if we've found ssrc that doesn't exist in our channel
  48. if(!newMedia[othersMediaIdx]){
  49. newMedia[othersMediaIdx] = {
  50. mediaindex: othersMedia.mediaindex,
  51. mid: othersMedia.mid,
  52. ssrcs: {},
  53. ssrcGroups: []
  54. };
  55. }
  56. newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
  57. }
  58. });
  59. // Look for new ssrc groups across the channels
  60. othersMedia.ssrcGroups.forEach(function(otherSsrcGroup){
  61. // try to match the other ssrc-group with an ssrc-group of ours
  62. var matched = false;
  63. for (var i = 0; i < myMedia.ssrcGroups.length; i++) {
  64. var mySsrcGroup = myMedia.ssrcGroups[i];
  65. if (otherSsrcGroup.semantics == mySsrcGroup.semantics
  66. && arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
  67. matched = true;
  68. break;
  69. }
  70. }
  71. if (!matched) {
  72. // Allocate channel if we've found an ssrc-group that doesn't
  73. // exist in our channel
  74. if(!newMedia[othersMediaIdx]){
  75. newMedia[othersMediaIdx] = {
  76. mediaindex: othersMedia.mediaindex,
  77. mid: othersMedia.mid,
  78. ssrcs: {},
  79. ssrcGroups: []
  80. };
  81. }
  82. newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);
  83. }
  84. });
  85. });
  86. return newMedia;
  87. };
  88. /**
  89. * Sends SSRC update IQ.
  90. * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove.
  91. * @param sid session identifier that will be put into the IQ.
  92. * @param initiator initiator identifier.
  93. * @param toJid destination Jid
  94. * @param isAdd indicates if this is remove or add operation.
  95. */
  96. SDPDiffer.prototype.toJingle = function(modify) {
  97. var sdpMediaSsrcs = this.getNewMedia();
  98. var self = this;
  99. // FIXME: only announce video ssrcs since we mix audio and dont need
  100. // the audio ssrcs therefore
  101. var modified = false;
  102. Object.keys(sdpMediaSsrcs).forEach(function(mediaindex){
  103. modified = true;
  104. var media = sdpMediaSsrcs[mediaindex];
  105. modify.c('content', {name: media.mid});
  106. modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: media.mid});
  107. // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly
  108. // generate sources from lines
  109. Object.keys(media.ssrcs).forEach(function(ssrcNum) {
  110. var 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(function (line) {
  115. var idx = line.indexOf(' ');
  116. var kv = line.substr(idx + 1);
  117. modify.c('parameter');
  118. if (kv.indexOf(':') == -1) {
  119. modify.attrs({ name: kv });
  120. } else {
  121. var nv = kv.split(':', 2);
  122. var name = nv[0];
  123. var value = SDPUtil.filter_special_chars(nv[1]);
  124. modify.attrs({ name: name });
  125. modify.attrs({ value: 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(function(ssrcGroup) {
  133. if (ssrcGroup.ssrcs.length != 0) {
  134. modify.c('ssrc-group', {
  135. semantics: ssrcGroup.semantics,
  136. xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'
  137. });
  138. ssrcGroup.ssrcs.forEach(function (ssrc) {
  139. modify.c('source', { ssrc: 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;