File: source/ice-candidate.js

  1. /**
  2. * <blockquote class="info">
  3. * Learn more about how ICE works in this
  4. * <a href="https://temasys.com.sg/ice-what-is-this-sorcery/">article here</a>.
  5. * </blockquote>
  6. * The list of Peer connection ICE gathering states.
  7. * @attribute CANDIDATE_GENERATION_STATE
  8. * @param {String} GATHERING <small>Value <code>"gathering"</code></small>
  9. * The value of the state when Peer connection is gathering ICE candidates.
  10. * <small>These ICE candidates are sent to Peer for its connection to check for a suitable matching
  11. * pair of ICE candidates to establish an ICE connection for stream audio, video and data.
  12. * See <a href="#event_iceConnectionState"><code>iceConnectionState</code> event</a> for ICE connection status.</small>
  13. * <small>This state cannot happen until Peer connection remote <code>"offer"</code> / <code>"answer"</code>
  14. * session description is set. See <a href="#event_peerConnectionState">
  15. * <code>peerConnectionState</code> event</a> for session description exchanging status.</small>
  16. * @param {String} COMPLETED <small>Value <code>"completed"</code></small>
  17. * The value of the state when Peer connection gathering of ICE candidates has completed.
  18. * @type JSON
  19. * @readOnly
  20. * @for Skylink
  21. * @since 0.4.1
  22. */
  23. Skylink.prototype.CANDIDATE_GENERATION_STATE = {
  24. NEW: 'new',
  25. GATHERING: 'gathering',
  26. COMPLETED: 'completed'
  27. };
  28.  
  29. /**
  30. * Stores the list of buffered ICE candidates that is received before
  31. * remote session description is received and set.
  32. * @attribute _peerCandidatesQueue
  33. * @param {Array} <#peerId> The list of the Peer connection buffered ICE candidates received.
  34. * @param {Object} <#peerId>.<#index> The Peer connection buffered ICE candidate received.
  35. * @type JSON
  36. * @private
  37. * @for Skylink
  38. * @since 0.5.1
  39. */
  40. Skylink.prototype._peerCandidatesQueue = {};
  41.  
  42. /**
  43. * Stores the list of Peer connection ICE candidates.
  44. * @attribute _gatheredCandidates
  45. * @param {JSON} <#peerId> The list of the Peer connection ICE candidates.
  46. * @param {JSON} <#peerId>.sending The list of the Peer connection ICE candidates sent.
  47. * @param {JSON} <#peerId>.receiving The list of the Peer connection ICE candidates received.
  48. * @type JSON
  49. * @private
  50. * @for Skylink
  51. * @since 0.6.14
  52. */
  53. Skylink.prototype._gatheredCandidates = {};
  54.  
  55. /**
  56. * Function that handles the Peer connection gathered ICE candidate to be sent.
  57. * @method _onIceCandidate
  58. * @private
  59. * @for Skylink
  60. * @since 0.1.0
  61. */
  62. Skylink.prototype._onIceCandidate = function(targetMid, candidate) {
  63. var self = this;
  64.  
  65. if (candidate.candidate) {
  66. var messageCan = candidate.candidate.split(' ');
  67. var candidateType = messageCan[7];
  68. log.debug([targetMid, 'RTCIceCandidate', null, 'Created and sending ' +
  69. candidateType + ' candidate:'], candidate);
  70.  
  71. if (self._forceTURN && candidateType !== 'relay') {
  72. if (!self._hasMCU) {
  73. log.warn([targetMid, 'RTCICECandidate', null, 'Ignoring sending of "' + candidateType +
  74. '" candidate as TURN connections is forced'], candidate);
  75. return;
  76. }
  77.  
  78. log.warn([targetMid, 'RTCICECandidate', null, 'Not ignoring sending of "' + candidateType +
  79. '" candidate although TURN connections is forced as MCU is present'], candidate);
  80. }
  81.  
  82. if (!self._gatheredCandidates[targetMid]) {
  83. self._gatheredCandidates[targetMid] = {
  84. sending: { host: [], srflx: [], relay: [] },
  85. receiving: { host: [], srflx: [], relay: [] }
  86. };
  87. }
  88.  
  89. self._gatheredCandidates[targetMid].sending[candidateType].push({
  90. sdpMid: candidate.sdpMid,
  91. sdpMLineIndex: candidate.sdpMLineIndex,
  92. candidate: candidate.candidate
  93. });
  94.  
  95. if (!self._enableIceTrickle) {
  96. log.warn([targetMid, 'RTCICECandidate', null, 'Ignoring sending of "' + candidateType +
  97. '" candidate as trickle ICE is disabled'], candidate);
  98. return;
  99. }
  100.  
  101. self._sendChannelMessage({
  102. type: self._SIG_MESSAGE_TYPE.CANDIDATE,
  103. label: candidate.sdpMLineIndex,
  104. id: candidate.sdpMid,
  105. candidate: candidate.candidate,
  106. mid: self._user.sid,
  107. target: targetMid,
  108. rid: self._room.id
  109. });
  110.  
  111. } else {
  112. log.debug([targetMid, 'RTCIceCandidate', null, 'End of gathering']);
  113. self._trigger('candidateGenerationState', self.CANDIDATE_GENERATION_STATE.COMPLETED,
  114. targetMid);
  115. // Disable Ice trickle option
  116. if (!self._enableIceTrickle) {
  117. var sessionDescription = self._peerConnections[targetMid].localDescription;
  118.  
  119. // make checks for firefox session description
  120. if (sessionDescription.type === self.HANDSHAKE_PROGRESS.ANSWER && window.webrtcDetectedBrowser === 'firefox') {
  121. sessionDescription.sdp = self._addSDPSsrcFirefoxAnswer(targetMid, sessionDescription.sdp);
  122. }
  123.  
  124. self._sendChannelMessage({
  125. type: sessionDescription.type,
  126. sdp: sessionDescription.sdp,
  127. mid: self._user.sid,
  128. //agent: window.webrtcDetectedBrowser,
  129. userInfo: self._getUserInfo(),
  130. target: targetMid,
  131. rid: self._room.id
  132. });
  133. }
  134.  
  135. // We should remove this.. this could be due to ICE failures
  136. // Adding this fix is bad
  137. // Does the restart in the case when the candidates are extremely a lot
  138. /*var doACandidateRestart = self._addedCandidates[targetMid].relay.length > 20 &&
  139. (window.webrtcDetectedBrowser === 'chrome' || window.webrtcDetectedBrowser === 'opera');
  140.  
  141. log.debug([targetMid, 'RTCIceCandidate', null, 'Relay candidates generated length'], self._addedCandidates[targetMid].relay.length);
  142.  
  143. if (doACandidateRestart) {
  144. setTimeout(function () {
  145. if (self._peerConnections[targetMid]) {
  146. if(self._peerConnections[targetMid].iceConnectionState !== self.ICE_CONNECTION_STATE.CONNECTED &&
  147. self._peerConnections[targetMid].iceConnectionState !== self.ICE_CONNECTION_STATE.COMPLETED) {
  148. // restart
  149. self._restartPeerConnection(targetMid, true, true, null, false);
  150. }
  151. }
  152. }, self._addedCandidates[targetMid].relay.length * 50);
  153. }*/
  154. }
  155. };
  156.  
  157. /**
  158. * Function that buffers the Peer connection ICE candidate when received
  159. * before remote session description is received and set.
  160. * @method _addIceCandidateToQueue
  161. * @private
  162. * @for Skylink
  163. * @since 0.5.2
  164. */
  165. Skylink.prototype._addIceCandidateToQueue = function(targetMid, candidate) {
  166. log.debug([targetMid, null, null, 'Queued candidate to add after ' +
  167. 'setRemoteDescription'], candidate);
  168. this._peerCandidatesQueue[targetMid] =
  169. this._peerCandidatesQueue[targetMid] || [];
  170. this._peerCandidatesQueue[targetMid].push(candidate);
  171. };
  172.  
  173. /**
  174. * Function that handles when the Peer connection received ICE candidate
  175. * has been added or processed successfully.
  176. * Separated in a function to prevent jshint errors.
  177. * @method _onAddIceCandidateSuccess
  178. * @private
  179. * @for Skylink
  180. * @since 0.5.9
  181. */
  182. Skylink.prototype._onAddIceCandidateSuccess = function () {
  183. log.debug([null, 'RTCICECandidate', null, 'Successfully added ICE candidate']);
  184. };
  185.  
  186. /**
  187. * Function that handles when the Peer connection received ICE candidate
  188. * has failed adding or processing.
  189. * Separated in a function to prevent jshint errors.
  190. * @method _onAddIceCandidateFailure
  191. * @private
  192. * @for Skylink
  193. * @since 0.5.9
  194. */
  195. Skylink.prototype._onAddIceCandidateFailure = function (error) {
  196. log.error([null, 'RTCICECandidate', null, 'Error'], error);
  197. };
  198.  
  199. /**
  200. * Function that adds all the Peer connection buffered ICE candidates received.
  201. * This should be called only after the remote session description is received and set.
  202. * @method _addIceCandidateFromQueue
  203. * @private
  204. * @for Skylink
  205. * @since 0.5.2
  206. */
  207. Skylink.prototype._addIceCandidateFromQueue = function(targetMid) {
  208. this._peerCandidatesQueue[targetMid] =
  209. this._peerCandidatesQueue[targetMid] || [];
  210. if(this._peerCandidatesQueue[targetMid].length > 0) {
  211. for (var i = 0; i < this._peerCandidatesQueue[targetMid].length; i++) {
  212. var candidate = this._peerCandidatesQueue[targetMid][i];
  213. log.debug([targetMid, null, null, 'Added queued candidate'], candidate);
  214. this._peerConnections[targetMid].addIceCandidate(candidate,
  215. this._onAddIceCandidateSuccess, this._onAddIceCandidateFailure);
  216. }
  217. delete this._peerCandidatesQueue[targetMid];
  218. } else {
  219. log.log([targetMid, null, null, 'No queued candidates to add']);
  220. }
  221. };