File: source/peer-handshake.js

  1. /**
  2. * Function that creates the Peer connection offer session description.
  3. * @method _doOffer
  4. * @private
  5. * @for Skylink
  6. * @since 0.5.2
  7. */
  8. Skylink.prototype._doOffer = function(targetMid, iceRestart, mergeMessageWithOffer) {
  9. var self = this;
  10. var pc = self._peerConnections[targetMid];
  11.  
  12. // Added checks to ensure that connection object is defined first
  13. if (!pc) {
  14. log.warn([targetMid, 'RTCSessionDescription', 'offer', 'Dropping of creating of offer ' +
  15. 'as connection does not exists']);
  16. return;
  17. }
  18.  
  19. // Added checks to ensure that state is "stable" if setting local "offer"
  20. if (pc.signalingState !== self.PEER_CONNECTION_STATE.STABLE) {
  21. log.warn([targetMid, 'RTCSessionDescription', 'offer',
  22. 'Dropping of creating of offer as signalingState is not "' +
  23. self.PEER_CONNECTION_STATE.STABLE + '" ->'], pc.signalingState);
  24. return;
  25. }
  26.  
  27. var offerConstraints = {
  28. offerToReceiveAudio: !(!self._sdpSettings.connection.audio && targetMid !== 'MCU') && self._getSDPCommonSupports(targetMid).video,
  29. offerToReceiveVideo: !(!self._sdpSettings.connection.video && targetMid !== 'MCU') && self._getSDPCommonSupports(targetMid).audio,
  30. iceRestart: !!((self._peerInformations[targetMid] || {}).config || {}).enableIceRestart &&
  31. iceRestart && self._enableIceRestart,
  32. voiceActivityDetection: self._voiceActivityDetection
  33. };
  34.  
  35. // Add stream only at offer/answer end
  36. if (!self._hasMCU || targetMid === 'MCU') {
  37. //self._compareTrackCounts(targetMid);
  38. self._addLocalMediaStreams(targetMid);
  39. }
  40.  
  41. if (self._initOptions.enableDataChannel && self._peerInformations[targetMid] &&
  42. self._peerInformations[targetMid].config.enableDataChannel/* &&
  43. !(!self._sdpSettings.connection.data && targetMid !== 'MCU')*/) {
  44. // Edge doesn't support datachannels yet
  45. if (!(self._dataChannels[targetMid] && self._dataChannels[targetMid].main)) {
  46. self._createDataChannel(targetMid);
  47. self._peerConnections[targetMid].hasMainChannel = true;
  48. }
  49. }
  50.  
  51. log.debug([targetMid, null, null, 'Creating offer with config:'], offerConstraints);
  52.  
  53. pc.endOfCandidates = false;
  54.  
  55. if (self._peerConnStatus[targetMid]) {
  56. self._peerConnStatus[targetMid].sdpConstraints = offerConstraints;
  57. }
  58.  
  59. var onSuccessCbFn = function(offer) {
  60. log.debug([targetMid, null, null, 'Created offer'], offer);
  61. self._handleNegotiationStats('create_offer', targetMid, offer, false);
  62. self._setLocalAndSendMessage(targetMid, offer, mergeMessageWithOffer);
  63. };
  64.  
  65. var onErrorCbFn = function(error) {
  66. log.error([targetMid, null, null, 'Failed creating an offer:'], error);
  67. self._handleNegotiationStats('error_create_offer', targetMid, null, false, error);
  68. self._trigger('handshakeProgress', self.HANDSHAKE_PROGRESS.ERROR, targetMid, error);
  69. };
  70.  
  71. pc.createOffer(onSuccessCbFn, onErrorCbFn, AdapterJS.webrtcDetectedType === 'plugin' ? {
  72. mandatory: {
  73. OfferToReceiveAudio: offerConstraints.offerToReceiveAudio,
  74. OfferToReceiveVideo: offerConstraints.offerToReceiveVideo,
  75. iceRestart: offerConstraints.iceRestart,
  76. voiceActivityDetection: offerConstraints.voiceActivityDetection
  77. }
  78. } : offerConstraints);
  79. };
  80.  
  81. /**
  82. * Function that creates the Peer connection answer session description.
  83. * This comes after receiving and setting the offer session description.
  84. * @method _doAnswer
  85. * @private
  86. * @for Skylink
  87. * @since 0.1.0
  88. */
  89. Skylink.prototype._doAnswer = function(targetMid) {
  90. var self = this;
  91. log.log([targetMid, null, null, 'Creating answer with config:'],
  92. self._room.connection.sdpConstraints);
  93. var pc = self._peerConnections[targetMid];
  94.  
  95. // Added checks to ensure that connection object is defined first
  96. if (!pc) {
  97. log.warn([targetMid, 'RTCSessionDescription', 'answer', 'Dropping of creating of answer ' +
  98. 'as connection does not exists']);
  99. return;
  100. }
  101.  
  102. // Added checks to ensure that state is "have-remote-offer" if setting local "answer"
  103. if (pc.signalingState !== self.PEER_CONNECTION_STATE.HAVE_REMOTE_OFFER) {
  104. log.warn([targetMid, 'RTCSessionDescription', 'answer',
  105. 'Dropping of creating of answer as signalingState is not "' +
  106. self.PEER_CONNECTION_STATE.HAVE_REMOTE_OFFER + '" ->'], pc.signalingState);
  107. return;
  108. }
  109.  
  110. var answerConstraints = AdapterJS.webrtcDetectedBrowser === 'edge' ? {
  111. offerToReceiveVideo: !(!self._sdpSettings.connection.audio && targetMid !== 'MCU') &&
  112. self._getSDPCommonSupports(targetMid, pc.remoteDescription).video,
  113. offerToReceiveAudio: !(!self._sdpSettings.connection.video && targetMid !== 'MCU') &&
  114. self._getSDPCommonSupports(targetMid, pc.remoteDescription).audio,
  115. voiceActivityDetection: self._voiceActivityDetection
  116. } : undefined;
  117.  
  118. // Add stream only at offer/answer end
  119. if (!self._hasMCU || targetMid === 'MCU') {
  120. self._addLocalMediaStreams(targetMid);
  121. }
  122.  
  123. if (self._peerConnStatus[targetMid]) {
  124. self._peerConnStatus[targetMid].sdpConstraints = answerConstraints;
  125. }
  126.  
  127. var onSuccessCbFn = function(answer) {
  128. log.debug([targetMid, null, null, 'Created answer'], answer);
  129. self._handleNegotiationStats('create_answer', targetMid, answer, false);
  130.  
  131. if (AdapterJS.webrtcDetectedBrowser === 'firefox') {
  132. self._setOriginalDTLSRole(answer, false);
  133. answer.sdp = self._modifyDTLSRole(answer);
  134. }
  135.  
  136. self._setLocalAndSendMessage(targetMid, answer);
  137. };
  138.  
  139. var onErrorCbFn = function(error) {
  140. log.error([targetMid, null, null, 'Failed creating an answer:'], error);
  141. self._handleNegotiationStats('error_create_answer', targetMid, null, false, error);
  142. self._trigger('handshakeProgress', self.HANDSHAKE_PROGRESS.ERROR, targetMid, error);
  143. };
  144.  
  145. // No ICE restart constraints for createAnswer as it fails in chrome 48
  146. // { iceRestart: true }
  147. pc.createAnswer(onSuccessCbFn, onErrorCbFn, answerConstraints);
  148. };
  149.  
  150. /**
  151. * Function that sets the local session description and sends to Peer.
  152. * If trickle ICE is disabled, the local session description will be sent after
  153. * ICE gathering has been completed.
  154. * @method _setLocalAndSendMessage
  155. * @private
  156. * @for Skylink
  157. * @since 0.5.2
  158. */
  159. Skylink.prototype._setLocalAndSendMessage = function(targetMid, _sessionDescription, mergeMessage) {
  160. var self = this;
  161. var pc = self._peerConnections[targetMid];
  162.  
  163. // Added checks to ensure that sessionDescription is defined first
  164. if (!(!!_sessionDescription && !!_sessionDescription.sdp)) {
  165. log.warn([targetMid, 'RTCSessionDescription', null, 'Local session description is undefined ->'], _sessionDescription);
  166. return;
  167. }
  168.  
  169. // Added checks to ensure that connection object is defined first
  170. if (!pc) {
  171. log.warn([targetMid, 'RTCSessionDescription', _sessionDescription.type,
  172. 'Local session description will not be set as connection does not exists ->'], _sessionDescription);
  173. self._handleNegotiationStats('dropped_' + sessionDescription.type, targetMid, sessionDescription, false, 'Peer connection does not exists');
  174. return;
  175.  
  176. } else if (_sessionDescription.type === self.HANDSHAKE_PROGRESS.OFFER &&
  177. pc.signalingState !== self.PEER_CONNECTION_STATE.STABLE) {
  178. log.warn([targetMid, 'RTCSessionDescription', _sessionDescription.type, 'Local session description ' +
  179. 'will not be set as signaling state is "' + pc.signalingState + '" ->'], _sessionDescription);
  180. self._handleNegotiationStats('dropped_offer', targetMid, sessionDescription, false, 'Peer connection state is "' + pc.signalingState + '"');
  181. return;
  182.  
  183. // Added checks to ensure that state is "have-remote-offer" if setting local "answer"
  184. } else if (_sessionDescription.type === self.HANDSHAKE_PROGRESS.ANSWER &&
  185. pc.signalingState !== self.PEER_CONNECTION_STATE.HAVE_REMOTE_OFFER) {
  186. log.warn([targetMid, 'RTCSessionDescription', _sessionDescription.type, 'Local session description ' +
  187. 'will not be set as signaling state is "' + pc.signalingState + '" ->'], _sessionDescription);
  188. self._handleNegotiationStats('dropped_answer', targetMid, sessionDescription, false, 'Peer connection state is "' + pc.signalingState + '"');
  189. return;
  190.  
  191. // Added checks if there is a current local sessionDescription being processing before processing this one
  192. } else if (pc.processingLocalSDP) {
  193. log.warn([targetMid, 'RTCSessionDescription', _sessionDescription.type,
  194. 'Local session description will not be set as another is being processed ->'], _sessionDescription);
  195. self._handleNegotiationStats('dropped_' + sessionDescription.type, targetMid, sessionDescription, false, 'Peer connection is currently processing an existing sdp');
  196. return;
  197. }
  198.  
  199. pc.processingLocalSDP = true;
  200.  
  201. // Sets and expected receiving codecs etc.
  202. var sessionDescription = {
  203. type: _sessionDescription.type,
  204. sdp: _sessionDescription.sdp
  205. };
  206.  
  207. sessionDescription.sdp = self._removeSDPFirefoxH264Pref(targetMid, sessionDescription);
  208. sessionDescription.sdp = self._setSDPCodecParams(targetMid, sessionDescription);
  209. sessionDescription.sdp = self._removeSDPUnknownAptRtx(targetMid, sessionDescription);
  210. sessionDescription.sdp = self._removeSDPCodecs(targetMid, sessionDescription);
  211. sessionDescription.sdp = self._handleSDPConnectionSettings(targetMid, sessionDescription, 'local');
  212. sessionDescription.sdp = self._removeSDPREMBPackets(targetMid, sessionDescription);
  213. sessionDescription.sdp = self._setSCTPport(targetMid, sessionDescription);
  214.  
  215. if (self._peerConnectionConfig.disableBundle) {
  216. sessionDescription.sdp = sessionDescription.sdp.replace(/a=group:BUNDLE.*\r\n/gi, '');
  217. }
  218.  
  219. log.log([targetMid, 'RTCSessionDescription', sessionDescription.type,
  220. 'Local session description updated ->'], sessionDescription.sdp);
  221.  
  222. var onSuccessCbFn = function() {
  223. log.debug([targetMid, 'RTCSessionDescription', sessionDescription.type,
  224. 'Local session description has been set ->'], sessionDescription);
  225.  
  226. pc.processingLocalSDP = false;
  227.  
  228. self._handleNegotiationStats('set_' + sessionDescription.type, targetMid, sessionDescription, false);
  229. self._trigger('handshakeProgress', sessionDescription.type, targetMid);
  230.  
  231. if (sessionDescription.type === self.HANDSHAKE_PROGRESS.ANSWER) {
  232. pc.setAnswer = 'local';
  233. } else {
  234. pc.setOffer = 'local';
  235. }
  236.  
  237. if (!self._initOptions.enableIceTrickle && !pc.gathered) {
  238. log.log([targetMid, 'RTCSessionDescription', sessionDescription.type,
  239. 'Local session description sending is halted to complete ICE gathering.']);
  240. return;
  241. }
  242.  
  243. var messageToSend = {
  244. type: sessionDescription.type,
  245. sdp: sessionDescription.sdp,
  246. mid: self._user.sid,
  247. target: targetMid,
  248. rid: self._room.id,
  249. userInfo: self._getUserInfo(targetMid)
  250. };
  251.  
  252. // Merging Restart and Offer messages. The already present keys in offer message will not be overwritten.
  253. // Only news keys from mergeMessage are added.
  254. if (mergeMessage && Object.keys(mergeMessage).length) {
  255. var keys = Object.keys(mergeMessage);
  256. var currentMessageKeys = Object.keys(messageToSend);
  257. for (var keyIndex = 0; keyIndex < keys.length; keyIndex++) {
  258. var key = keys[keyIndex];
  259. if (currentMessageKeys.indexOf(key) === -1) {
  260. messageToSend[key] = mergeMessage[key];
  261. }
  262. }
  263. }
  264.  
  265. self._sendChannelMessage(messageToSend);
  266. self._handleNegotiationStats(sessionDescription.type, targetMid, sessionDescription, false);
  267. };
  268.  
  269. var onErrorCbFn = function(error) {
  270. log.error([targetMid, 'RTCSessionDescription', sessionDescription.type, 'Local description failed setting ->'], error);
  271.  
  272. pc.processingLocalSDP = false;
  273.  
  274. self._handleNegotiationStats('error_set_' + sessionDescription.type, targetMid, sessionDescription, false, error);
  275. self._trigger('handshakeProgress', self.HANDSHAKE_PROGRESS.ERROR, targetMid, error);
  276. };
  277.  
  278. pc.setLocalDescription(new RTCSessionDescription(sessionDescription), onSuccessCbFn, onErrorCbFn);
  279. };
  280.