您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

VideoSSRCHack.js 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /* global $ */
  2. /*
  3. The purpose of this hack is to re-use SSRC of first video stream ever created
  4. for any video streams created later on. In order to do that this hack:
  5. 1. Stores the SSRC of the first video stream created by
  6. a) scanning Jingle session-accept/session-invite for existing video SSRC
  7. b) watching for 'source-add' for new video stream if it has not been
  8. created in step a)
  9. 2. Exposes method 'mungeLocalVideoSSRC' which replaces any new video SSRC with
  10. the stored one. It is called by 'TracablePeerConnection' before local SDP is
  11. returned to the other parts of the application.
  12. 3. Scans 'source-remove'/'source-add' notifications for stored video SSRC and
  13. blocks those notifications. This makes Jicofo and all participants think
  14. that it exists all the time even if the video stream has been removed or
  15. replaced locally. Thanks to that there is no additional signaling activity
  16. on video mute or when switching to the desktop stream.
  17. */
  18. var SDP = require('./SDP');
  19. /**
  20. * Stored SSRC of local video stream.
  21. */
  22. var localVideoSSRC;
  23. /**
  24. * Method removes <source> element which describes <tt>localVideoSSRC</tt>
  25. * from given Jingle IQ.
  26. * @param modifyIq 'source-add' or 'source-remove' Jingle IQ.
  27. * @param actionName display name of the action which will be printed in log
  28. * messages.
  29. * @returns {*} modified Jingle IQ, so that it does not contain <source> element
  30. * corresponding to <tt>localVideoSSRC</tt> or <tt>null</tt> if no
  31. * other SSRCs left to be signaled after removing it.
  32. */
  33. var filterOutSource = function (modifyIq, actionName) {
  34. var modifyIqTree = $(modifyIq.tree());
  35. if (!localVideoSSRC)
  36. return modifyIqTree[0];
  37. var videoSSRC = modifyIqTree.find(
  38. '>jingle>content[name="video"]' +
  39. '>description>source[ssrc="' + localVideoSSRC + '"]');
  40. if (!videoSSRC.length) {
  41. return modifyIqTree[0];
  42. }
  43. console.info(
  44. 'Blocking ' + actionName + ' for local video SSRC: ' + localVideoSSRC);
  45. videoSSRC.remove();
  46. // Check if any sources still left to be added/removed
  47. if (modifyIqTree.find('>jingle>content>description>source').length) {
  48. return modifyIqTree[0];
  49. } else {
  50. return null;
  51. }
  52. };
  53. /**
  54. * Scans given Jingle IQ for video SSRC and stores it.
  55. * @param jingleIq the Jingle IQ to be scanned for video SSRC.
  56. */
  57. var storeLocalVideoSSRC = function (jingleIq) {
  58. var videoSSRCs =
  59. $(jingleIq.tree())
  60. .find('>jingle>content[name="video"]>description>source');
  61. console.info('Video desc: ', videoSSRCs);
  62. if (!videoSSRCs.length)
  63. return;
  64. var ssrc = videoSSRCs.attr('ssrc');
  65. if (ssrc) {
  66. localVideoSSRC = ssrc;
  67. console.info(
  68. 'Stored local video SSRC for future re-use: ' + localVideoSSRC);
  69. } else {
  70. console.error('No "ssrc" attribute present in <source> element');
  71. }
  72. };
  73. var LocalVideoSSRCHack = {
  74. /**
  75. * Method must be called before 'session-initiate' or 'session-invite' is
  76. * sent. Scans the IQ for local video SSRC and stores it if detected.
  77. *
  78. * @param sessionInit our 'session-initiate' or 'session-accept' Jingle IQ
  79. * which will be scanned for local video SSRC.
  80. */
  81. processSessionInit: function (sessionInit) {
  82. if (localVideoSSRC) {
  83. console.error("Local SSRC stored already: " + localVideoSSRC);
  84. return;
  85. }
  86. storeLocalVideoSSRC(sessionInit);
  87. },
  88. /**
  89. * If we have local video SSRC stored searched given
  90. * <tt>localDescription</tt> for video SSRC and makes sure it is replaced
  91. * with the stored one.
  92. * @param localDescription local description object that will have local
  93. * video SSRC replaced with the stored one
  94. * @returns modified <tt>localDescription</tt> object.
  95. */
  96. mungeLocalVideoSSRC: function (localDescription) {
  97. // IF we have local video SSRC stored make sure it is replaced
  98. // with old SSRC
  99. if (localVideoSSRC) {
  100. var newSdp = new SDP(localDescription.sdp);
  101. if (newSdp.media[1].indexOf("a=ssrc:") !== -1 &&
  102. !newSdp.containsSSRC(localVideoSSRC)) {
  103. // Get new video SSRC
  104. var map = newSdp.getMediaSsrcMap();
  105. var videoPart = map[1];
  106. var videoSSRCs = videoPart.ssrcs;
  107. var newSSRC = Object.keys(videoSSRCs)[0];
  108. console.info(
  109. "Replacing new video SSRC: " + newSSRC +
  110. " with " + localVideoSSRC);
  111. localDescription.sdp =
  112. newSdp.raw.replace(
  113. new RegExp('a=ssrc:' + newSSRC, 'g'),
  114. 'a=ssrc:' + localVideoSSRC);
  115. }
  116. }
  117. return localDescription;
  118. },
  119. /**
  120. * Method must be called before 'source-add' notification is sent. In case
  121. * we have local video SSRC advertised already it will be removed from the
  122. * notification. If no other SSRCs are described by given IQ null will be
  123. * returned which means that there is no point in sending the notification.
  124. * @param sourceAdd 'source-add' Jingle IQ to be processed
  125. * @returns modified 'source-add' IQ which can be sent to the focus or
  126. * <tt>null</tt> if no notification shall be sent. It is no longer
  127. * a Strophe IQ Builder instance, but DOM element tree.
  128. */
  129. processSourceAdd: function (sourceAdd) {
  130. if (!localVideoSSRC) {
  131. // Store local SSRC if available
  132. storeLocalVideoSSRC(sourceAdd);
  133. return sourceAdd;
  134. } else {
  135. return filterOutSource(sourceAdd, 'source-add');
  136. }
  137. },
  138. /**
  139. * Method must be called before 'source-remove' notification is sent.
  140. * Removes local video SSRC from the notification. If there are no other
  141. * SSRCs described in the given IQ <tt>null</tt> will be returned which
  142. * means that there is no point in sending the notification.
  143. * @param sourceRemove 'source-remove' Jingle IQ to be processed
  144. * @returns modified 'source-remove' IQ which can be sent to the focus or
  145. * <tt>null</tt> if no notification shall be sent. It is no longer
  146. * a Strophe IQ Builder instance, but DOM element tree.
  147. */
  148. processSourceRemove: function (sourceRemove) {
  149. return filterOutSource(sourceRemove, 'source-remove');
  150. }
  151. };
  152. module.exports = LocalVideoSSRCHack;