You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TraceablePeerConnection.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. var XMPPEvents = require("../../service/xmpp/XMPPEvents");
  2. function TraceablePeerConnection(ice_config, constraints, session) {
  3. var self = this;
  4. var RTCPeerconnection = navigator.mozGetUserMedia ? mozRTCPeerConnection : webkitRTCPeerConnection;
  5. this.peerconnection = new RTCPeerconnection(ice_config, constraints);
  6. this.updateLog = [];
  7. this.stats = {};
  8. this.statsinterval = null;
  9. this.maxstats = 0; // limit to 300 values, i.e. 5 minutes; set to 0 to disable
  10. var Interop = require('sdp-interop').Interop;
  11. this.interop = new Interop();
  12. var Simulcast = require('sdp-simulcast');
  13. this.simulcast = new Simulcast({numOfLayers: 3, explodeRemoteSimulcast: false});
  14. // override as desired
  15. this.trace = function (what, info) {
  16. //console.warn('WTRACE', what, info);
  17. self.updateLog.push({
  18. time: new Date(),
  19. type: what,
  20. value: info || ""
  21. });
  22. };
  23. this.onicecandidate = null;
  24. this.peerconnection.onicecandidate = function (event) {
  25. self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' '));
  26. if (self.onicecandidate !== null) {
  27. self.onicecandidate(event);
  28. }
  29. };
  30. this.onaddstream = null;
  31. this.peerconnection.onaddstream = function (event) {
  32. self.trace('onaddstream', event.stream.id);
  33. if (self.onaddstream !== null) {
  34. self.onaddstream(event);
  35. }
  36. };
  37. this.onremovestream = null;
  38. this.peerconnection.onremovestream = function (event) {
  39. self.trace('onremovestream', event.stream.id);
  40. if (self.onremovestream !== null) {
  41. self.onremovestream(event);
  42. }
  43. };
  44. this.onsignalingstatechange = null;
  45. this.peerconnection.onsignalingstatechange = function (event) {
  46. self.trace('onsignalingstatechange', self.signalingState);
  47. if (self.onsignalingstatechange !== null) {
  48. self.onsignalingstatechange(event);
  49. }
  50. };
  51. this.oniceconnectionstatechange = null;
  52. this.peerconnection.oniceconnectionstatechange = function (event) {
  53. self.trace('oniceconnectionstatechange', self.iceConnectionState);
  54. if (self.oniceconnectionstatechange !== null) {
  55. self.oniceconnectionstatechange(event);
  56. }
  57. };
  58. this.onnegotiationneeded = null;
  59. this.peerconnection.onnegotiationneeded = function (event) {
  60. self.trace('onnegotiationneeded');
  61. if (self.onnegotiationneeded !== null) {
  62. self.onnegotiationneeded(event);
  63. }
  64. };
  65. self.ondatachannel = null;
  66. this.peerconnection.ondatachannel = function (event) {
  67. self.trace('ondatachannel', event);
  68. if (self.ondatachannel !== null) {
  69. self.ondatachannel(event);
  70. }
  71. };
  72. if (!navigator.mozGetUserMedia && this.maxstats) {
  73. this.statsinterval = window.setInterval(function() {
  74. self.peerconnection.getStats(function(stats) {
  75. var results = stats.result();
  76. for (var i = 0; i < results.length; ++i) {
  77. //console.log(results[i].type, results[i].id, results[i].names())
  78. var now = new Date();
  79. results[i].names().forEach(function (name) {
  80. var id = results[i].id + '-' + name;
  81. if (!self.stats[id]) {
  82. self.stats[id] = {
  83. startTime: now,
  84. endTime: now,
  85. values: [],
  86. times: []
  87. };
  88. }
  89. self.stats[id].values.push(results[i].stat(name));
  90. self.stats[id].times.push(now.getTime());
  91. if (self.stats[id].values.length > self.maxstats) {
  92. self.stats[id].values.shift();
  93. self.stats[id].times.shift();
  94. }
  95. self.stats[id].endTime = now;
  96. });
  97. }
  98. });
  99. }, 1000);
  100. }
  101. };
  102. dumpSDP = function(description) {
  103. if (typeof description === 'undefined' || description == null) {
  104. return '';
  105. }
  106. return 'type: ' + description.type + '\r\n' + description.sdp;
  107. };
  108. if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
  109. TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
  110. TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
  111. TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
  112. var desc = this.peerconnection.localDescription;
  113. this.trace('getLocalDescription::preTransform', dumpSDP(desc));
  114. // if we're running on FF, transform to Plan B first.
  115. if (navigator.mozGetUserMedia) {
  116. desc = this.interop.toPlanB(desc);
  117. this.trace('getLocalDescription::postTransform (Plan B)', dumpSDP(desc));
  118. }
  119. return desc;
  120. });
  121. TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() {
  122. var desc = this.peerconnection.remoteDescription;
  123. this.trace('getRemoteDescription::preTransform', dumpSDP(desc));
  124. // if we're running on FF, transform to Plan B first.
  125. if (navigator.mozGetUserMedia) {
  126. desc = this.interop.toPlanB(desc);
  127. this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
  128. }
  129. return desc;
  130. });
  131. }
  132. TraceablePeerConnection.prototype.addStream = function (stream) {
  133. this.trace('addStream', stream.id);
  134. try
  135. {
  136. this.peerconnection.addStream(stream);
  137. }
  138. catch (e)
  139. {
  140. console.error(e);
  141. return;
  142. }
  143. };
  144. TraceablePeerConnection.prototype.removeStream = function (stream, stopStreams) {
  145. this.trace('removeStream', stream.id);
  146. if(stopStreams) {
  147. stream.getAudioTracks().forEach(function (track) {
  148. track.stop();
  149. });
  150. stream.getVideoTracks().forEach(function (track) {
  151. track.stop();
  152. });
  153. }
  154. try {
  155. // FF doesn't support this yet.
  156. this.peerconnection.removeStream(stream);
  157. } catch (e) {
  158. console.error(e);
  159. }
  160. };
  161. TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
  162. this.trace('createDataChannel', label, opts);
  163. return this.peerconnection.createDataChannel(label, opts);
  164. };
  165. TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
  166. this.trace('setLocalDescription::preTransform', dumpSDP(description));
  167. // if we're running on FF, transform to Plan A first.
  168. if (navigator.mozGetUserMedia) {
  169. description = this.interop.toUnifiedPlan(description);
  170. this.trace('setLocalDescription::postTransform (Plan A)', dumpSDP(description));
  171. }
  172. var self = this;
  173. this.peerconnection.setLocalDescription(description,
  174. function () {
  175. self.trace('setLocalDescriptionOnSuccess');
  176. successCallback();
  177. },
  178. function (err) {
  179. self.trace('setLocalDescriptionOnFailure', err);
  180. failureCallback(err);
  181. }
  182. );
  183. /*
  184. if (this.statsinterval === null && this.maxstats > 0) {
  185. // start gathering stats
  186. }
  187. */
  188. };
  189. TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
  190. this.trace('setRemoteDescription::preTransform', dumpSDP(description));
  191. // TODO the focus should squeze or explode the remote simulcast
  192. description = this.simulcast.mungeRemoteDescription(description);
  193. this.trace('setRemoteDescription::postTransform (simulcast)', dumpSDP(description));
  194. // if we're running on FF, transform to Plan A first.
  195. if (navigator.mozGetUserMedia) {
  196. description = this.interop.toUnifiedPlan(description);
  197. this.trace('setRemoteDescription::postTransform (Plan A)', dumpSDP(description));
  198. }
  199. var self = this;
  200. this.peerconnection.setRemoteDescription(description,
  201. function () {
  202. self.trace('setRemoteDescriptionOnSuccess');
  203. successCallback();
  204. },
  205. function (err) {
  206. self.trace('setRemoteDescriptionOnFailure', err);
  207. failureCallback(err);
  208. }
  209. );
  210. /*
  211. if (this.statsinterval === null && this.maxstats > 0) {
  212. // start gathering stats
  213. }
  214. */
  215. };
  216. TraceablePeerConnection.prototype.close = function () {
  217. this.trace('stop');
  218. if (this.statsinterval !== null) {
  219. window.clearInterval(this.statsinterval);
  220. this.statsinterval = null;
  221. }
  222. this.peerconnection.close();
  223. };
  224. TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) {
  225. var self = this;
  226. this.trace('createOffer', JSON.stringify(constraints, null, ' '));
  227. this.peerconnection.createOffer(
  228. function (offer) {
  229. self.trace('createOfferOnSuccess::preTransform', dumpSDP(offer));
  230. // if we're running on FF, transform to Plan B first.
  231. // NOTE this is not tested because in meet the focus generates the
  232. // offer.
  233. if (navigator.mozGetUserMedia) {
  234. offer = self.interop.toPlanB(offer);
  235. self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer));
  236. }
  237. if (config.enableSimulcast && self.simulcast.isSupported()) {
  238. offer = self.simulcast.mungeLocalDescription(offer);
  239. self.trace('createOfferOnSuccess::postTransform (simulcast)', dumpSDP(offer));
  240. }
  241. successCallback(offer);
  242. },
  243. function(err) {
  244. self.trace('createOfferOnFailure', err);
  245. failureCallback(err);
  246. },
  247. constraints
  248. );
  249. };
  250. TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) {
  251. var self = this;
  252. this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
  253. this.peerconnection.createAnswer(
  254. function (answer) {
  255. self.trace('createAnswerOnSuccess::preTransfom', dumpSDP(answer));
  256. // if we're running on FF, transform to Plan A first.
  257. if (navigator.mozGetUserMedia) {
  258. answer = self.interop.toPlanB(answer);
  259. self.trace('createAnswerOnSuccess::postTransfom (Plan B)', dumpSDP(answer));
  260. }
  261. if (config.enableSimulcast && self.simulcast.isSupported()) {
  262. answer = self.simulcast.mungeLocalDescription(answer);
  263. self.trace('createAnswerOnSuccess::postTransfom (simulcast)', dumpSDP(answer));
  264. }
  265. successCallback(answer);
  266. },
  267. function(err) {
  268. self.trace('createAnswerOnFailure', err);
  269. failureCallback(err);
  270. },
  271. constraints
  272. );
  273. };
  274. TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) {
  275. var self = this;
  276. this.trace('addIceCandidate', JSON.stringify(candidate, null, ' '));
  277. this.peerconnection.addIceCandidate(candidate);
  278. /* maybe later
  279. this.peerconnection.addIceCandidate(candidate,
  280. function () {
  281. self.trace('addIceCandidateOnSuccess');
  282. successCallback();
  283. },
  284. function (err) {
  285. self.trace('addIceCandidateOnFailure', err);
  286. failureCallback(err);
  287. }
  288. );
  289. */
  290. };
  291. TraceablePeerConnection.prototype.getStats = function(callback, errback) {
  292. if (navigator.mozGetUserMedia) {
  293. // ignore for now...
  294. if(!errback)
  295. errback = function () {
  296. }
  297. this.peerconnection.getStats(null,callback,errback);
  298. } else {
  299. this.peerconnection.getStats(callback);
  300. }
  301. };
  302. module.exports = TraceablePeerConnection;