Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

LocalSdpMunger.js 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /* global __filename */
  2. import { getLogger } from 'jitsi-meet-logger';
  3. import * as MediaType from '../../service/RTC/MediaType';
  4. import { SdpTransformWrap } from '../xmpp/SdpTransformUtil';
  5. const logger = getLogger(__filename);
  6. /**
  7. * Fakes local SDP, so that it will reflect detached local tracks associated
  8. * with the {@link TraceablePeerConnection} and make operations like
  9. * attach/detach and video mute/unmute local operations. That means it prevents
  10. * from SSRC updates being sent to Jicofo/remote peer, so that there is no
  11. * sRD/sLD cycle on the remote side.
  12. */
  13. export default class LocalSdpMunger {
  14. /**
  15. * Creates new <tt>LocalSdpMunger</tt> instance.
  16. *
  17. * @param {TraceablePeerConnection} tpc
  18. */
  19. constructor(tpc) {
  20. this.tpc = tpc;
  21. }
  22. /**
  23. * Makes sure that detached local audio tracks stored in the parent
  24. * {@link TraceablePeerConnection} are described in the local SDP.
  25. * It's done in order to prevent from sending 'source-remove'/'source-add'
  26. * Jingle notifications when local audio track is detached from
  27. * the {@link TraceablePeerConnection}.
  28. * @param {SdpTransformWrap} transformer the transformer instance which will
  29. * be used to process the SDP.
  30. * @return {boolean} <tt>true</tt> if there were any modifications to
  31. * the SDP wrapped by <tt>transformer</tt>.
  32. * @private
  33. */
  34. _addDetachedLocalAudioTracksToSDP(transformer) {
  35. const localAudio = this.tpc.getLocalTracks(MediaType.AUDIO);
  36. if (!localAudio.length) {
  37. return false;
  38. }
  39. const audioMLine = transformer.selectMedia('audio');
  40. if (!audioMLine) {
  41. logger.error(
  42. 'Unable to hack local audio track SDP - no "audio" media');
  43. return false;
  44. }
  45. if (audioMLine.direction === 'inactive') {
  46. logger.error(
  47. 'Not doing local audio transform for "inactive" direction');
  48. return false;
  49. }
  50. let modified = false;
  51. for (const audioTrack of localAudio) {
  52. const isAttached = audioTrack._isAttachedToPC(this.tpc);
  53. const shouldFake = !isAttached;
  54. logger.debug(
  55. `${audioTrack} isAttached: ${isAttached
  56. } => should fake audio SDP ?: ${shouldFake}`);
  57. if (!shouldFake) {
  58. // not using continue increases indentation
  59. // eslint-disable-next-line no-continue
  60. continue;
  61. }
  62. // Inject removed SSRCs
  63. const audioSSRC = this.tpc.getLocalSSRC(audioTrack);
  64. const audioMSID = audioTrack.storedMSID;
  65. if (!audioSSRC) {
  66. logger.error(
  67. `Can't fake SDP for ${audioTrack} - no SSRC stored`);
  68. // Aborts the forEach on this particular track,
  69. // but will continue with the other ones
  70. // eslint-disable-next-line no-continue
  71. continue;
  72. } else if (!audioMSID) {
  73. logger.error(
  74. `No MSID stored for local audio SSRC: ${audioSSRC}`);
  75. // eslint-disable-next-line no-continue
  76. continue;
  77. }
  78. if (audioMLine.getSSRCCount() > 0) {
  79. logger.debug(
  80. 'Doing nothing - audio SSRCs are still there');
  81. // audio SSRCs are still there
  82. // eslint-disable-next-line no-continue
  83. continue;
  84. }
  85. modified = true;
  86. // We need to fake sendrecv
  87. audioMLine.direction = 'sendrecv';
  88. logger.debug(`Injecting audio SSRC: ${audioSSRC}`);
  89. audioMLine.addSSRCAttribute({
  90. id: audioSSRC,
  91. attribute: 'cname',
  92. value: `injected-${audioSSRC}`
  93. });
  94. audioMLine.addSSRCAttribute({
  95. id: audioSSRC,
  96. attribute: 'msid',
  97. value: audioMSID
  98. });
  99. }
  100. return modified;
  101. }
  102. /**
  103. * Makes sure that detached (or muted) local video tracks associated with
  104. * the parent {@link TraceablePeerConnection} are described in the local
  105. * SDP. It's done in order to prevent from sending
  106. * 'source-remove'/'source-add' Jingle notifications when local video track
  107. * is detached from the {@link TraceablePeerConnection} (or muted).
  108. *
  109. * NOTE 1 video track is assumed
  110. *
  111. * @param {SdpTransformWrap} transformer the transformer instance which will
  112. * be used to process the SDP.
  113. * @return {boolean} <tt>true</tt> if there were any modifications to
  114. * the SDP wrapped by <tt>transformer</tt>.
  115. * @private
  116. */
  117. _addDetachedLocalVideoTracksToSDP(transformer) {
  118. // Go over each video tracks and check if the SDP has to be changed
  119. const localVideos = this.tpc.getLocalTracks(MediaType.VIDEO);
  120. if (!localVideos.length) {
  121. return false;
  122. } else if (localVideos.length !== 1) {
  123. logger.error(
  124. 'There is more than 1 video track ! '
  125. + 'Strange things may happen !', localVideos);
  126. }
  127. const videoMLine = transformer.selectMedia('video');
  128. if (!videoMLine) {
  129. logger.error(
  130. 'Unable to hack local video track SDP - no "video" media');
  131. return false;
  132. }
  133. if (videoMLine.direction === 'inactive') {
  134. logger.error(
  135. 'Not doing local video transform for "inactive" direction.');
  136. return false;
  137. }
  138. let modified = false;
  139. for (const videoTrack of localVideos) {
  140. const isMuted = videoTrack.isMuted();
  141. const muteInProgress = videoTrack.inMuteOrUnmuteProgress;
  142. const isAttached = videoTrack._isAttachedToPC(this.tpc);
  143. const shouldFakeSdp = isMuted || muteInProgress || !isAttached;
  144. logger.debug(
  145. `${videoTrack
  146. } isMuted: ${isMuted
  147. }, is mute in progress: ${muteInProgress
  148. }, is attached ? : ${isAttached
  149. } => should fake sdp ? : ${shouldFakeSdp}`);
  150. if (!shouldFakeSdp) {
  151. // eslint-disable-next-line no-continue
  152. continue;
  153. }
  154. // Inject removed SSRCs
  155. const requiredSSRCs
  156. = this.tpc.isSimulcastOn()
  157. ? this.tpc.simulcast.ssrcCache
  158. : [ this.tpc.sdpConsistency.cachedPrimarySsrc ];
  159. if (!requiredSSRCs.length) {
  160. logger.error(
  161. `No SSRCs stored for: ${videoTrack} in ${this.tpc}`);
  162. // eslint-disable-next-line no-continue
  163. continue;
  164. }
  165. if (!videoMLine.getSSRCCount()) {
  166. logger.error(
  167. 'No video SSRCs found '
  168. + '(should be at least the recv-only one');
  169. // eslint-disable-next-line no-continue
  170. continue;
  171. }
  172. modified = true;
  173. // We need to fake sendrecv
  174. videoMLine.direction = 'sendrecv';
  175. // Check if the recvonly has MSID
  176. const primarySSRC = requiredSSRCs[0];
  177. // FIXME the cname could come from the stream, but may
  178. // turn out to be too complex. It is fine to come up
  179. // with any value, as long as we only care about
  180. // the actual SSRC values when deciding whether or not
  181. // an update should be sent
  182. const primaryCname = `injected-${primarySSRC}`;
  183. for (const ssrcNum of requiredSSRCs) {
  184. // Remove old attributes
  185. videoMLine.removeSSRC(ssrcNum);
  186. // Inject
  187. logger.debug(
  188. `Injecting video SSRC: ${ssrcNum} for ${videoTrack}`);
  189. videoMLine.addSSRCAttribute({
  190. id: ssrcNum,
  191. attribute: 'cname',
  192. value: primaryCname
  193. });
  194. videoMLine.addSSRCAttribute({
  195. id: ssrcNum,
  196. attribute: 'msid',
  197. value: videoTrack.storedMSID
  198. });
  199. }
  200. if (requiredSSRCs.length > 1) {
  201. const group = {
  202. ssrcs: requiredSSRCs.join(' '),
  203. semantics: 'SIM'
  204. };
  205. if (!videoMLine.findGroup(group.semantics, group.ssrcs)) {
  206. // Inject the group
  207. logger.debug(
  208. `Injecting SIM group for ${videoTrack}`, group);
  209. videoMLine.addSSRCGroup(group);
  210. }
  211. }
  212. // Insert RTX
  213. // FIXME in P2P RTX is used by Chrome regardless of config option
  214. // status. Because of that 'source-remove'/'source-add'
  215. // notifications are still sent to remove/add RTX SSRC and FID group
  216. if (!this.tpc.options.disableRtx) {
  217. this.tpc.rtxModifier.modifyRtxSsrcs2(videoMLine);
  218. }
  219. }
  220. return modified;
  221. }
  222. /**
  223. * Maybe modifies local description to fake local tracks SDP when those are
  224. * either muted or detached from the <tt>PeerConnection</tt>.
  225. *
  226. * @param {object} desc the WebRTC SDP object instance for the local
  227. * description.
  228. */
  229. maybeMungeLocalSdp(desc) {
  230. // Nothing to be done in early stage when localDescription
  231. // is not available yet
  232. if (!desc || !desc.sdp) {
  233. return;
  234. }
  235. const transformer = new SdpTransformWrap(desc.sdp);
  236. let modified = this._addDetachedLocalAudioTracksToSDP(transformer);
  237. if (this._addDetachedLocalVideoTracksToSDP(transformer)) {
  238. modified = true;
  239. }
  240. if (modified) {
  241. // Write
  242. desc.sdp = transformer.toRawSDP();
  243. // logger.info("Post TRANSFORM: ", desc.sdp);
  244. }
  245. }
  246. }