File: source/peer-handshake.js

  1. /**
  2. * These are the list of Peer connection handshake states that Skylink would trigger.
  3. * - Do not be confused with {{#crossLink "Skylink/PEER_CONNECTION_STATE:attr"}}PEER_CONNECTION_STATE{{/crossLink}}.
  4. * This is the Peer recognition connection that is established with the platform signaling protocol, and not
  5. * the Peer connection signaling state itself.
  6. * - In this case, this happens before the {{#crossLink "Skylink/PEER_CONNECTION_STATE:attr"}}PEER_CONNECTION_STATE
  7. * handshaking states. {{/crossLink}} The <code>OFFER</code> and <code>ANSWER</code> relates to the
  8. * {{#crossLink "Skylink/PEER_CONNECTION_STATE:attr"}}PEER_CONNECTION_STATE states{{/crossLink}}.
  9. * - For example as explanation how these state works below, let's make self as the offerer and
  10. * the connecting Peer as the answerer.
  11. * @attribute HANDSHAKE_PROGRESS
  12. * @type JSON
  13. * @param {String} ENTER <small>Value <code>"enter"</code></small>
  14. * The state when Peer have received <code>ENTER</code> from self,
  15. * and Peer connection with self is initialised with self.<br>
  16. * This state will occur for both self and Peer as <code>ENTER</code>
  17. * message is sent to ping for Peers in the room.<br>
  18. * At this state, Peer would sent <code>WELCOME</code> to the peer to
  19. * start the session description connection handshake.<br>
  20. * <table class="table table-condensed">
  21. * <thead><tr><th class="col-md-1"></th><th class="col-md-5">Self</th><th>Peer</th></thead>
  22. * <tbody>
  23. * <tr><td class="col-md-1">1.</td>
  24. * <td class="col-md-5">Sends <code>ENTER</code></td><td>Sends <code>ENTER</code></td></tr>
  25. * <tr><td class="col-md-1">2.</td>
  26. * <td class="col-md-5">-</td><td>Receives self <code>ENTER</code></td></tr>
  27. * <tr><td class="col-md-1">3.</td>
  28. * <td class="col-md-5">-</td><td>Sends self <code>WELCOME</code></td></tr>
  29. * </tbody>
  30. * </table>
  31. * @param {String} WELCOME <small>Value <code>"welcome"</code></small>
  32. * The state when self have received <code>WELCOME</code> from Peer,
  33. * and Peer connection is initialised with Peer.<br>
  34. * At this state, self would start the session description connection handshake and
  35. * send the local <code>OFFER</code> session description to Peer.
  36. * <table class="table table-condensed">
  37. * <thead><tr><th class="col-md-1"></th><th class="col-md-5">Self</th><th>Peer</th></thead>
  38. * <tbody>
  39. * <tr><td class="col-md-1">4.</td>
  40. * <td class="col-md-5">Receives <code>WELCOME</code></td><td>-</td></tr>
  41. * <tr><td class="col-md-1">5.</td>
  42. * <td class="col-md-5">Generates <code>OFFER</code></td><td>-</td></tr>
  43. * <tr><td class="col-md-1">6.</td>
  44. * <td class="col-md-5">Sets local <code>OFFER</code><sup>REF</sup></td><td>-</td></tr>
  45. * <tr><td class="col-md-1">7.</td>
  46. * <td class="col-md-5">Sends <code>OFFER</code></td><td>-</td></tr>
  47. * </tbody>
  48. * </table>
  49. * <sup>REF</sup>: The will cause {{#crossLink "Skylink/PEER_CONNECTION_STATE:attr"}}PEER_CONNECTION_STATE{{/crossLink}}
  50. * state go to <code>HAVE_LOCAL_OFFER</code>.
  51. * @param {String} OFFER <small>Value <code>"offer"</code></small>
  52. * The state when Peer received <code>OFFER</code> from self.
  53. * At this state, Peer would set the remote <code>OFFER</code> session description and
  54. * start to send local <code>ANSWER</code> session description to self.<br>
  55. * <table class="table table-condensed">
  56. * <thead><tr><th class="col-md-1"></th><th class="col-md-5">Self</th><th>Peer</th></thead>
  57. * <tbody>
  58. * <tr><td class="col-md-1">8.</td>
  59. * <td class="col-md-5">-</td><td>Receives <code>OFFER</code></td></tr>
  60. * <tr><td class="col-md-1">9.</td>
  61. * <td class="col-md-5">-</td><td>Sets remote <code>OFFER</code><sup>REF</sup></td></tr>
  62. * <tr><td class="col-md-1">10.</td>
  63. * <td class="col-md-5">-</td><td>Generates <code>ANSWER</code></td></tr>
  64. * <tr><td class="col-md-1">11.</td>
  65. * <td class="col-md-5">-</td><td>Sets local <code>ANSWER</code></td></tr>
  66. * <tr><td class="col-md-1">12.</td>
  67. * <td class="col-md-5">-</td><td>Sends <code>ANSWER</code></td></tr>
  68. * </tbody>
  69. * </table>
  70. * <sup>REF</sup>: The will cause {{#crossLink "Skylink/PEER_CONNECTION_STATE:attr"}}PEER_CONNECTION_STATE{{/crossLink}}
  71. * state go to <code>HAVE_REMOTE_OFFER</code>.
  72. * @param {String} ANSWER <small>Value <code>"answer"</code></small>
  73. * The state when self received <code>ANSWER</code> from Peer.<br>
  74. * At this state, self would set the remote <code>ANSWER</code> session description and
  75. * the connection handshaking progress has been completed.<br>
  76. * <table class="table table-condensed">
  77. * <thead><tr><th class="col-md-1"></th><th class="col-md-5">Self</th><th>Peer</th></thead>
  78. * <tbody>
  79. * <tr><td class="col-md-1">13.</td>
  80. * <td class="col-md-5">Receives <code>ANSWER</code></td><td>-</td></tr>
  81. * <tr><td class="col-md-1">14.</td>
  82. * <td class="col-md-5">Sets remote <code>ANSWER</code></td><td>-</td></tr>
  83. * </tbody>
  84. * </table>
  85. * @param {String} ERROR <small>Value <code>"error"</code></small>
  86. * The state when connection handshake has occurred and exception,
  87. * in this which the connection handshake could have been aborted abruptly
  88. * and no Peer connection is established.
  89. * @readOnly
  90. * @component Peer
  91. * @for Skylink
  92. * @since 0.1.0
  93. */
  94. Skylink.prototype.HANDSHAKE_PROGRESS = {
  95. ENTER: 'enter',
  96. WELCOME: 'welcome',
  97. OFFER: 'offer',
  98. ANSWER: 'answer',
  99. ERROR: 'error'
  100. };
  101.  
  102. /**
  103. * Stores the list of Peer connection health timeout objects that
  104. * waits for any existing Peer "healthy" state in successful
  105. * {{#crossLink "Skylink/_peerConnectionHealth:attr"}}_peerConnectionHealth{{/crossLink}}.
  106. * If timeout has reached it's limit and does not have any "healthy" connection state
  107. * with Peer connection, it will restart the connection again with
  108. * {{#crossLink "Skylink/_restartPeerConnection:method"}}_restartPeerConnection(){{/crossLink}}.
  109. * @attribute _peerConnectionHealthTimers
  110. * @param {Object} (#peerId) The timeout object set using <code>setTimeout()</code> that
  111. * does the wait for any "healthy" state connection associated with the Peer connection.
  112. * This will be removed when the Peer connection has ended or when the Peer
  113. * connection has been met with a "healthy" state.
  114. * @type JSON
  115. * @private
  116. * @required
  117. * @component Peer
  118. * @for Skylink
  119. * @since 0.5.5
  120. */
  121. Skylink.prototype._peerConnectionHealthTimers = {};
  122.  
  123. /**
  124. * Stores the list of Peer connections that has connection
  125. * established successfully. When the Peer connection has a
  126. * successful ICE connection state of <code>"completed"</code>,
  127. * it stores the Peer connection as "healthy".
  128. * @attribute _peerConnectionHealth
  129. * @param {Boolean} (#peerId) The flag that indicates if the associated Peer
  130. * connection is in a "healthy" state. If the value is <code>true</code>, it means
  131. * that the Peer connectin is in a "healthy" state.
  132. * @type JSON
  133. * @private
  134. * @required
  135. * @component Peer
  136. * @since 0.5.5
  137. */
  138. Skylink.prototype._peerConnectionHealth = {};
  139.  
  140. /**
  141. * Stores the peer connection priority weight.
  142. * @attribute _peerPriorityWeight
  143. * @type Number
  144. * @private
  145. * @required
  146. * @for Skylink
  147. * @since 0.5.0
  148. */
  149. Skylink.prototype._peerPriorityWeight = 0;
  150.  
  151. /**
  152. * Starts to initiate the WebRTC layer of handshake connection by
  153. * creating the <code>OFFER</code> session description and then
  154. * sending it to the associated Peer.
  155. * The offerer status may be shifted to the other peer depending on
  156. * when version of browser that is initiating the connection
  157. * to what version of browser to.
  158. * @method _doOffer
  159. * @param {String} targetMid The Peer ID to send the <code>OFFER</code> to.
  160. * @param {JSON} peerBrowser The Peer platform agent information.
  161. * @param {String} peerBrowser.name The Peer platform browser or agent name.
  162. * @param {Number} peerBrowser.version The Peer platform browser or agent version.
  163. * @param {Number} peerBrowser.os The Peer platform name.
  164. * @param {Function} renegoCallback The callback function that triggers after
  165. * the offer has been created or responsed.
  166. * @private
  167. * @for Skylink
  168. * @component Peer
  169. * @since 0.5.2
  170. */
  171. Skylink.prototype._doOffer = function(targetMid, peerBrowser) {
  172. var self = this;
  173. var pc = self._peerConnections[targetMid] || self._addPeer(targetMid, peerBrowser);
  174. log.log([targetMid, null, null, 'Checking caller status'], peerBrowser);
  175. // NOTE ALEX: handle the pc = 0 case, just to be sure
  176. var inputConstraints = self._room.connection.offerConstraints;
  177. var sc = self._room.connection.sdpConstraints;
  178. for (var name in sc.mandatory) {
  179. if (sc.mandatory.hasOwnProperty(name)) {
  180. inputConstraints.mandatory[name] = sc.mandatory[name];
  181. }
  182. }
  183. inputConstraints.optional.concat(sc.optional);
  184. checkMediaDataChannelSettings(peerBrowser.agent, peerBrowser.version,
  185. function(beOfferer, unifiedOfferConstraints) {
  186. // attempt to force make firefox not to offer datachannel.
  187. // we will not be using datachannel in MCU
  188. if (window.webrtcDetectedType === 'moz' && peerBrowser.agent === 'MCU') {
  189. unifiedOfferConstraints.mandatory = unifiedOfferConstraints.mandatory || {};
  190. unifiedOfferConstraints.mandatory.MozDontOfferDataChannel = true;
  191. beOfferer = true;
  192. }
  193.  
  194. unifiedOfferConstraints.mandatory.iceRestart = true;
  195. peerBrowser.os = peerBrowser.os || '';
  196.  
  197. if (!(peerBrowser.agent === 'MCU' || self._hasMCU)) {
  198. /*
  199. // for windows firefox to mac chrome interopability
  200. if (window.webrtcDetectedBrowser === 'firefox' &&
  201. window.navigator.platform.indexOf('Win') === 0 &&
  202. peerBrowser.agent !== 'firefox' &&
  203. peerBrowser.agent !== 'MCU' &&
  204. peerBrowser.os.indexOf('Mac') === 0) {
  205. beOfferer = false;
  206. }*/
  207. if (window.webrtcDetectedBrowser === 'firefox' && peerBrowser.agent !== 'firefox') {
  208. beOfferer = false;
  209. }
  210. } else {
  211. beOfferer = true;
  212. }
  213.  
  214. if (beOfferer) {
  215. if (window.webrtcDetectedBrowser === 'firefox' && window.webrtcDetectedVersion >= 32) {
  216. unifiedOfferConstraints = {
  217. offerToReceiveAudio: true,
  218. offerToReceiveVideo: true
  219. };
  220.  
  221. /*if (window.webrtcDetectedVersion > 37) {
  222. unifiedOfferConstraints = {};
  223. }*/
  224. }
  225.  
  226. log.debug([targetMid, null, null, 'Creating offer with config:'], unifiedOfferConstraints);
  227.  
  228. pc.createOffer(function(offer) {
  229. log.debug([targetMid, null, null, 'Created offer'], offer);
  230. self._setLocalAndSendMessage(targetMid, offer);
  231. }, function(error) {
  232. self._trigger('handshakeProgress', self.HANDSHAKE_PROGRESS.ERROR,
  233. targetMid, error);
  234. log.error([targetMid, null, null, 'Failed creating an offer:'], error);
  235. }, unifiedOfferConstraints);
  236. } else {
  237. log.debug([targetMid, null, null, 'User\'s browser is not eligible to create ' +
  238. 'the offer to the other peer. Requesting other peer to create the offer instead'
  239. ], peerBrowser);
  240.  
  241. self._sendChannelMessage({
  242. type: self._SIG_MESSAGE_TYPE.WELCOME,
  243. mid: self._user.sid,
  244. rid: self._room.id,
  245. agent: window.webrtcDetectedBrowser,
  246. version: window.webrtcDetectedVersion,
  247. os: window.navigator.platform,
  248. userInfo: self.getPeerInfo(),
  249. target: targetMid,
  250. weight: -1,
  251. sessionType: !!self._mediaScreen ? 'screensharing' : 'stream'
  252. });
  253. }
  254. }, inputConstraints);
  255. };
  256.  
  257. /**
  258. * Responses to the <code>OFFER</code> session description received and
  259. * creates an <code>ANSWER</code> session description to sent
  260. * to the associated Peer to complete the WebRTC handshake layer.
  261. * @method _doAnswer
  262. * @param {String} targetMid The Peer ID to send the <code>ANSWER</code> to.
  263. * @private
  264. * @for Skylink
  265. * @component Peer
  266. * @since 0.1.0
  267. */
  268. Skylink.prototype._doAnswer = function(targetMid) {
  269. var self = this;
  270. log.log([targetMid, null, null, 'Creating answer with config:'],
  271. self._room.connection.sdpConstraints);
  272. var pc = self._peerConnections[targetMid];
  273. if (pc) {
  274. // No ICE restart constraints for createAnswer as it fails in chrome 48
  275. // { iceRestart: true }
  276. pc.createAnswer(function(answer) {
  277. log.debug([targetMid, null, null, 'Created answer'], answer);
  278. self._setLocalAndSendMessage(targetMid, answer);
  279. }, function(error) {
  280. log.error([targetMid, null, null, 'Failed creating an answer:'], error);
  281. self._trigger('handshakeProgress', self.HANDSHAKE_PROGRESS.ERROR, targetMid, error);
  282. });//, self._room.connection.sdpConstraints);
  283. } else {
  284. /* Houston ..*/
  285. log.error([targetMid, null, null, 'Requested to create an answer but user ' +
  286. 'does not have any existing connection to peer']);
  287. return;
  288. }
  289. };
  290.  
  291. /**
  292. * Starts the waiting timeout for a "healthy" connection
  293. * with associated Peer connection.
  294. * It waits for any existing Peer "healthy" state in successful
  295. * {{#crossLink "Skylink/_peerConnectionHealth:attr"}}_peerConnectionHealth{{/crossLink}}.
  296. * If timeout has reached it's limit and does not have any "healthy" connection state
  297. * with Peer connection, it will restart the connection again with
  298. * {{#crossLink "Skylink/_restartPeerConnection:method"}}_restartPeerConnection(){{/crossLink}}.
  299. * This sets the timeout object associated with the Peer into
  300. * {{#crossLink "Skylink/_peerConnectionHealthTimers"}}_peerConnectionHealthTimers(){{/crossLink}}.
  301. * @method _startPeerConnectionHealthCheck
  302. * @param {String} peerId The Peer ID to start a waiting timeout for a "healthy" connection.
  303. * @param {Boolean} [toOffer=false] The flag that indicates if Peer connection
  304. * is an offerer or an answerer for an accurate timeout waiting time.
  305. * @private
  306. * @component Peer
  307. * @for Skylink
  308. * @since 0.5.5
  309. */
  310. Skylink.prototype._startPeerConnectionHealthCheck = function (peerId, toOffer) {
  311. var self = this;
  312. var timer = (self._enableIceTrickle && !self._peerIceTrickleDisabled[peerId]) ?
  313. (toOffer ? 12500 : 10000) : 50000;
  314. timer = (self._hasMCU) ? 105000 : timer;
  315.  
  316. // increase timeout for android/ios
  317. /*var agent = self.getPeerInfo(peerId).agent;
  318. if (['Android', 'iOS'].indexOf(agent.name) > -1) {
  319. timer = 105000;
  320. }*/
  321.  
  322. timer += self._retryCount*10000;
  323.  
  324. log.log([peerId, 'PeerConnectionHealth', null,
  325. 'Initializing check for peer\'s connection health']);
  326.  
  327. if (self._peerConnectionHealthTimers[peerId]) {
  328. // might be a re-handshake again
  329. self._stopPeerConnectionHealthCheck(peerId);
  330. }
  331.  
  332. self._peerConnectionHealthTimers[peerId] = setTimeout(function () {
  333. // re-handshaking should start here.
  334. var connectionStable = false;
  335. var pc = self._peerConnections[peerId];
  336.  
  337. if (pc) {
  338. var dc = (self._dataChannels[peerId] || {}).main;
  339.  
  340. var dcConnected = pc.hasMainChannel ? dc && dc.readyState === self.DATA_CHANNEL_STATE.OPEN : true;
  341. var iceConnected = pc.iceConnectionState === self.ICE_CONNECTION_STATE.CONNECTED ||
  342. pc.iceConnectionState === self.ICE_CONNECTION_STATE.COMPLETED;
  343. var signalingConnected = pc.signalingState === self.PEER_CONNECTION_STATE.STABLE;
  344.  
  345. connectionStable = dcConnected && iceConnected && signalingConnected;
  346.  
  347. log.debug([peerId, 'PeerConnectionHealth', null, 'Connection status'], {
  348. dcConnected: dcConnected,
  349. iceConnected: iceConnected,
  350. signalingConnected: signalingConnected
  351. });
  352. }
  353.  
  354. log.debug([peerId, 'PeerConnectionHealth', null, 'Require reconnection?'], connectionStable);
  355.  
  356. if (!connectionStable) {
  357. log.warn([peerId, 'PeerConnectionHealth', null, 'Peer\'s health timer ' +
  358. 'has expired'], 10000);
  359.  
  360. // clear the loop first
  361. self._stopPeerConnectionHealthCheck(peerId);
  362.  
  363. log.debug([peerId, 'PeerConnectionHealth', null,
  364. 'Ice connection state time out. Re-negotiating connection']);
  365.  
  366. //Maximum increament is 5 minutes
  367. if (self._retryCount<30){
  368. //Increase after each consecutive connection failure
  369. self._retryCount++;
  370. }
  371.  
  372. // do a complete clean
  373. if (!self._hasMCU) {
  374. self._restartPeerConnection(peerId, true, true, null, false);
  375. } else {
  376. self._restartMCUConnection();
  377. }
  378. } else {
  379. self._peerConnectionHealth[peerId] = true;
  380. }
  381. }, timer);
  382. };
  383.  
  384. /**
  385. * Stops the waiting timeout for a "healthy" connection associated
  386. * with the Peer.
  387. * @method _stopPeerConnectionHealthCheck
  388. * @param {String} peerId The Peer ID to stop a waiting
  389. * timeout for a "healthy" connection.
  390. * @private
  391. * @component Peer
  392. * @for Skylink
  393. * @since 0.5.5
  394. */
  395. Skylink.prototype._stopPeerConnectionHealthCheck = function (peerId) {
  396. var self = this;
  397.  
  398. if (self._peerConnectionHealthTimers[peerId]) {
  399. log.debug([peerId, 'PeerConnectionHealth', null,
  400. 'Stopping peer connection health timer check']);
  401.  
  402. clearTimeout(self._peerConnectionHealthTimers[peerId]);
  403. delete self._peerConnectionHealthTimers[peerId];
  404.  
  405. } else {
  406. log.debug([peerId, 'PeerConnectionHealth', null,
  407. 'Peer connection health does not have a timer check']);
  408. }
  409. };
  410.  
  411. /**
  412. * Sets the WebRTC handshake layer session description into the
  413. * Peer <code>RTCPeerConnection</code> object <i><code>
  414. * RTCPeerConnection.setLocalDescription()</code></i> associated
  415. * with the Peer connection.
  416. * @method _setLocalAndSendMessage
  417. * @param {String} targetMid The Peer ID to send the session description to
  418. * after setting into the associated <code>RTCPeerConnection</code> object.
  419. * @param {JSON} sessionDescription The <code>OFFER</code> or an <code>ANSWER</code>
  420. * session description to set to the associated Peer after setting into
  421. * the <code>RTCPeerConnection</code> object.
  422. * @trigger handshakeProgress
  423. * @private
  424. * @component Peer
  425. * @for Skylink
  426. * @since 0.5.2
  427. */
  428. Skylink.prototype._setLocalAndSendMessage = function(targetMid, sessionDescription) {
  429. var self = this;
  430. var pc = self._peerConnections[targetMid];
  431.  
  432. /*if (sessionDescription.type === self.HANDSHAKE_PROGRESS.ANSWER && pc.setAnswer) {
  433. log.log([targetMid, 'RTCSessionDescription', sessionDescription.type,
  434. 'Ignoring session description. User has already set local answer'], sessionDescription);
  435. return;
  436. }
  437. if (sessionDescription.type === self.HANDSHAKE_PROGRESS.OFFER && pc.setOffer) {
  438. log.log([targetMid, 'RTCSessionDescription', sessionDescription.type,
  439. 'Ignoring session description. User has already set local offer'], sessionDescription);
  440. return;
  441. }*/
  442.  
  443. // NOTE ALEX: handle the pc = 0 case, just to be sure
  444. var sdpLines = sessionDescription.sdp.split('\r\n');
  445.  
  446. // remove h264 invalid pref
  447. sdpLines = self._removeSDPFirefoxH264Pref(sdpLines);
  448. // Check if stereo was enabled
  449. if (self._streamSettings.hasOwnProperty('audio')) {
  450. if (self._streamSettings.audio.stereo) {
  451. self._addSDPStereo(sdpLines);
  452. }
  453. }
  454.  
  455. log.info([targetMid, null, null, 'Requested stereo:'], (self._streamSettings.audio ?
  456. (self._streamSettings.audio.stereo ? self._streamSettings.audio.stereo : false) :
  457. false));
  458.  
  459. // set sdp bitrate
  460. if (self._streamSettings.hasOwnProperty('bandwidth')) {
  461. var peerSettings = (self._peerInformations[targetMid] || {}).settings || {};
  462.  
  463. sdpLines = self._setSDPBitrate(sdpLines, peerSettings);
  464. }
  465.  
  466. // set sdp resolution
  467. /*if (self._streamSettings.hasOwnProperty('video')) {
  468. sdpLines = self._setSDPVideoResolution(sdpLines, self._streamSettings.video);
  469. }*/
  470.  
  471. self._streamSettings.bandwidth = self._streamSettings.bandwidth || {};
  472.  
  473. self._streamSettings.video = self._streamSettings.video || false;
  474.  
  475. log.info([targetMid, null, null, 'Custom bandwidth settings:'], {
  476. audio: (self._streamSettings.bandwidth.audio || 'Not set') + ' kB/s',
  477. video: (self._streamSettings.bandwidth.video || 'Not set') + ' kB/s',
  478. data: (self._streamSettings.bandwidth.data || 'Not set') + ' kB/s'
  479. });
  480.  
  481. if (self._streamSettings.video.hasOwnProperty('frameRate') &&
  482. self._streamSettings.video.hasOwnProperty('resolution')){
  483. log.info([targetMid, null, null, 'Custom resolution settings:'], {
  484. frameRate: (self._streamSettings.video.frameRate || 'Not set') + ' fps',
  485. width: (self._streamSettings.video.resolution.width || 'Not set') + ' px',
  486. height: (self._streamSettings.video.resolution.height || 'Not set') + ' px'
  487. });
  488. }
  489.  
  490. // set video codec
  491. if (self._selectedVideoCodec !== self.VIDEO_CODEC.AUTO) {
  492. sdpLines = self._setSDPVideoCodec(sdpLines);
  493. } else {
  494. log.log([targetMid, null, null, 'Not setting any video codec']);
  495. }
  496.  
  497. // set audio codec
  498. if (self._selectedAudioCodec !== self.AUDIO_CODEC.AUTO) {
  499. sdpLines = self._setSDPAudioCodec(sdpLines);
  500. } else {
  501. log.log([targetMid, null, null, 'Not setting any audio codec']);
  502. }
  503.  
  504. sessionDescription.sdp = sdpLines.join('\r\n');
  505.  
  506. // NOTE ALEX: opus should not be used for mobile
  507. // Set Opus as the preferred codec in SDP if Opus is present.
  508. //sessionDescription.sdp = preferOpus(sessionDescription.sdp);
  509. // limit bandwidth
  510. //sessionDescription.sdp = this._limitBandwidth(sessionDescription.sdp);
  511. log.log([targetMid, 'RTCSessionDescription', sessionDescription.type,
  512. 'Updated session description:'], sessionDescription);
  513.  
  514. pc.setLocalDescription(sessionDescription, function() {
  515. log.debug([targetMid, sessionDescription.type, 'Local description set']);
  516. self._trigger('handshakeProgress', sessionDescription.type, targetMid);
  517. if (sessionDescription.type === self.HANDSHAKE_PROGRESS.ANSWER) {
  518. pc.setAnswer = 'local';
  519. } else {
  520. pc.setOffer = 'local';
  521. }
  522. var shouldWaitForCandidates = false;
  523.  
  524. if (!(self._enableIceTrickle && !self._peerIceTrickleDisabled[targetMid])) {
  525. shouldWaitForCandidates = true;
  526. // there is no sessiondescription created at first go
  527. if (pc.setOffer === 'remote' || pc.setAnswer === 'remote') {
  528. shouldWaitForCandidates = false;
  529. }
  530. }
  531. if (!shouldWaitForCandidates) {
  532. // make checks for firefox session description
  533. if (sessionDescription.type === self.HANDSHAKE_PROGRESS.ANSWER && window.webrtcDetectedBrowser === 'firefox') {
  534. sessionDescription.sdp = self._addSDPSsrcFirefoxAnswer(targetMid, sessionDescription.sdp);
  535. }
  536.  
  537. self._sendChannelMessage({
  538. type: sessionDescription.type,
  539. sdp: sessionDescription.sdp,
  540. mid: self._user.sid,
  541. target: targetMid,
  542. rid: self._room.id
  543. });
  544. } else {
  545. log.log([targetMid, 'RTCSessionDescription', sessionDescription.type,
  546. 'Waiting for Ice gathering to complete to prevent Ice trickle']);
  547. }
  548. }, function(error) {
  549. self._trigger('handshakeProgress', self.HANDSHAKE_PROGRESS.ERROR, targetMid, error);
  550. log.error([targetMid, 'RTCSessionDescription', sessionDescription.type,
  551. 'Failed setting local description: '], error);
  552. });
  553. };
  554.