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.

strophe.jingle.adapter.js 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. function TraceablePeerConnection(ice_config, constraints) {
  2. var self = this;
  3. var RTCPeerconnection = navigator.mozGetUserMedia ? mozRTCPeerConnection : webkitRTCPeerConnection;
  4. this.peerconnection = new RTCPeerconnection(ice_config, constraints);
  5. this.updateLog = [];
  6. this.stats = {};
  7. this.statsinterval = null;
  8. this.maxstats = 300; // limit to 300 values, i.e. 5 minutes; set to 0 to disable
  9. // override as desired
  10. this.trace = function(what, info) {
  11. //console.warn('WTRACE', what, info);
  12. self.updateLog.push({
  13. time: new Date(),
  14. type: what,
  15. value: info || ""
  16. });
  17. };
  18. this.onicecandidate = null;
  19. this.peerconnection.onicecandidate = function (event) {
  20. self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' '));
  21. if (self.onicecandidate !== null) {
  22. self.onicecandidate(event);
  23. }
  24. };
  25. this.onaddstream = null;
  26. this.peerconnection.onaddstream = function (event) {
  27. self.trace('onaddstream', event.stream.id);
  28. if (self.onaddstream !== null) {
  29. self.onaddstream(event);
  30. }
  31. };
  32. this.onremovestream = null;
  33. this.peerconnection.onremovestream = function (event) {
  34. self.trace('onremovestream', event.stream.id);
  35. if (self.onremovestream !== null) {
  36. self.onremovestream(event);
  37. }
  38. };
  39. this.onsignalingstatechange = null;
  40. this.peerconnection.onsignalingstatechange = function (event) {
  41. self.trace('onsignalingstatechange', event.srcElement.signalingState);
  42. if (self.onsignalingstatechange !== null) {
  43. self.onsignalingstatechange(event);
  44. }
  45. };
  46. this.oniceconnectionstatechange = null;
  47. this.peerconnection.oniceconnectionstatechange = function (event) {
  48. self.trace('oniceconnectionstatechange', event.srcElement.iceConnectionState);
  49. if (self.oniceconnectionstatechange !== null) {
  50. self.oniceconnectionstatechange(event);
  51. }
  52. };
  53. this.onnegotiationneeded = null;
  54. this.peerconnection.onnegotiationneeded = function (event) {
  55. self.trace('onnegotiationneeded');
  56. if (self.onnegotiationneeded !== null) {
  57. self.onnegotiationneeded(event);
  58. }
  59. };
  60. self.ondatachannel = null;
  61. this.peerconnection.ondatachannel = function (event) {
  62. self.trace('ondatachannel', event);
  63. if (self.ondatachannel !== null) {
  64. self.ondatachannel(event);
  65. }
  66. }
  67. if (!navigator.mozGetUserMedia) {
  68. this.statsinterval = window.setInterval(function() {
  69. self.peerconnection.getStats(function(stats) {
  70. var results = stats.result();
  71. for (var i = 0; i < results.length; ++i) {
  72. //console.log(results[i].type, results[i].id, results[i].names())
  73. var now = new Date();
  74. results[i].names().forEach(function (name) {
  75. var id = results[i].id + '-' + name;
  76. if (!self.stats[id]) {
  77. self.stats[id] = {
  78. startTime: now,
  79. endTime: now,
  80. values: [],
  81. times: []
  82. };
  83. }
  84. self.stats[id].values.push(results[i].stat(name));
  85. self.stats[id].times.push(now.getTime());
  86. if (self.stats[id].values.length > self.maxstats) {
  87. self.stats[id].values.shift();
  88. self.stats[id].times.shift();
  89. }
  90. self.stats[id].endTime = now;
  91. });
  92. }
  93. });
  94. }, 1000);
  95. }
  96. };
  97. dumpSDP = function(description) {
  98. return 'type: ' + description.type + '\r\n' + description.sdp;
  99. }
  100. if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
  101. TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
  102. TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
  103. TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() { return this.peerconnection.localDescription; });
  104. TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() { return this.peerconnection.remoteDescription; });
  105. }
  106. TraceablePeerConnection.prototype.addStream = function (stream) {
  107. this.trace('addStream', stream.id);
  108. this.peerconnection.addStream(stream);
  109. };
  110. TraceablePeerConnection.prototype.removeStream = function (stream) {
  111. this.trace('removeStream', stream.id);
  112. this.peerconnection.removeStream(stream);
  113. };
  114. TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
  115. this.trace('createDataChannel', label, opts);
  116. this.peerconnection.createDataChannel(label, opts);
  117. }
  118. TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
  119. var self = this;
  120. this.trace('setLocalDescription', dumpSDP(description));
  121. this.peerconnection.setLocalDescription(description,
  122. function () {
  123. self.trace('setLocalDescriptionOnSuccess');
  124. successCallback();
  125. },
  126. function (err) {
  127. self.trace('setLocalDescriptionOnFailure', err);
  128. failureCallback(err);
  129. }
  130. );
  131. /*
  132. if (this.statsinterval === null && this.maxstats > 0) {
  133. // start gathering stats
  134. }
  135. */
  136. };
  137. TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
  138. var self = this;
  139. this.trace('setRemoteDescription', dumpSDP(description));
  140. this.peerconnection.setRemoteDescription(description,
  141. function () {
  142. self.trace('setRemoteDescriptionOnSuccess');
  143. successCallback();
  144. },
  145. function (err) {
  146. self.trace('setRemoteDescriptionOnFailure', err);
  147. failureCallback(err);
  148. }
  149. );
  150. /*
  151. if (this.statsinterval === null && this.maxstats > 0) {
  152. // start gathering stats
  153. }
  154. */
  155. };
  156. TraceablePeerConnection.prototype.close = function () {
  157. this.trace('stop');
  158. if (this.statsinterval !== null) {
  159. window.clearInterval(this.statsinterval);
  160. this.statsinterval = null;
  161. }
  162. this.peerconnection.close();
  163. };
  164. TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) {
  165. var self = this;
  166. this.trace('createOffer', JSON.stringify(constraints, null, ' '));
  167. this.peerconnection.createOffer(
  168. function (offer) {
  169. self.trace('createOfferOnSuccess', dumpSDP(offer));
  170. successCallback(offer);
  171. },
  172. function(err) {
  173. self.trace('createOfferOnFailure', err);
  174. failureCallback(err);
  175. },
  176. constraints
  177. );
  178. };
  179. TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) {
  180. var self = this;
  181. this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
  182. this.peerconnection.createAnswer(
  183. function (answer) {
  184. self.trace('createAnswerOnSuccess', dumpSDP(answer));
  185. successCallback(answer);
  186. },
  187. function(err) {
  188. self.trace('createAnswerOnFailure', err);
  189. failureCallback(err);
  190. },
  191. constraints
  192. );
  193. };
  194. TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) {
  195. var self = this;
  196. this.trace('addIceCandidate', JSON.stringify(candidate, null, ' '));
  197. this.peerconnection.addIceCandidate(candidate);
  198. /* maybe later
  199. this.peerconnection.addIceCandidate(candidate,
  200. function () {
  201. self.trace('addIceCandidateOnSuccess');
  202. successCallback();
  203. },
  204. function (err) {
  205. self.trace('addIceCandidateOnFailure', err);
  206. failureCallback(err);
  207. }
  208. );
  209. */
  210. };
  211. TraceablePeerConnection.prototype.getStats = function(callback, errback) {
  212. if (navigator.mozGetUserMedia) {
  213. // ignore for now...
  214. } else {
  215. this.peerconnection.getStats(callback);
  216. }
  217. };
  218. // mozilla chrome compat layer -- very similar to adapter.js
  219. function setupRTC() {
  220. var RTC = null;
  221. if (navigator.mozGetUserMedia) {
  222. console.log('This appears to be Firefox');
  223. var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
  224. if (version >= 22) {
  225. RTC = {
  226. peerconnection: mozRTCPeerConnection,
  227. browser: 'firefox',
  228. getUserMedia: navigator.mozGetUserMedia.bind(navigator),
  229. attachMediaStream: function (element, stream) {
  230. element[0].mozSrcObject = stream;
  231. element[0].play();
  232. },
  233. pc_constraints: {}
  234. };
  235. if (!MediaStream.prototype.getVideoTracks)
  236. MediaStream.prototype.getVideoTracks = function () { return []; };
  237. if (!MediaStream.prototype.getAudioTracks)
  238. MediaStream.prototype.getAudioTracks = function () { return []; };
  239. RTCSessionDescription = mozRTCSessionDescription;
  240. RTCIceCandidate = mozRTCIceCandidate;
  241. }
  242. } else if (navigator.webkitGetUserMedia) {
  243. console.log('This appears to be Chrome');
  244. RTC = {
  245. peerconnection: webkitRTCPeerConnection,
  246. browser: 'chrome',
  247. getUserMedia: navigator.webkitGetUserMedia.bind(navigator),
  248. attachMediaStream: function (element, stream) {
  249. element.attr('src', webkitURL.createObjectURL(stream));
  250. },
  251. // DTLS should now be enabled by default but..
  252. pc_constraints: {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]}
  253. };
  254. if (navigator.userAgent.indexOf('Android') != -1) {
  255. RTC.pc_constraints = {}; // disable DTLS on Android
  256. }
  257. if (!webkitMediaStream.prototype.getVideoTracks) {
  258. webkitMediaStream.prototype.getVideoTracks = function () {
  259. return this.videoTracks;
  260. };
  261. }
  262. if (!webkitMediaStream.prototype.getAudioTracks) {
  263. webkitMediaStream.prototype.getAudioTracks = function () {
  264. return this.audioTracks;
  265. };
  266. }
  267. }
  268. if (RTC === null) {
  269. try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
  270. }
  271. return RTC;
  272. }
  273. function getUserMediaWithConstraints(um, resolution, bandwidth, fps) {
  274. var constraints = {audio: false, video: false};
  275. if (um.indexOf('video') >= 0) {
  276. constraints.video = {mandatory: {}};// same behaviour as true
  277. }
  278. if (um.indexOf('audio') >= 0) {
  279. constraints.audio = {};// same behaviour as true
  280. }
  281. if (um.indexOf('screen') >= 0) {
  282. constraints.video = {
  283. "mandatory": {
  284. "chromeMediaSource": "screen"
  285. }
  286. };
  287. }
  288. if (resolution && !constraints.video) {
  289. constraints.video = {mandatory: {}};// same behaviour as true
  290. }
  291. // see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions
  292. switch (resolution) {
  293. // 16:9 first
  294. case '1080':
  295. case 'fullhd':
  296. constraints.video.mandatory.minWidth = 1920;
  297. constraints.video.mandatory.minHeight = 1080;
  298. constraints.video.mandatory.minAspectRatio = 1.77;
  299. break;
  300. case '720':
  301. case 'hd':
  302. constraints.video.mandatory.minWidth = 1280;
  303. constraints.video.mandatory.minHeight = 720;
  304. constraints.video.mandatory.minAspectRatio = 1.77;
  305. break;
  306. case '360':
  307. constraints.video.mandatory.minWidth = 640;
  308. constraints.video.mandatory.minHeight = 360;
  309. constraints.video.mandatory.minAspectRatio = 1.77;
  310. break;
  311. case '180':
  312. constraints.video.mandatory.minWidth = 320;
  313. constraints.video.mandatory.minHeight = 180;
  314. constraints.video.mandatory.minAspectRatio = 1.77;
  315. break;
  316. // 4:3
  317. case '960':
  318. constraints.video.mandatory.minWidth = 960;
  319. constraints.video.mandatory.minHeight = 720;
  320. break;
  321. case '640':
  322. case 'vga':
  323. constraints.video.mandatory.minWidth = 640;
  324. constraints.video.mandatory.minHeight = 480;
  325. break;
  326. case '320':
  327. constraints.video.mandatory.minWidth = 320;
  328. constraints.video.mandatory.minHeight = 240;
  329. break;
  330. default:
  331. if (navigator.userAgent.indexOf('Android') != -1) {
  332. constraints.video.mandatory.minWidth = 320;
  333. constraints.video.mandatory.minHeight = 240;
  334. constraints.video.mandatory.maxFrameRate = 15;
  335. }
  336. break;
  337. }
  338. if (bandwidth) { // doesn't work currently, see webrtc issue 1846
  339. if (!constraints.video) constraints.video = {mandatory: {}};//same behaviour as true
  340. constraints.video.optional = [{bandwidth: bandwidth}];
  341. }
  342. if (fps) { // for some cameras it might be necessary to request 30fps
  343. // so they choose 30fps mjpg over 10fps yuy2
  344. if (!constraints.video) constraints.video = {mandatory: {}};// same behaviour as tru;
  345. constraints.video.mandatory.minFrameRate = fps;
  346. }
  347. try {
  348. RTC.getUserMedia(constraints,
  349. function (stream) {
  350. console.log('onUserMediaSuccess');
  351. $(document).trigger('mediaready.jingle', [stream]);
  352. },
  353. function (error) {
  354. console.warn('Failed to get access to local media. Error ', error);
  355. $(document).trigger('mediafailure.jingle');
  356. });
  357. } catch (e) {
  358. console.error('GUM failed: ', e);
  359. $(document).trigger('mediafailure.jingle');
  360. }
  361. }