Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

statistics.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /* global require */
  2. var LocalStats = require("./LocalStatsCollector.js");
  3. var logger = require("jitsi-meet-logger").getLogger(__filename);
  4. var RTPStats = require("./RTPStatsCollector.js");
  5. var EventEmitter = require("events");
  6. var StatisticsEvents = require("../../service/statistics/Events");
  7. var CallStats = require("./CallStats");
  8. var ScriptUtil = require('../util/ScriptUtil');
  9. // Since callstats.io is a third party, we cannot guarantee the quality of their
  10. // service. More specifically, their server may take noticeably long time to
  11. // respond. Consequently, it is in our best interest (in the sense that the
  12. // intergration of callstats.io is pretty important to us but not enough to
  13. // allow it to prevent people from joining a conference) to (1) start
  14. // downloading their API as soon as possible and (2) do the downloading
  15. // asynchronously.
  16. function loadCallStatsAPI() {
  17. ScriptUtil.loadScript(
  18. 'https://api.callstats.io/static/callstats.min.js',
  19. /* async */ true,
  20. /* prepend */ true);
  21. // FIXME At the time of this writing, we hope that the callstats.io API will
  22. // have loaded by the time we needed it (i.e. CallStats.init is invoked).
  23. }
  24. /**
  25. * Log stats via the focus once every this many milliseconds.
  26. */
  27. var LOG_INTERVAL = 60000;
  28. function Statistics(xmpp, options) {
  29. this.rtpStats = null;
  30. this.eventEmitter = new EventEmitter();
  31. this.xmpp = xmpp;
  32. this.options = options || {};
  33. this.callStatsIntegrationEnabled
  34. = this.options.callStatsID && this.options.callStatsSecret
  35. // Even though AppID and AppSecret may be specified, the integration
  36. // of callstats.io may be disabled because of globally-disallowed
  37. // requests to any third parties.
  38. && (this.options.disableThirdPartyRequests !== true);
  39. if(this.callStatsIntegrationEnabled)
  40. loadCallStatsAPI();
  41. this.callStats = null;
  42. /**
  43. * Send the stats already saved in rtpStats to be logged via the focus.
  44. */
  45. this.logStatsIntervalId = null;
  46. }
  47. Statistics.audioLevelsEnabled = false;
  48. Statistics.prototype.startRemoteStats = function (peerconnection) {
  49. if(!Statistics.audioLevelsEnabled)
  50. return;
  51. this.stopRemoteStats();
  52. try {
  53. this.rtpStats
  54. = new RTPStats(peerconnection, 200, 2000, this.eventEmitter);
  55. this.rtpStats.start();
  56. } catch (e) {
  57. this.rtpStats = null;
  58. logger.error('Failed to start collecting remote statistics: ' + e);
  59. }
  60. if (this.rtpStats) {
  61. this.logStatsIntervalId = setInterval(function () {
  62. var stats = this.rtpStats.getCollectedStats();
  63. if (this.xmpp.sendLogs(stats)) {
  64. this.rtpStats.clearCollectedStats();
  65. }
  66. }.bind(this), LOG_INTERVAL);
  67. }
  68. };
  69. Statistics.localStats = [];
  70. Statistics.startLocalStats = function (stream, callback) {
  71. if(!Statistics.audioLevelsEnabled)
  72. return;
  73. var localStats = new LocalStats(stream, 200, callback);
  74. this.localStats.push(localStats);
  75. localStats.start();
  76. };
  77. Statistics.prototype.addAudioLevelListener = function(listener) {
  78. if(!Statistics.audioLevelsEnabled)
  79. return;
  80. this.eventEmitter.on(StatisticsEvents.AUDIO_LEVEL, listener);
  81. };
  82. Statistics.prototype.removeAudioLevelListener = function(listener) {
  83. if(!Statistics.audioLevelsEnabled)
  84. return;
  85. this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);
  86. };
  87. Statistics.prototype.addConnectionStatsListener = function (listener) {
  88. this.eventEmitter.on(StatisticsEvents.CONNECTION_STATS, listener);
  89. };
  90. Statistics.prototype.removeConnectionStatsListener = function (listener) {
  91. this.eventEmitter.removeListener(StatisticsEvents.CONNECTION_STATS, listener);
  92. };
  93. Statistics.prototype.dispose = function () {
  94. if(Statistics.audioLevelsEnabled) {
  95. Statistics.stopAllLocalStats();
  96. this.stopRemoteStats();
  97. if(this.eventEmitter)
  98. this.eventEmitter.removeAllListeners();
  99. }
  100. };
  101. Statistics.stopAllLocalStats = function () {
  102. if(!Statistics.audioLevelsEnabled)
  103. return;
  104. for(var i = 0; i < this.localStats.length; i++)
  105. this.localStats[i].stop();
  106. this.localStats = [];
  107. };
  108. Statistics.stopLocalStats = function (stream) {
  109. if(!Statistics.audioLevelsEnabled)
  110. return;
  111. for(var i = 0; i < Statistics.localStats.length; i++)
  112. if(Statistics.localStats[i].stream === stream){
  113. var localStats = Statistics.localStats.splice(i, 1);
  114. localStats[0].stop();
  115. break;
  116. }
  117. };
  118. Statistics.prototype.stopRemoteStats = function () {
  119. if (!Statistics.audioLevelsEnabled || !this.rtpStats) {
  120. return;
  121. }
  122. this.rtpStats.stop();
  123. this.rtpStats = null;
  124. if (this.logStatsIntervalId) {
  125. clearInterval(this.logStatsIntervalId);
  126. this.logStatsIntervalId = null;
  127. }
  128. };
  129. //CALSTATS METHODS
  130. /**
  131. * Initializes the callstats.io API.
  132. * @param peerConnection {JingleSessionPC} the session object
  133. * @param Settings {Settings} the settings instance. Declared in
  134. * /modules/settings/Settings.js
  135. */
  136. Statistics.prototype.startCallStats = function (session, settings) {
  137. if(this.callStatsIntegrationEnabled && !this.callstats) {
  138. this.callstats = new CallStats(session, settings, this.options);
  139. }
  140. };
  141. /**
  142. * Returns true if the callstats integration is enabled, otherwise returns
  143. * false.
  144. *
  145. * @returns true if the callstats integration is enabled, otherwise returns
  146. * false.
  147. */
  148. Statistics.prototype.isCallstatsEnabled = function () {
  149. return this.callStatsIntegrationEnabled;
  150. };
  151. /**
  152. * Notifies CallStats for ice connection failed
  153. * @param {RTCPeerConnection} pc connection on which failure occured.
  154. */
  155. Statistics.prototype.sendIceConnectionFailedEvent = function (pc) {
  156. if(this.callstats)
  157. this.callstats.sendIceConnectionFailedEvent(pc, this.callstats);
  158. };
  159. /**
  160. * Notifies CallStats for mute events
  161. * @param mute {boolean} true for muted and false for not muted
  162. * @param type {String} "audio"/"video"
  163. */
  164. Statistics.prototype.sendMuteEvent = function (muted, type) {
  165. if(this.callstats)
  166. CallStats.sendMuteEvent(muted, type, this.callstats);
  167. };
  168. /**
  169. * Notifies CallStats for screen sharing events
  170. * @param start {boolean} true for starting screen sharing and
  171. * false for not stopping
  172. */
  173. Statistics.prototype.sendScreenSharingEvent = function (start) {
  174. if(this.callstats)
  175. CallStats.sendScreenSharingEvent(start, this.callstats);
  176. };
  177. /**
  178. * Notifies the statistics module that we are now the dominant speaker of the
  179. * conference.
  180. */
  181. Statistics.prototype.sendDominantSpeakerEvent = function () {
  182. if(this.callstats)
  183. CallStats.sendDominantSpeakerEvent(this.callstats);
  184. };
  185. /**
  186. * Lets the underlying statistics module know where is given SSRC rendered by
  187. * providing renderer tag ID.
  188. * @param ssrc {number} the SSRC of the stream
  189. * @param isLocal {boolean} <tt>true<tt> if this stream is local or
  190. * <tt>false</tt> otherwise.
  191. * @param usageLabel {string} meaningful usage label of this stream like
  192. * 'microphone', 'camera' or 'screen'.
  193. * @param containerId {string} the id of media 'audio' or 'video' tag which
  194. * renders the stream.
  195. */
  196. Statistics.prototype.associateStreamWithVideoTag =
  197. function (ssrc, isLocal, usageLabel, containerId) {
  198. if(this.callstats) {
  199. this.callstats.associateStreamWithVideoTag(
  200. ssrc, isLocal, usageLabel, containerId);
  201. }
  202. };
  203. /**
  204. * Notifies CallStats that getUserMedia failed.
  205. *
  206. * @param {Error} e error to send
  207. */
  208. Statistics.prototype.sendGetUserMediaFailed = function (e) {
  209. if(this.callstats)
  210. CallStats.sendGetUserMediaFailed(e, this.callstats);
  211. };
  212. /**
  213. * Notifies CallStats that getUserMedia failed.
  214. *
  215. * @param {Error} e error to send
  216. */
  217. Statistics.sendGetUserMediaFailed = function (e) {
  218. CallStats.sendGetUserMediaFailed(e, null);
  219. };
  220. /**
  221. * Notifies CallStats that peer connection failed to create offer.
  222. *
  223. * @param {Error} e error to send
  224. * @param {RTCPeerConnection} pc connection on which failure occured.
  225. */
  226. Statistics.prototype.sendCreateOfferFailed = function (e, pc) {
  227. if(this.callstats)
  228. CallStats.sendCreateOfferFailed(e, pc, this.callstats);
  229. };
  230. /**
  231. * Notifies CallStats that peer connection failed to create answer.
  232. *
  233. * @param {Error} e error to send
  234. * @param {RTCPeerConnection} pc connection on which failure occured.
  235. */
  236. Statistics.prototype.sendCreateAnswerFailed = function (e, pc) {
  237. if(this.callstats)
  238. CallStats.sendCreateAnswerFailed(e, pc, this.callstats);
  239. };
  240. /**
  241. * Notifies CallStats that peer connection failed to set local description.
  242. *
  243. * @param {Error} e error to send
  244. * @param {RTCPeerConnection} pc connection on which failure occured.
  245. */
  246. Statistics.prototype.sendSetLocalDescFailed = function (e, pc) {
  247. if(this.callstats)
  248. CallStats.sendSetLocalDescFailed(e, pc, this.callstats);
  249. };
  250. /**
  251. * Notifies CallStats that peer connection failed to set remote description.
  252. *
  253. * @param {Error} e error to send
  254. * @param {RTCPeerConnection} pc connection on which failure occured.
  255. */
  256. Statistics.prototype.sendSetRemoteDescFailed = function (e, pc) {
  257. if(this.callstats)
  258. CallStats.sendSetRemoteDescFailed(e, pc, this.callstats);
  259. };
  260. /**
  261. * Notifies CallStats that peer connection failed to add ICE candidate.
  262. *
  263. * @param {Error} e error to send
  264. * @param {RTCPeerConnection} pc connection on which failure occured.
  265. */
  266. Statistics.prototype.sendAddIceCandidateFailed = function (e, pc) {
  267. if(this.callstats)
  268. CallStats.sendAddIceCandidateFailed(e, pc, this.callstats);
  269. };
  270. /**
  271. * Notifies CallStats that there is an unhandled error on the page.
  272. *
  273. * @param {Error} e error to send
  274. * @param {RTCPeerConnection} pc connection on which failure occured.
  275. */
  276. Statistics.prototype.sendUnhandledError = function (e) {
  277. if(this.callstats)
  278. CallStats.sendUnhandledError(e, this.callstats);
  279. };
  280. /**
  281. * Notifies CallStats that there is unhandled exception.
  282. *
  283. * @param {Error} e error to send
  284. */
  285. Statistics.sendUnhandledError = function (e) {
  286. CallStats.sendUnhandledError(e, null);
  287. };
  288. /**
  289. * Sends the given feedback through CallStats.
  290. *
  291. * @param overall an integer between 1 and 5 indicating the user feedback
  292. * @param detailed detailed feedback from the user. Not yet used
  293. */
  294. Statistics.prototype.sendFeedback = function(overall, detailed) {
  295. if(this.callstats)
  296. this.callstats.sendFeedback(overall, detailed);
  297. };
  298. Statistics.LOCAL_JID = require("../../service/statistics/constants").LOCAL_JID;
  299. module.exports = Statistics;