-
Notifications
You must be signed in to change notification settings - Fork 1
/
respoke-stats.min.map
1 lines (1 loc) · 28.3 KB
/
respoke-stats.min.map
1
{"version":3,"sources":["../plugins/respoke-stats/respoke-stats.js"],"names":["factory","define","amd","exports","require","respoke","log","MediaStats","params","format","report","aliases","Object","keys","forEach","oldName","name","newname","members","connection","foundIncomingNetworkPaths","foundOutgoingNetworkPaths","JSON","parse","stringify","cons","googChannelId","googLocalAddress","googRemoteAddress","googLocalCandidateType","googRemoteCandidateType","googReadable","googRtt","googTransportType","googWritable","localaudio","googCodecName","bytesSent","packetsSent","localvideo","remoteaudio","bytesReceived","packetsReceived","remotevideo","MediaStatsParser","startsWith","string","value","slice","length","initStats","sdp","pc","remoteDescription","localDescription","remote","local","side","rsdp","lines","split","mediaType","lineIndex","line","lbits","ssrc","substring","interestingStats","match","onStats","timer","setInterval","that","getStats","done","err","error","message","stack","statsInterval","warn","buildStats","rawStats","stats","results","result","allStats","state","iceGatheringState","icegatheringState","iceConnectionState","statType","eachStat","rule","filter","typeMatch","type","keyMatch","stat","key","timestamp","periodLength","oldStats","testInt","parseInt","isNaN","deltas","undefined","indexOf","charAt","toUpperCase","Class","className","peerConnection","interval","deferred","Q","defer","retVal","handlePromise","promise","onSuccess","onError","args","navigator","mozGetUserMedia","push","resolve","reject","Error","apply","stopStats","clearInterval"],"mappings":"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","file":"respoke-stats.min.js","sourcesContent":["/*\n * Copyright 2015, Digium, Inc.\n * All rights reserved.\n *\n * This source code is licensed under The MIT License found in the\n * LICENSE file in the root directory of this source tree.\n *\n * For all details and documentation: https://www.respoke.io\n */\n/* global define: false, respoke: false */\n\n// UMD wrapper to provide support for CommonJS, AMD, and browser globals\n(function (factory) {\n \"use strict\";\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define(['respoke'], factory);\n } else if (typeof exports === 'object') {\n // Node/CommonJS\n factory(require('respoke'));\n } else {\n // Browser globals\n factory(respoke);\n }\n}(function (respoke) {\n \"use strict\";\n var log = respoke.log;\n\n /**\n * A report containing statistical information about the flow of media with the latest live statistics.\n *\n * This is a **plugin** for respoke. To leverage it, include `<script src=\"https://cdn.respoke.io/respoke-stats.min.js\"></script>`.\n *\n * The plugin adds the methods `getStats()` and `stopStats()` to `respoke.Call`.\n *\n * ## Usage\n *\n * Once you have a `Call` instance after `endpoint.startCall()` or in the `client.on('call')` / `new Client({ onCall: yourCallHandler })` event listener:\n *\n * **using callbacks**\n *\n * call.getStats({\n * onStats: function continualStatsHandler(evt) { . . . },\n * onSuccess: yourOnSuccessHandler,\n * onError: yourOnErrorHandler\n * });\n *\n * **or using a promise**\n *\n * call.getStats({\n * onStats: function continualStatsHandler(evt) { . . . },\n * }).done(onSuccess, onFailure);\n *\n * @class respoke.MediaStats\n * @constructor\n * @link https://cdn.respoke.io/respoke-stats.min.js\n * @param {object} params\n */\n respoke.MediaStats = function (params) {\n params = JSON.parse(JSON.stringify(params || {}));\n /**\n * Information about the connection.\n * @memberof! respoke.MediaStats\n * @type {object}\n * @name connection\n * @property {string} channelId - A string which identifies this media stream (which may contain several\n * media stream tracks) to the browser.\n * @property {boolean} foundIncomingNetworkPaths - Whether or not the ICE hole-punching process has found\n * a suitable network path from the remote party to this client.\n * @property {boolean} foundOutgoingNetworkPaths - Whether or not the ICE hole-punching process has found\n * a suitable network path from this client to the remote party.\n * @property {string} localHost - The local IP and port number of the media connection.\n * @property {string} remoteHost - The remote IP and port number of the media connection.\n * @property {string} localMediaPath - The type of network path the local media is taking to the remote\n * party, one of \"local\", \"srflx\", \"prflx\", \"relay\".\n * @property {string} remoteMediaPath - The type of network path the local media is taking to the remote\n * party, one of \"local\", \"srflx\", \"prflx\", \"relay\".\n * @property {string} roundTripTime - How long it takes media packets to traverse the network path.\n * @property {string} transport - Whether the media is flowing via UDP or TCP\n */\n /**\n * Information about the local audio stream track.\n * @memberof! respoke.MediaStats\n * @type {object}\n * @name localaudio\n * @property {string} audioInputLevel - Microphone volume.\n * @property {string} codec - Audio codec in use.\n * @property {string} totalBytesSent - Total number of bytes sent since media first began flowing.\n * @property {string} periodBytesSent - Number of bytes sent since the last stats event.\n * @property {string} totalPacketsSent - Total number of packets sent since media first began flowing.\n * @property {string} periodPacketsSent - Number of packets sent since the last stats event.\n * @property {string} transportId - The identifer of the media stream to which this media stream track belongs.\n */\n /**\n * Information about the local video stream track.\n * @memberof! respoke.MediaStats\n * @type {object}\n * @name localvideo\n * @property {string} codec - Video codec in use.\n * @property {string} totalBytesSent - Total number of bytes sent since media first began flowing.\n * @property {string} periodBytesSent - Number of bytes sent since the last stats event.\n * @property {string} totalPacketsSent - Total number of packets sent since media first began flowing.\n * @property {string} periodPacketsSent - Number of packets sent since the last stats event.\n * @property {string} transportId - The identifer of the media stream to which this media stream track belongs.\n */\n /**\n * Information about the remote audio stream track.\n * @memberof! respoke.MediaStats\n * @type {object}\n * @name remoteaudio\n * @property {string} audioOutputLevel\n * @property {string} totalBytesReceived - Total number of bytes received since media first began flowing.\n * @property {string} periodBytesReceived - Number of bytes received since the last stats event.\n * @property {string} packetsLost - Total number of packets lost.\n * @property {string} totalPacketsReceived - Total number of packets received since media first began flowing.\n * @property {string} periodPacketsReceived - Number of packets received since the last stats event.\n * @property {string} transportId - The identifer of the media stream to which this media stream track\n * belongs.\n */\n /**\n * Information about the remote video stream track.\n * @memberof! respoke.MediaStats\n * @type {object}\n * @name remotevideo\n * @property {string} totalBytesReceived - Total number of bytes received since media first began flowing.\n * @property {string} periodBytesReceived - Number of bytes received since the last stats event.\n * @property {string} packetsLost - Total number of packets lost.\n * @property {string} totalPacketsReceived - Total number of packets received since media first began flowing.\n * @property {string} periodPacketsReceived - Number of packets received since the last stats event.\n * @property {string} transportId - The identifer of the media stream to which this media stream track belongs.\n */\n /**\n * Information about connection state.\n * @memberof! respoke.MediaStats\n * @type {object}\n * @name state\n * @property {string} iceConnectionState - Indicates where we are in terms of ICE network negotiation -- \"hole\n * punching.\"\n * @property {string} iceGatheringState - Indicates whether we have started or finished gathering ICE\n * candidates from the browser.\n */\n /**\n * The date and time at which this stats snapshot was taken.\n * @memberof! respoke.MediaStats\n * @name timestamp\n * @type {date}\n */\n /**\n * The time that has passed since the last stats snapshot was taken.\n * @memberof! respoke.MediaStats\n * @name periodLength\n * @type {number}\n */\n /**\n * These aliases define what things should be renamed before report is sent.\n * @memberof! respoke.MediaStats\n * @private\n * @name aliases\n * @type {object}\n */\n var aliases = {\n cons: {\n newname: 'connection',\n members: {\n googChannelId: 'channelId',\n googLocalAddress: 'localHost',\n googRemoteAddress: 'remoteHost',\n googLocalCandidateType: 'localMediaPath',\n googRemoteCandidateType: 'remoteMediaPath',\n googReadable: 'foundIncomingNetworkPaths',\n googRtt: 'roundTripTime',\n googTransportType: 'transport',\n googWritable: 'foundOutgoingNetworkPaths'\n }\n },\n localaudio: {\n members: {\n googCodecName: 'codec',\n bytesSent: 'totalBytesSent',\n packetsSent: 'totalPacketsSent'\n }\n },\n localvideo: {\n members: {\n googCodecName: 'codec',\n bytesSent: 'totalBytesSent',\n packetsSent: 'totalPacketsSent'\n }\n },\n remoteaudio: {\n members: {\n googCodecName: 'codec',\n bytesReceived: 'totalBytesReceived',\n packetsReceived: 'totalPacketsReceived'\n }\n },\n remotevideo: {\n members: {\n googCodecName: 'codec',\n bytesReceived: 'totalBytesReceived',\n packetsReceived: 'totalPacketsReceived'\n }\n }\n };\n\n /**\n * Rename report attributes to have more readable, understandable names.\n * @memberof! respoke.MediaStats\n * @method respoke.MediaStats.format\n * @param {object} report\n * @param {object} aliases\n * @returns {object}\n * @private\n */\n function format(report, aliases) {\n Object.keys(aliases).forEach(function eachAttr(oldName) {\n var name;\n if (typeof aliases[oldName] === 'string') {\n report[aliases[oldName]] = report[oldName];\n delete report[oldName];\n } else if (typeof aliases[oldName] === 'object') {\n name = oldName;\n if (aliases[oldName].newname) {\n report[aliases[oldName].newname] = report[oldName];\n name = aliases[oldName].newname;\n delete report[oldName];\n }\n if (aliases[oldName].members) {\n format(report[name], aliases[oldName].members);\n }\n }\n });\n\n if (report.connection) {\n report.connection.foundIncomingNetworkPaths = report.connection.foundIncomingNetworkPaths === 'true';\n report.connection.foundOutgoingNetworkPaths = report.connection.foundOutgoingNetworkPaths === 'true';\n }\n return report;\n }\n return format(params, aliases);\n }; // End respoke.MediaStats\n\n /**\n * A handler for WebRTC statistics. This class takes an `onStats` callback which it calls every `interval` seconds\n * with the latest live statistics.\n * @class respoke.MediaStatsParser\n * @private\n * @constructor\n * @augments respoke.Class\n * @param {RTCPeerConnection} peerConnection\n */\n respoke.MediaStatsParser = function (params) {\n params = params || {};\n var that = respoke.Class(params);\n /**\n * @memberof! respoke.MediaStatsParser\n * @name className\n * @type {string}\n */\n that.className = 'respoke.MediaStatsParser';\n /**\n * @memberof! respoke.MediaStatsParser\n * @private\n * @name oldStats\n * @type {boolean}\n */\n var oldStats = false;\n /**\n * @memberof! respoke.MediaStatsParser\n * @private\n * @name pc\n * @type RTCPeerConnection\n */\n var pc = params.peerConnection;\n delete params.peerConnection;\n /**\n * @memberof! respoke.MediaStatsParser\n * @private\n * @name timer\n * @type {number}\n * @desc The timer for calling the onStats callback; the output of setInterval.\n */\n var timer = 0;\n /**\n * @memberof! respoke.MediaStatsParser\n * @private\n * @name statsInterval\n * @type {number}\n * @desc The millisecond interval on which we call the onStats callback.\n */\n var statsInterval = params.interval || 5000;\n\n /*\n * The data you get out of getStats needs some pruning and a tidy up\n * so I define some things I think are 'interesting' and how to find them.\n *\n * getStats gives you an array of results each of which has a type.\n * Each result contains a list of keys and a dictionary of values that can\n * be retrieved by key. (no ,they aren't properties)\n *\n * The interesting stats object is a list of objects, each describing the\n * type of result that contains the stats, the names of the relevant stats\n * and a way to filter out the irrelevant results objects of the same type.\n *\n * An added complication is that the standards are in flux so google add\n * data in chrome (some of it useful) that isn;t in the draft standard.\n */\n\n /**\n * @memberof! respoke.MediaStatsParser\n * @private\n * @name interestingStats\n * @type {object}\n */\n var interestingStats = {\n cons: {\n type: \"googCandidatePair\",\n match: {key: \"googActiveConnection\", value: \"true\"},\n keys: [\n \"googWritable\", \"googReadable\", \"googTransportType\", \"googLocalCandidateType\",\n \"googRemoteCandidateType\", \"googRemoteAddress\", \"googLocalAddress\", \"googRtt\", \"googChannelId\"\n ]\n },\n // the next 4 property names _matter_ they have to finish with the value of an m= line\n // if you change them things won't work.\n localaudio: {\n type: \"ssrc\",\n match: {key: \"ssrc\", value: \"\"},\n keys: [\"audioInputLevel\", \"packetsSent\", \"bytesSent\", \"transportId\", \"googCodecName\"]\n },\n remoteaudio: {\n type: \"ssrc\",\n match: {key: \"ssrc\", value: \"\"},\n keys: [\"audioOutputLevel\", \"packetsReceived\", \"packetsLost\", \"bytesReceived\", \"transportId\"]\n },\n remotevideo: {\n type: \"ssrc\",\n match: {key: \"ssrc\", value: \"\"},\n keys: [\"packetsReceived\", \"packetsLost\", \"bytesReceived\", \"transportId\"]\n },\n localvideo: {\n type: \"ssrc\",\n match: {key: \"ssrc\", value: \"\"},\n keys: [\"packetsSent\", \"bytesSent\", \"transportId\", \"googCodecName\"]\n }\n };\n\n /**\n * @memberof! respoke.MediaStatsParser\n * @private\n * @name deltas\n * @type {object}\n */\n var deltas = {\n packetsSent: true,\n bytesSent: true,\n packetsReceived: true,\n bytesReceived: true\n };\n\n /**\n * Determine if a string starts with the given value.\n * @memberof! respoke.MediaStatsParser\n * @method respoke.MediaStatsParser.startsWith\n * @param {string} string\n * @param {string} value\n * @returns {boolean}\n * @private\n */\n function startsWith(string, value) {\n return (string && string.slice && (string.slice(0, value.length) === value));\n }\n\n /**\n * Parse the SDPs. Kick off continuous calling of getStats() every `interval` milliseconds.\n * @memberof! respoke.MediaStatsParser\n * @method respoke.MediaStatsParser.initStats\n * @private\n */\n function initStats() {\n var sdp = {};\n if (!pc || !pc.remoteDescription || !pc.remoteDescription.sdp ||\n !pc.localDescription || !pc.localDescription.sdp) {\n log.warn(\"missing info.\");\n return;\n }\n\n sdp = {\n remote: pc.remoteDescription.sdp,\n local: pc.localDescription.sdp\n };\n\n /**\n * extract the ssrcs from the sdp, because it isn't anwhere else.\n * we will use them to map results to audio/video etc\n */\n Object.keys(sdp).forEach(function eachKey(side) {\n var rsdp = sdp[side];\n // filet the sdp\n var lines = rsdp.split(\"\\r\\n\");\n var mediaType = null;\n\n Object.keys(lines).forEach(function lineNum(lineIndex) {\n var line = lines[lineIndex];\n var lbits = null;\n var ssrc = null;\n\n if (startsWith(line, \"m=\")) { // take a note of the sort of media we are looking at\n mediaType = line.substring(2, 7); // should be either 'audio' or 'video'\n } else if (startsWith(line, \"a=ssrc:\")) {\n lbits = line.split(\" \");\n ssrc = lbits[0].substring(\"a=ssrc:\".length);\n\n if (interestingStats[side + mediaType]) {\n // fill in the value of the respective 'match'\n // build the name of the stat from parts\n if (interestingStats[side + mediaType].match.value.length === 0) {\n interestingStats[side + mediaType].match.value = ssrc;\n }\n }\n }\n });\n });\n\n if (params.onStats) {\n timer = setInterval(function statsTimerHandler() {\n that.getStats().done(params.onStats, function errorHandler(err) {\n log.error(\"error in getStats\", err.message, err.stack);\n });\n }, statsInterval);\n } else {\n log.warn(\"Not starting stats, no onStats callback provided.\");\n }\n }\n\n /**\n * Get one snapshot of stats from the call's PeerConnection.\n * @memberof! respoke.MediaStatsParser\n * @method respoke.MediaStatsParser.getStats\n * @param {object} [params]\n * @param {respoke.MediaStatsParser.statsHandler} [params.onSuccess] - Success handler for this\n * invocation of this method only.\n * @param {respoke.Client.errorHandler} [params.onError] - Error handler for this invocation of this\n * method only.\n * @param {respoke.MediaStatsParser.statsHandler} [params.onStats] - Callback accepting a single `event` argument.\n * @returns {Promise<object>|undefined}\n */\n that.getStats = function (params) {\n params = params || {};\n var deferred = respoke.Q.defer();\n var retVal = respoke.handlePromise(deferred.promise, params.onSuccess, params.onError);\n var args = [];\n\n if (!pc.getStats) {\n deferred.reject(new Error(\"no peer connection getStats()\"));\n return retVal;\n }\n\n if (navigator.mozGetUserMedia) {\n args.push(null);\n }\n\n args.push(function successHandler(stats) {\n deferred.resolve(respoke.MediaStats(buildStats(stats)));\n });\n args.push(function errorHandler(err) {\n log.error(err);\n deferred.reject(new Error(\"Can't get stats.\"));\n });\n pc.getStats.apply(pc, args);\n return retVal;\n };\n\n /**\n * Stop fetching and processing of call stats.\n * @memberof! respoke.MediaStatsParser\n * @method respoke.MediaStatsParser.stopStats\n */\n that.stopStats = function () {\n clearInterval(timer);\n };\n\n /**\n * Receive raw stats and parse them.\n * @memberof! respoke.MediaStatsParser\n * @method respoke.MediaStatsParser.buildStats\n * @param {object} rawStats\n * @private\n */\n function buildStats(rawStats) {\n // extract and repackage 'interesting' stats using the rules above\n var stats = rawStats; // might need to re-instate some sort of wrapper here\n var results = stats.result();\n\n var allStats = {\n state: {\n iceGatheringState: pc.icegatheringState,\n iceConnectionState: pc.iceConnectionState\n }\n };\n\n Object.keys(interestingStats).forEach(function eachStatType(statType) {\n var eachStat = {};\n var rule = interestingStats[statType];\n var report = results.filter(function eachResult(result) {\n var typeMatch = (result.type === rule.type);\n var keyMatch = (result.stat(rule.match.key) === rule.match.value);\n return (typeMatch && keyMatch);\n });\n\n if (report.length > 0) {\n if (report[0].timestamp) {\n allStats.timestamp = report[0].timestamp;\n allStats.periodLength = allStats.timestamp - oldStats.timestamp;\n }\n rule.keys.forEach(function eachKey(key) {\n var testInt = parseInt(report[0].stat(key), 10);\n if (!isNaN(testInt)) {\n eachStat[key] = testInt;\n } else {\n eachStat[key] = report[0].stat(key);\n }\n\n if (deltas[key] && oldStats && oldStats[statType] &&\n [null, undefined].indexOf(oldStats[statType][key]) === -1) {\n eachStat[\"period\" + key.charAt(0).toUpperCase() + key.slice(1)] =\n (eachStat[key] - oldStats[statType][key]);\n }\n });\n }\n allStats[statType] = eachStat;\n });\n oldStats = allStats;\n return allStats;\n }\n\n initStats();\n\n return that;\n }; // End respoke.MediaStatsParser\n}));\n\n/**\n * Success handler for methods that generate stats.\n * @callback respoke.MediaStatsParser.statsHandler\n * @param {respoke.MediaStats}\n */\n"]}