Introduction
Welcome to the callstats.io API!
The callstats.io’s Javascript client library enables performance monitoring features in browser-based WebRTC endpoints. The communication with callstats.io occurs over Secure HTTP (https://
) and Secure WebSocket (wss://
). The endpoint (the browser in this case) MUST support WebSockets. Additionally, the origin server MUST allow Cross-Origin Resource Sharing (CORS) and MAY need to serve its own pages over HTTPS to avoid mixed content warnings.
Versioning
callstats.js
uses semantic versioning. The latest version is in the changelog. However, programmatically the version can be fetched by invoking callstats.version
.
Please subscribe to our callstats-dev mailing list to get notifications and changelogs of new callstats.js
releases.
API
callstats.initialize() with app secret
var localUserID = {};
localUserID.userName = "Clark Kent";
localUserID.aliasName = "superman";
localUserID.loginID = "superman@dc"
var additionalIDs = {
customerID: "Customer Identifier. Example, walmart.",
tenantID: "Tenant Identifier. Example, monster.",
productName: "Product Name. Example, Jitsi.",
meetingsName: "Meeting Name. Example, Jitsi loves callstats.",
serverName: "Server/MiddleBox Name. Example, jvb-prod-us-east-mlkncws12.",
pbxID: "PBX Identifier. Example, walmart.",
pbxExtensionID: "PBX Extension Identifier. Example, 5625.",
fqExtensionID: "Fully qualified Extension Identifier. Example, +71 (US) +5625.",
sessionID: "Session Identifier. Example, session-12-34",
};
var configParams = {
disableBeforeUnloadHandler: true, // disables callstats.js's window.onbeforeunload parameter.
applicationVersion: "app_version", // Application version specified by the developer.
disablePrecalltest: true // disables the pre-call test, it is enabled by default.
siteID: "siteID", // The name/ID of the site/campus from where the call/pre-call test is made.
additionalIDs: additionalIDs, // additionalIDs object, contains application related IDs.
collectLegacyStats: true //enables the collection of legacy stats in chrome browser
collectIP: true //enables the collection localIP address
};
callstats.initialize(AppID, AppSecret, localUserID, csInitCallback, csStatsCallback, configParams);
Params | Argument | Type | Description |
---|---|---|---|
AppID |
Required | String | Application ID is obtained from callstats.io. |
AppSecret |
Required | String | Application secret is obtained from callstats.io. |
localUserID |
Required | String (128 bytes) or Object | it is provided by the developer and MUST NOT be null or empty. |
csInitCallback |
Optional | Callback | asynchronously reports status of the protocol messages. |
csStatsCallback |
Optional | Callback | asynchronously reports the conference statistics. |
configParams |
Optional | JSON | it is the set of parameters to enable/disable certain features in the library. |
JSON for UserID (supported since v3.14)
In some cases, customers want to provide the actual username in addition to the alias to callstats.io. Since callstats.js version 3.14, it accepts userID both as a String or an object. Section on generating userID provides more guidelines on choosing a localUserID
.
Keys | Required | Description |
---|---|---|
userName |
Yes | Strint of maximum lenth 128 characters. |
aliasName |
Yes | String of maximum length 128 characters. |
loginID |
No | String of maximum length 128 characters. |
JSON for configParams
It provides developers a method to enable or disable certain features or functions within the callstats.js
library. It is a javascript object with the following OPTIONAL key-value pairs. They are:
Keys | Required | Description |
---|---|---|
disableBeforeUnloadHandler |
No | by default the value is false . |
applicationVersion |
No | String of maximum length 30 characters. |
disablePrecalltest |
No | by default the value is false . |
siteID |
No | String (256 bytes). |
collectLegacyStats |
No | by default the value is true . |
additionalIDs |
No | JSON object. |
collectIP |
No | by default the value is true . |
JSON for additionalIDs
Keys | Required | Description |
---|---|---|
customerID |
No | String (256 bytes) Example, walmart. |
tenantID |
No | String (256 bytes) Example, monster. |
productName |
No | String (256 bytes) Example, jitsi. |
meetingsName |
No | String (256 bytes) Example, jitsi loves callstats. |
serverName |
No | String (256 bytes) Example, jvb-prod-us-east-mlkncws12. |
pbxID |
No | String (256 bytes) Example, walmart. |
pbxExtensionID |
No | String (256 bytes) Example, 5625. |
fqExtensionID |
No | String (256 bytes) Example, +71 (US) +5625. |
sessionID |
No | String (256 bytes) Example, session-12-34. |
callstats.initialize() with third party authentication
- Authenticates with the callstats.io backend to setup a trusted relationship with it.
Params | Argument | Type | Description |
---|---|---|---|
AppID |
Required | String | Application ID is obtained from callstats.io. |
tokenGenerator |
Required | callback | Callback to generate token. |
localUserID |
Required | String (128 bytes) or Object | it is provided by the developer and MUST NOT be null or empty. |
csInitCallback |
Optional | callback | asynchronously reports status of the protocol messages. |
csStatsCallback |
Optional | callback | asynchronously reports the conference statistics. |
configParams |
Optional | JSON | it is the set of parameters to enable/disable certain features in the library. |
callstats.addNewFabric()
var fabricAttributes = {
remoteEndpointType: callstats.endpointType.peer,
fabricTransmissionDirection: callstats.transmissionDirection.sendrecv
};
callstats.addNewFabric(pcObject, remoteUserID, fabricUsage, conferenceID, fabricAttributes, pcCallback);
- Indicates that the WebRTC application requests callstats.js to monitor the performance of the PeerConnection between the two endpoints (represented by the corresponding UserIDs).
Params | Argument | Type | Description |
---|---|---|---|
pcObject |
Required | Object | PeerConnection object. |
remoteUserID |
Required | String (128 bytes) or Object | It is generated by the origin server. |
fabricUsage |
Required | Enum | Valid values are discussed in a later section. |
conferenceID |
Required | String (256 bytes) | It is generated by the origin server. |
fabricAttributes |
Optional | Object | Contains two attributes section and section. |
pcCallback |
Optional | Callback | the callback asynchronously reports failure or success for pcObject. |
callstats.reportError()
callstats.reportError(pcObject, conferenceID, callstats.webRTCFunctions.createOffer);
- Notifies the callstats.io back-end about conference setup failure reason.
Params | Argument | Type | Description |
---|---|---|---|
pcObject |
Required | Object | PeerConnection object |
conferenceID |
Required | String (256 bytes) | It is generated by the origin server. |
webRTCFunctions |
Required | Enum | Name of the WebRTC function that failed. |
domError |
Optional | Object | DOMError object |
localSDP |
Optional | Object | Local SDP collected when errors occur. |
remoteSDP |
Optional | Object | Remote SDP collected when errors occur. |
callstats.associateMstWithUserID()
// Extracting SSRC from SDP
var validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);
var reg = /^ssrc:(\d*) ([\w_]*):(.*)/;
pc.remoteDescription.sdp.split(/(\r\n|\r|\n)/).filter(validLine).forEach(function (l) {
var type = l[0];
var content = l.slice(2);
if(type === 'a') {
if (reg.test(content)) {
var match = content.match(reg);
if(($.inArray(match[1],ssrcs) === -1)) {
ssrcs.push(match[1]);
}
}
}
});
ssrcs.forEach(function(ssrc) {
window.callstats.associateMstWithUserID(pcObject, userID, conferenceID, ssrc, usageLabel, associatedVideoTag);
});
- Maps the SSRC to the userID that generated it. This is useful when multiple MediaStreamTracks (MSTs) are sent or received in a single PeerConnection.
Params | Argument | Type | Description |
---|---|---|---|
pcObject |
Required | Object | PeerConnection object. |
userID |
Required | String (128 bytes) or Object | It is generated by the origin server. |
conferenceID |
Required | String (256 bytes) | It is generated by the origin server. |
SSRC |
Required | Object | Synchronization Source Identifier, as defined in RFC3550. |
usageLabel |
Required | String (20 bytes) | it is generated by the origin server. |
associatedVideoTag |
Optional | String | handler to the user’s video tag. |
callstats.sendFabricEvent()
- Notifies the callstats.io back-end about user specific events (e.g., ‘Hold’, 'Mute’, etc.) on a PeerConnection. It is usually generated due to local user interaction at a PeerConnection.
Params | Argument | Type | Description |
---|---|---|---|
pcObject |
Required | Object | PeerConnection object. |
fabricEvent |
Required | Enum | with valid values discussed in a later section. |
conferenceID |
Required | String (256 bytes) | It is generated by the origin server. |
eventData |
Optional | Object | event related data. |
callstats.sendUserFeedback()
- Send the feedback on conference performance indicated by the user.
Params | Argument | Type | Description |
---|---|---|---|
conferenceID |
Required | String (256 bytes) | It is generated by the origin server. |
feedback |
Required | Object | JSON object. |
pcCallback |
Optional | Callback | the callback asynchronously reports failure or success of feedback submission. |
JSON for feedback
Keys | Required | Type | Description |
---|---|---|---|
userID |
Required | String (128 bytes) | It is generated by the origin server. Discussed in a later section. |
overall |
Required | Integer (between 1-5) | Typically the scores correspond to the mean opinion score, in this case this value represents the overall quality perceived by the userID. |
audio , video , screen |
Optional | Integer (between 1-5) | Similar to the definition of overall , except these values correspond to specific types of media streams. |
comment |
Optional | String | Detailed user feedback. |
callstats.on()
- The “on” function is used to set the callbacks for events.
Params | Argument | Type | Description |
---|---|---|---|
eventName |
Required | String | The allowed values are “preCallTestResults”, “defaultConfig”, “recommendedConfig”, “connectionRecommendation”, “error”, and “stats”. |
csEventCallback |
Required | Callback | The callback asynchronously provides new event data whenever it is available. |
callstats.on(eventName, csEventCallback);
callstats.attachWifiStatsHandler()
- The “attachWifiStatsHandler” function is used to set the callbacks for WiFi stats. You can send WiFi stats to callstat.io and visualize the stats on the dashboard.
Params | Argument | Type | Description |
---|---|---|---|
getWifiStats |
Required | Callback | This method returns a Promise that resolves the WiFi stats (stringified). |
callstats.attachWifiStatsHandler(getWifiStats);
function getWifiStats() {
return new Promise(function(resolve, reject) {
resolve(JSON.stringify(wifistats));
});
}
wifistats = {
signal: 20, // mandatory
rssi: -1, // mandatory
timestamp: 152320980808, // mandatory
interface: 'en0', // optional
noise: -90, // optional
addresses: ['192.168.0.1', '192.168.0.2'], // optional
}
API return values
- All the callstats.io APIs returns an object containing
status
andmsg
.
Params | Description |
---|---|
status |
Returns one of the values defined in callStatsAPIReturnStatus |
msg |
Returns the corresponding message related to the status . |
callstats.startPrecallTests()
callstats.startPrecallTests(iceServers, interval);
var iceServers = [
{
"urls":[
"turns:taas.callstats.io:443?transport=udp",
"turns:taas.callstats.io:443?transport=tcp",
"turn:taas.callstats.io:80?transport=udp",
"turn:taas.callstats.io:80?transport=tcp"
],
"username":,
"credential":,
"label": "turn2"
},
{
"urls":[
"turn:turn-server-1.dialogue.io:3478"
],
"username":,
"credential":,
"label": "turn2"
}
];
- This API can be used to make precall tests continuously as per the interval given by the customers. The results of the precall test will be given in the
preCallTestResults
callback. Once we run precall tests on all the turn credentials provided, the connection recommendation is provided in theconnectionRecommendation
callback.
Params | Argument | Type | Description |
---|---|---|---|
iceServers |
Required | Object | TURN Server list provided by customer. |
interval |
Required | Integer | Indicates the delay (in seconds) after the tests to restart the pre call tests. |
callstats.stopPrecallTests()
callstats.stopPrecallTests();
- This API to stop the precall tests started using the API
startPrecallTests()
callstats.sendCallDetails()
callstats.sendCallDetails(pc, conferenceID, callAttributes);
var callAttributes = {
callType:, //inbound or outbound or monitoring
role: //agent or participant or manager
}
- This API reports the call details from Contact Centers to Callstats backend.
Params | Argument | Type | Description |
---|---|---|---|
pc |
Required | Object | RTCPeerConnection associated with this connection. |
conferenceID |
Required | String (256 bytes) | It is generated by the origin server. |
callAttributes |
Required | JSON | Contains information about the call. |
Callbacks and Error Handling
The WebRTC application can provide callback functions to callstats.js which are invoked in case of the following events:
- After the initialize
- To enquire the stats
- To obtain pre-call test results
- To obtain the default configuration
- To obtain the recommended configuration
- To check the errors
The initialize callback
// initialize the callstats js API
var callstats = new callstats();
callstats.initialize(AppID, AppSecret, localUserId, csInitCallback, csStatsCallback);
function csInitCallback (csStatus, csMsg) {
console.log("Initializing Status: "+csStatus+" msg: "+csMsg);
}
The csInitCallback function is given as a parameter to the initialize()
call. To report different success and failure cases, which can occur during initialize()
or sending measurements to callstats.io. The callback takes the form of:
csStatus and csMsg are of type String. csMsg
is a descriptive error returned by callstats.io.
The stats callback
// initialize the callstats js API
var callstats = new callstats();
callstats.initialize(AppID, AppSecret, localUserId, csInitCallback, csStatsCallback);
var reportType = {
inbound: 'inbound-rtp',
outbound: 'outbound-rtp'
};
// callback function to receive the stats
var csStatsCallback = function (stats) {
var ssrc;
for (ssrc in stats.streams) {
console.log("SSRC is: ", ssrc);
var dataSsrc = stats.streams[ssrc];
console.log("SSRC Type ", dataSsrc.reportType);
if (dataSsrc.reportType === reportType.outbound) {
console.log("RTT is: ", dataSsrc.rtt);
} else if (dataSsrc.reportType === reportType.inbound) {
console.log("Inbound loss rate is: ", dataSsrc.fractionLoss);
}
}
}
// JSON description of the metrics structure
"stats": {
"connectionState":, // one of: online, offline
"fabricState":, // one of: initialising, established, disrupted.
"conferenceURL":, // URL of the conference
// New csStatsCallback format
"mediaStreamTracks": [
[0]: {
"remoteUserID":, // associated during the addNewFabric() API
"statsType":, // inbound-rtp, outbound-rtp, remote-inbound-rtp, remote-outbound-rtp
// data here is parsed from the local and remote SDP
"cname":, // CNAME associated with the SSRC
"msid":, // mediastreamID associated with the SSRC
"mediaType":, // either video or audio
// provided in associateMstWithUserID() API
"usageLabel":, //usage information
"associatedVideoTag":, // video tag that renders the media
"bitrate":, // interval bitrate in kbps
"packetRate":, // packets per second
"fractionLoss":, // interval fractional loss
"packetLossPercentage":,// Packet loss in percentage
"jitter":, // interval jitter in ms
"averageJitter":, // Average jitter
"rtt":, // round-trip time in ms.
"averageRTT":, // Average round-trip time in ms
"quality":, // interval quality as per boundary conditions.
"audioInputLevel":, // audio input level
"audioOutputLevel":, // audio output level
},
...
]
};
The csStatsCallback function can either be given as a parameter to the initialize() call, or set separately with the on() functionality.
The csStatsCallback()
will be called by the callstats.js for each PeerConnection independently at regular intervals. By default the interval is set as 10 seconds to make sure we do not overwhelm the app with too many messages. For more information, please check out our blog on csStatsCallback()
The pre-call test results callback
//Usage
callstats.on("preCallTestResults", csPreCallTestResultsCallback);
function preCallTestResultsCallback(status, results) {
//Check the status
if (status == callstats.callStatsAPIReturnStatus.success) {
//Results
var connectivity = results.mediaConnectivity;
var rtt = results.rtt;
var loss = results.fractionalLoss;
var throughput = results.throughput;
}
else {
console.log("Pre-call test could not be run");
}
}
The csPreCallTestResultsCallback
function is set with the on() functionality. The callback is invoked when the pre-call test results are available. The pre-call test measures the Media Connectivity, Round Trip Time, Fractional Loss, and Throughput against callstats.io TURN servers. You can use the “status” to check if the pre-call test has succeeded, it will return success
or failure
. The pre-call test is enabled by default and is running as long as the callback is not fired. The pre-call test might return partial results if the tests are interrupted or the call begins during the pre-call test. You can disable pre-call test by setting “disablePrecalltest” to true
in configParams
.
Params | Type | Description |
---|---|---|
mediaConnectivity |
boolean | True or False. |
rtt |
float | Round Trip Time in ms. Returns “null” if there is no result. |
fractionalLoss |
float | Fractional Loss [0-1]. Returns “null” if there is no result. |
throughput |
float | Throughput in kbps. Returns “null” if there is no result. |
The default configuration callback
function csDefaultConfigurationCallback(config) {
var pc = new RTCPeerConnection(config.peerConnection);
getUserMedia(config.media);
}
The csDefaultConfigurationCallback
function is set with the on() functionality. The callback is invoked when the default configuration provided in the callstats.io dashboard is available. It is only fired once.
config.peerConnection
object is of type RTCConfigurationconfig.media
object is of type MediaStreamConstraints
The integration steps are listed here
The recommended configuration callback
function csRecommendedConfigurationCallback(config) {
var pc = new RTCPeerConnection(config.peerConnection);
getUserMedia(config.media);
}
The csRecommendedConfigurationCallback
function is set with the on() functionality. The callback is invoked when the recommended configuration provided by callstats.io is available.
The connection recommendation callback
//Usage
callstats.on("connectionRecommendation", csConnectionRecommendationCallback);
function csConnectionRecommendationCallback(){
"aggregatedStats":[
{
"provider":"turn1",
"roundTripTime":46.3499755859375,
"jitter":143.7728006389737,
"fractionLost":0,
"throughput":4160.657588552875
},
{
"provider":"turn2",
"roundTripTime":106.699951171875,
"jitter":1547.3617393722136,
"fractionLost":0.5257732840987372,
"throughput":584.6231721117872
}
],
"providerRanking":[
{
"provider":"turn1",
"acceptable":true
},
{
"provider":"turn2",
"acceptable":false
}
]
}
The csConnectionRecommendationCallback
function is set with the on() functionality. The callback is invoked when the precall tests are finished for all the TURN credentials given in ‘startPrecallTest’. Connection recommendation gives the statistics measured, and the ranking of the TURN servers provided.
Params | Type | Description |
---|---|---|
provider |
string | TURN label. |
roundTripTime |
float | Round Trip Time in ms. Returns “null” if there is no result. |
jitter |
float | jitter in ms |
fractionLost |
float | Fractional Loss [0-1]. Returns “null” if there is no result. |
throughput |
float | Throughput in kbps. Returns “null” if there is no result. |
acceptable |
boolean | True or False |
API Enums
Enumeration of fabricUsage
Usage | Description |
---|---|
multiplex |
Describes a PeerConnection carrying multiple media streams on the same port. |
audio |
Describes an audio-only PeerConnection. |
video |
Describes a video-only PeerConnection. |
screen |
Describes a screen-sharing PeerConnection. |
data |
Describes a PeerConnection with only DataChannels. |
unbundled |
Describes a PeerConnection carrying media streams on different ports. |
When using a single PeerConnection between a pair of userIDs for sending and receiving audio and video, application MUST use multiplex
.
Enumeration of fabricEvent
Name | Description |
---|---|
fabricHold |
The fabric is currently not sending and receiving any media, but the connection is still active. |
fabricResume |
The fabric is resuming communication with the remote endpoint. |
audioMute |
The fabric is currently not sending any Audio, but MAY be sending video. |
audioUnmute |
The fabric is resuming Audio communication. |
videoPause |
The fabric is currently not sending any Video, but MAY be sending audio. |
videoResume |
The fabric is resuming Video communication. |
fabricTerminated |
The PeerConnection is destroyed and is no longer sending or receiving any media. |
screenShareStart |
The PeerConnection started the screen sharing. |
screenShareStop |
The PeerConnection stopped the screen sharing. |
dominantSpeaker |
The userID reports that it is the dominant speaker and not the remote participants. |
activeDeviceList |
The userID reports the active devices used by him during the conference. |
Enumeration of webRTCFunctions
Function Name | Description |
---|---|
getUserMedia |
The failure occurred in getUserMedia function (added in callstats.js version 3.4.x). |
createOffer |
The failure occurred in createOffer function. |
createAnswer |
The failure occurred in createAnswer function. |
setLocalDescription |
The failure occurred in setLocalDescription function. |
setRemoteDescription |
The failure occurred in setRemoteDescription function. |
addIceCandidate |
The failure occurred in addIceCandidate function. |
iceConnectionFailure |
Ice connection failure detected by the application. |
signalingError |
Signaling related errors in the application. |
applicationLog |
Application related logs, this will not be considered as a failure. |
Enumeration of endpointType
Name | Description |
---|---|
peer |
The endpoint is a WebRTC client/peer. |
server |
The endpoint is a media server or a middle-box. |
Enumeration of transmissionDirection
Name | Description |
---|---|
sendonly |
PeerConnection is for sending only. |
receiveonly |
PeerConnection is for receiving only. |
sendrecv |
PeerConnection is for sending and receiving. |
inactive |
PeerConnection is inactive. |
Enumeration of userIDType
Name | Description |
---|---|
local |
Set the localUserID |
remote |
Set the remoteUserID |
csStatus Types
Name | Description |
---|---|
httpError |
HTTP error. |
authError |
Authentication failed, AppID or AppSecret is incorrect. |
success |
The back-end has accepted the request and the endpoint is authenticated, or capable of sending measurement data. |
tokenGenerationError |
Application could not generate the JWT. |
Enumeration of callStatsAPIReturnStatus
Name | Description |
---|---|
success |
The API call was successful. |
failure |
The API call failed. |
Authentication
Basic Authentication over HTTPS
The application requires an AppID
and AppSecret
to authenticate with callstats.io. The origin server is expected to pass the userID
for each endpoint in a WebRTC call. The callstats.js
internally implements a 4-way handshake, based on simple challenge-response protocol. If successful, the callstats.js generates a token valid for 2 hours. The token
is subsequently used by the callstats.js to send event and measurement data to callstats.io.
White-listing (Optional but highly recommended)
callstats.io uses the “Origin” header in the HTTP request to fetch the request’s origin. RFC6454 explains the algorithm used by user-agents to compute the “Origin” header.
callstats.io compares the Origin URL sent by the HTTP user-agent in the authentication message with the stored Origin URL for that particular AppID. If the origins match, callstats.io returns a new token and associates the token to the requesting userID. Alternatively, if the origins does not match, callstats.io rejects the request, and denies access to that particular user-agent.
3rd party authentication
Instead of relying only on the endpoint for authentication, the callstats.io also implements third-party authentication, which requires the origin server to generate token for the endpoint which is then used to authenticate the endpoint. 3rd party authentication is discussed in detail in a later section.
Integrating with your App
Step 1: Include callstats.js
<script src="https://api.callstats.io/static/callstats.min.js"></script>
Everything in the
callstats.js
is scoped under the callstats namespace, hence create the object.
var callstats = new callstats();
Add the callstats.js
in the HEAD tag.
Step 2: Initialize() with AppSecret
//initialize the app with application tokens
var AppID = "YOUR APPLICATION ID";
var AppSecret = "YOUR APPLICATION SECRET";
//localUserID is generated or given by the origin server
callstats.initialize(AppID, AppSecret, localUserID, csInitCallback, csStatsCallback, configParams);
After the user is authenticated with the origin server (or when the page loads), call initialize()
with appropriate parameters (see API section). Check the callback for errors. If the authentication succeeds, callstats.js
will receive a valid authentication token to make subsequent API calls.
For more information on callbacks, please refer to csInitCallback and csStatsCallback. Also have a look at step 8 for csStatsCallback data handling.
ALTERNATIVE: If you are interested in using the third-party authentication, see the details described in a later section.
Step 3: addNewFabric()
//adding Fabrics
var pc_config = {"iceServers": [{url: "stun:stun.example.org:3478"}]};
var pcObject = new RTCPeerConnection(pc_config);
function pcCallback (err, msg) {
console.log("Monitoring status: "+ err + " msg: " + msg);
};
// pcObject is created, tell callstats about it
// pick a fabricUsage enumeration, if pc is sending both media and data: use multiplex.
var usage = callstats.fabricUsage.multiplex;
var fabricAttributes = {
remoteEndpointType: callstats.endpointType.peer,
fabricTransmissionDirection: callstats.transmissionDirection.sendrecv
};
//remoteUserID is the recipient's userID
//conferenceID is generated or provided by the origin server (webrtc service)
callstats.addNewFabric(pcObject, remoteUserID, usage, conferenceID, fabricAttributes, pcCallback);
When creating a PeerConnection, call addNewFabric()
with appropriate parameters (see API section). It is important to make the request only after the PeerConnection is created. The PeerConnection object MUST NOT be “undefined” or NULL because callstats.js
uses getStats()
to query the metrics from the browser internals. The application SHOULD call addNewFabric()
immediately after the PeerConnection object is created.
Time stamp of addNewFabric()
is used as a reference point to calculate fabric failure delay or fabric setup delay:
In any WebRTC endpoint, where multiple PeerConnections are created between each participant (e.g., audio and video sent over different PeerConnections or a mesh call), the addNewFabric()
MUST be called for each PeerConnection.
Step 4: reportError()
//adding Fabrics
var pc_config = {"iceServers": [{url: "stun:stun.example.org:3478"}]};
var pcObject = new RTCPeerConnection(pc_config);
function pcCallback (err, msg) {
console.log("Monitoring status: "+ err + " msg: " + msg);
};
function createOfferError(err) {
callstats.reportError(pcObject, conferenceID, callstats.webRTCFunctions.createOffer, err);
}
// remoteUserID is the recipient's userID
// conferenceID is generated or provided by the origin server (webrtc service)
// pcObject is created, tell callstats about it
// pick a fabricUsage enumeration, if pc is sending both media and data: use multiplex.
var usage = callstats.fabricUsage.multiplex;
callstats.addNewFabric(pcObject, remoteUserID, usage, conferenceID, pcCallback);
// let the "negotiationneeded" event trigger offer generation
pcObject.onnegotiationneeded = function () {
// create offer
pcObject.createOffer().then(
localDescriptionCreatedCallback,
createOfferErrorCallback
);
}
Sometimes WebRTC endpoints fail to establish connectivity, this may occur when user-agents and/or bridges implement differing flavors of the Session Description Protocol (SDP) or may not support some features that others implement.
The WebRTC APIs either have a callback or a Promise associated to them. Since callstats.js ver. 3.2.x
, WebRTC applications can use reportError()
to capture at which stage the negotiation fails and pass on the DomError returned by the callback or Promise to callstats.io. The failure reason will appear both in the conference time-line and aggregate on the main dashboard. See Section enumerating WebRTC functions for details. The example below reports error when creating an SDP offer:
Step 5: (OPTIONAL) sendFabricEvent()
// send fabricEvent: videoPause
callstats.sendFabricEvent(pcObject, callstats.fabricEvent.videoPause, conferenceID);
// devices are returned by the
/*
var devices = navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
//a typical device looks like:
//device= { "deviceId":"default","kind":"videoinput","label":"FaceTime HD Camera","groupId":"2004946474"}
*/
var eventData = {
deviceList: devices // array of active device
};
// send fabricEvent: activeDeviceList
callstats.sendFabricEvent(pcObject, callstats.fabricEvent.activeDeviceList, conferenceID, eventData);
During the conference, users might perform several actions impacting the measurements and conference analysis. The user might mute the audio or switch off the camera or do screen sharing during a conference. These events can directly impact the measurement data (For example, you can see a significant drop in throughput when camera is switched off). For the list of all possible conference events, please refer here
Send the appropriate fabricEvent
via sendFabricEvent()
.
fabricSetup
andfabricSetupFailed
has been deprecated in v2.1.0 and v3.10.0, respectively, these events are now generated automatically by the JS library.
send
fabricTerminated
when an endpoint or participant disconnects from the conference, it notifies callstats.js to stop monitoring the local PeerConnection. Depending on the implementation of the hangup in your WebRTC application (may have to rely on signaling), the remote endpoint sends afabricTerminated
event before destroying its local PeerConnection object. callstats.io monitors each PeerConnection in real-time, and generates summary statistics when the participant leaves. The summary of statistics for each conference is aggregated when all the participant have left. If nofabricTerminated
event is received, callstats.io will summarize and aggregate the summary statistics 30 seconds after the last measurement for a conference is received.send
fabricHold
orfabricResume
whenever the user holds and resumes the call. This is usually done when a user gets multiple incoming conference calls, and has to stop transmitting (hold) on one conference call to transmit on the other, and then returns to earlier call to resume transmitting (unhold).send
dominantSpeaker
when a particular userID appears to be the only participant speaking. Typically, each endpoint calculates the dominant speaker over the set of participants in a sliding time-window (say, 10 seconds). Then the endpoint that notices that it is the dominant speaker sends the event.send
activeDeviceList
whenever the audio input, audio output, or video input device changes.
Step 6: (OPTIONAL) associateMstWithUserID()
// After O/A is complete, i.e., onAddStream is fired
var localUserID = "Alice";
var remoteUserID = "Bob";
var conferenceID = "AliceAndBobAndCharlie";
var mstLabel = "front-camera";
// SSRC1 is the SSRCs of the local video stream
// SSRC2 is the SSRC of the remote video stream, usually received in the remote SDP
// mstLabel is a developer provided string that lets them identify
// various tracks (e.g., front-camera, back-camera, without looking at the
// configurations of the individual MSTs). In this example, we assume it is the
// front-camera.
callstats.associateMstWithUserID(pc, localUserID, conferenceID, ssrc1, mstLabel);
callstats.associateMstWithUserID(pc, remoteUserID, conferenceID, ssrc2, mstLabel);
When interacting with the conference server, the developer is most likely going to use the name or identifier associated with the conference server as the remoteUserID
. A typical conference bridge (for example, Jitsi Videobridge) transmits multiple media stream tracks within a peer connection. In which case, using a remote participant’s userID is impractical as there maybe several participants.
Since callstats.js ver. 3.3.x
, we allow mapping Synchronization Source Identifier (SSRC) for a mediastreamtrack to a userID (both local and remote). By default the local and remote MediaStreamTracks are automatically mapped to the localUserID and remoteUserID. With associateMstWithUserID()
, you can override the actual local and remote userIDs to the correct association. If the DOM identifiers of the video tags associated to each participant, callstats.js will calculate better quality scores for each participant. The code example shows how the API can be integrated:
More discussion related to the motivation of associateMstWithUserID()
is covered in the following blog post.
Step 7: (OPTIONAL) sendUserFeedback()
var overallRating = 4; // 1-5 rating, typically according to MOS scale.
var feedback = {
"userID": localUserID, //mandatory
"overall": overallRating, //mandatory
};
callstats.sendUserFeedback(conferenceID, feedback, pcCallback);
The developers are expected to design an appropriate UI to get user input on quality at the end of the call. Typically, services collect user feedback based on the Mean Opinion Score (MOS). However, it is not neccessary to use all values of the MOS scale, for example a service using only 2 point scale: it can associate 1 and 5 to bad and excellent, respectively and not use the values 2 to 4.
Step 8: (OPTIONAL) Handling stats from csStatsCallback()
The developers can handle the stats received from csStatsCallback function in a way suitable to their application. It can be used for displaying bitrate or based on the conference quality indicators applications can change their settings etc. For more details check this blog post.
Step 9: (OPTIONAL) Submitting application logs
The developers can send application error logs using reportError()
API and track them on callstats.io dashboard. The logs will help in debugging the corresponding conferences. The error
can be an object or a string.
var error1 = {
message: "Error message",
error: "Error 1",
stack: "stack trace for the error"
};
callstats.reportError(pc, confID, callstats.webRTCFunctions.applicationLog, error1);
error2 = "application error ";
callstats.reportError(pc, confID, callstats.webRTCFunctions.applicationLog, error2);
Step 10: (OPTIONAL) Obtaining the default configuration
// example: default configuration callback
function csDefaultConfigCallback(config) {
var pc = new RTCPeerConnection(config.peerConnection);
getUserMedia(config.media);
}
// Setting the default configuration callback
callstats.on(“defaultConfig”, csDefaultConfigCallback);
You can obtain the default configuration you have set on the dashboard via default configuration callback. The default configuration can be set for the Peer Connection or the media. You can set the configuration by visiting the “App Settings” page and navigating to the “configuration” tab. The section RTCPeerConnection configuration and Media constraints can be used for entering the default configuration in JSON format.
Loading with require.js
Script loading with require.js
<script data-main="YOUR REQUIRE JS CONFIG FILE" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.15/require.js"></script>
requirejs.config({
// 1. define source paths to other dependencies
paths: {
//jQuery usage is deprecated from the version 3.10.0 onwards and socket.io is deprecated from 3.16.0
callstats: "https://api.callstats.io/static/callstats.min"
},
shim: {
'callstats': {
deps: [],
},
'WebRTCApp': {
deps: ['callstats',... <other dependencies> ...],
}
}
});
// 3. load your main webRTCApp which depends on callstats
require(['WebRTCApp']);
The require.js helps load dependencies at the origin server. Below is an example snippet:
Generating userID, conferenceID, and JWT
userID
The userID
is expected to be unique, and the max length is 256 bytes. It is used by callstats.io to distinguish between different endpoints. The userIDs generated by the origin server and therefore the origin server decides if these identifiers are ephemeral or actual usernames.
callstats.io treats userIDs
as opaque identifiers, the origin server MAY choose to pass the same identifiers as it is using or send a SHA1 hash of the username instead (where the username and domain are the ones used by the origin server). If you are not going to search by userID, we prefer that you use ephemeral userIDs. Alternatively, we support internationalization, and non-English named usernames, we automatically encode all incoming userID
strings into UTF-8, and they are case-sensitive.
Typically, if the same user joins the conference via multiple means (devices or browsers) at the same time, the origin server MAY use the same userID. If the userID
changes, the app MUST call initialize()
again.
localUserID: The local userID that creates the PeerConnection, this userID is used when calling
initialize()
remoteUserID: This is the userID of the remote peer, in a mesh topology this is the userID of the remote participant. Alternative, when communicating with a conferencing server there are two choices.
Using multiple PeerConnections between a local userID and conference server: The number of PeerConnections perhaps uniquely match 1:1 with the number of participants in the session. That is, there may be one PeerConnection which just sends media to the conference server and multiple PeerConnections carrying MediaStreamTracks (MSTs) associated with different participants. In this case, it may be advisable to use the
conferenceServerID
as the remoteUserID for the outbound media, and the actual remoteUserIDs for the inbound media.Multiple MSTs in a PeerConnection: There is only one PeerConnection between the localUserID and the conference bridge and the PeerConnection carries all the MediaStreamTracks (MSTs) received at the bridge from the various participants. In this case, the remoteUserID is the name of the conference bridge (e.g., conferenceServerID). More discussion about this is covered in the following blog post.
UserID objects
In some cases, customers want to provide the actual username in addition to the alias to callstats.io. For example, the actual username can be accessed by the customer support, while the alias is shown to engineering team. The customer service representative needs to know the real names of the users to perform their work, while the engineering team does not. This functionality helps callstats.io separate the userID information and show the correct end-user information based on the user role.
Since callstats.js version 3.14, it accepts userID both as a String or an object.
conferenceID
The conferenceID
is a unique call identifier to associate all userIDs in a conference. Therefore, all participants in a common call MUST use the same conferenceID
. It is also generated by the origin server and MUST be temporally unique (i.e., for the duration of the call), the origin server MAY reuse the conferenceID
identifier again for separate conferences at a later time, after the previous call using it has ended. The maximum length of the conferenceID is 256 bytes.
Like userIDs, callstats.io treats conferenceIDs
as opaque identifiers, the origin server MAY use the same identifier as the one generated by the conference bridge, or use the URL of the page where the WebRTC call is taking place (e.g., www.example.com/conferenceID), concatenate current time-stamp with a random number, or pass a SHA1 hash of one of the above schemes. To enable internationalization, and non-English named conferenceIDs, we automatically encode all incoming conferenceID
strings into UTF-8, and these strings are case-sensitive. This helps you as an app-developer or WebRTC service operator to easily perform dashboard search by directly copy-pasting (e.g., インターネット, интернэт, ອິນເຕີເນັດ).
The origin server MAY reuse the userID
within the same conference call, i.e., two or more endpoints report using the same userID
and conferenceID
. while this is NOT RECOMMENDED, the callstats.io will still attempt to distinguish these userIDs
as distinct entities.
Third-party authentication
Token format used by callstats.io’s third party authentication system is JWT. Currently the only supported algorithm is ES256.
API
The API is defined above.
Token claims
{
"userID":"4358",
"appID":"545619706",
"keyID":"0123456789abcedf00",
"iat":1465221382,
"nbf":1465221682,
"exp":1465221682,
"jti":"25b30fb33a7764d2971534507718f35274bb"
}
Token supports following claims:
Claim | Required/optional | Type | Description |
---|---|---|---|
appID | Required | String | This is your AppID. |
userID | Required | String | This is endpoint’s local user identifier. |
keyID | Required | String | ID of the key that was used to generate this token. This can be obtained from secrets configuration page |
exp | Optional | NumericDate | Token expiration time. It’s recommended that this is set 5-10 minutes into the future. |
nbf | Optional | NumericDate | Token not valid before. It’s recommended that this is set 5-10 minutes into the past. |
jti | Optional | String | JWT ID. This is used for audit logging. |
Alternative Step 2: Initialize() with JWT (Client example)
//initialize the app with application tokens
var AppID = "YOUR APPLICATION ID";
function exampleTokenGenerator(initialToken) {
var cached = null;
if (initalToken)
var cached = initialToken;
// forcenew = set to true if application should generate new token and false if
// it's okay to use cached token
// callback(error, token). error should be set to non-null if there was an
// non-recoverable error. Token should be the JWT. Please see section
// "Third-party Authentication" for more complete documentation
return function(forcenew, callback) {
if (!forcenew && cached !== null)
return callback(null, cached);
// 1. get new token
var xhr = new XMLHttpRequest();
xhr.open('POST', '/getToken');
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
xhr.onload = function() {
// Did we get 200 OK response?
if (xhr.status == 200) {
// Get token and send it to callback
var resp = JSON.parse(req.responseText);
// the token should contain the claims defined in third party authentication
return callback(null, resp.token);
}
console.log("Couldn't get the token");
console.log(req.responseText);
// if uncorrectable error happens, inform callstats.io
return callback('Unknown error');
};
xhr.send(data);
};
}
function csInitCallback (csStatus, csMsg) {
console.log("Initializing Status: "+csStatus+" msg: "+csMsg);
}
//userID is generated or given by the origin server
callstats.initialize(AppID, exampleTokenGenerator(initialToken), userID, csInitCallback, csStatsCallback, configParams);
After the user is authenticated with the origin server (or when the page loads), call initialize()
with appropriate parameters (see API section). Check the callback for errors. If the authentication succeeds, callstats.js
will receive an appropriate authentication token to make subsequent API calls.
Generating a JSON Web Token (JWT)
var express = require('express');
var jwt = require('jsonwebtoken');
var app = express();
var fs = require('fs');
var crypto = require('crypto');
// Your ECDSA private key for which the public key is submitted to
// callstats.io dashboard
var key = fs.readFileSync('ecprivate.key');
var keyid = '0102030405060709';
var appid = 12345678;
// Dummy audit log
var audit = {log: console.log};
// Dummy session middleware
app.use(function (req, res, next) {
req.user = {id: 300};
next();
});
// Dummy JWT ID generator
var randomIdGenerator = function () {
return 42;
};
app.post('/getToken', function (req, res) {
if (!req.user) {
res.status(403);
return res.send(JSON.stringify({error: "User not logged in"}));
}
var randomid = randomIdGenerator().toString();
var token = null;
try {
token = jwt.sign(
{
userID: req.user.id.toString(),
appID: appid,
keyID: keyid
}, key,
{
algorithm: "ES256",
jwtid: randomid,
expiresIn: 300, //5 minutes
notBefore: -300 //-5 minutes
});
} catch (err) {
console.log(err);
res.status(500);
return res.send(JSON.stringify({error: "Token creation failed"}));
}
audit.log({action: "GrantToken", user: req.user.id, tokenid: randomid});
res.status(200);
res.send(JSON.stringify({token: token}));
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
You can use the following code as an example to generate JWT for authenticating the endpoint.
To correctly generate a token keep in mind the following:
Create a token header as a JSON:
{ "typ":"JWT", "alg":"HS256" }
Create a token payload as a JSON in the format:
{ "userID": x, "appID": a, "keyID": k, "iat": i, "nbf": n, "exp": e, "jti": j }
Please note thatiat
,nbf
,exp
,jti
are optional, but it is recommended to use it, otherwise the token won’t be timed out. More informationn can be found in RFC 7519.Encode with base64 stringified header and payload and join them with dot. In pseudo code something like:
token = base64Encode(JSON.stringify(header)) + "." + base64Encode(JSON.stringify(payload))
Sign your token with HMAC SHA-256 algorithm
Use signed token “code” in the message that you send in the authentication request
Support
If you have comments or have new feature requests, join the discussion on the mailing list or drop us an email.
Webhook Notifications
{
"reason":"dissatisfactoryUserFeedback","timestamp":1471354044616,"appId":"xxxxx","triggerType":"interval",
"actualValue":5,"threshold":0.2,"fractionalValue":0.3333333333333333,
"period":{"start":1458125913802,"end":1458129519080},
"url":"https:\/\/dashboard.callstats.io\/search?feedback12=1",
"text":"33% of the conferences had dissatisfactory feedback in the last hour i.e., 5 of 15 total conferences with appID xxxxx. The current trigger threshold is 20%."
}
{
"reason":"failedConferences","timestamp":1471354046133,"appId":"xxxxx","triggerType":"interval",
"actualValue":7,"threshold":0.1,"fractionalValue":0.11290322580645161,
"period":{"start":1458125913802,"end":1458129519080},
"url":"https:\/\/dashboard.callstats.io\/search?failureGroup=totalFailure",
"text":"11% of the conferences failed in the last hour i.e., 7 of 62 total conferences with appID xxxxx. The current trigger threshold is 10%."
}
JSON encoding
callstats.io sends periodic notifications (JSON encoded messages) to webhook URLs. The notification messages are sent when either a single event occurs (individual notification) or a metric exceeds a threshold value in a defined interval (interval notification), in the message these are identified by the triggerType
. Details of the notification message structure are below:
Keys | Description |
---|---|
reason |
camelCase string identifying the notification. |
timestamp |
Unix timestamp in seconds when the message was sent. |
appId |
The application identifier corresponding to the message. |
triggerType |
One of interval or individual . |
actualValue |
The actual measured value of the metric when the message was triggered. |
threshold |
The threshold over which the notification is triggered. For individual metrics, this value is 1. |
fractionalValue |
The ratio of the actualValue of a certain metric and the total measurement points of that metric. e.g., failed/total conferences. |
period |
Object containing start and end timestamps. |
url |
The search URL that will find the conference on callstats.io dashboard. |
text |
Notification in english text. |
Keys for the Notification reason
Keys | Description |
---|---|
failedConferences | ratio of failed conferences exceeded threshold |
dissatisfactoryUserFeedback | ratio of user feedback with dissatisfactory rating exceeded threshold |