getStats.js | WebRTC getStats API
Single Page Demo or Multi User P2P Demo
A tiny JavaScript library using WebRTC getStats API to return peer connection stats i.e. bandwidth usage, packets lost, local/remote ip addresses and ports, type of connection etc.
It is MIT Licenced, which means that you can use it in any commercial/non-commercial product, free of cost.
npm install getstats
cd node_modules
cd getstats
node server.js
# and open:
# http://localhost:9999/
To use it:
<script src="./node_modules/getstats/getStats.js"></script>
Link the library
<script src="https://cdn.webrtc-experiment.com/getStats.js"></script>
<!-- or min.js -->
<script src="https://cdn.webrtc-experiment.com/getStats.min.js"></script>
<!-- or without CDN -->
<script src="https://www.webrtc-experiment.com/getStats.js"></script>
<!-- or rawgit -->
<script src="https://rawgit.com/muaz-khan/getStats/master/getStats.js"></script>
Or link specific build:
<script src="https://github.com/muaz-khan/getStats/releases/download/1.0.6/getStats.js"></script>
window.getStats
To invoke directly:
getStats(peer, callback, interval);
RTCPeerConnection.prototype.getPeerStats
Or, to setup an instance method:
// if your code is encapsulated under a method
(function() {
RTCPeerConnection.prototype.getPeerStats = window.getStats;
// or
RTCPeerConnection.prototype.__getStats = window.getStats;
// or
RTCPeerConnection.prototype.getConnectionStats = window.getStats;
// or
RTCPeerConnection.prototype['your-choice'] = window.getStats;
})();
NEVER set/override RTCPeerConnection.prototype.getStats
because it is a reserved method.
// following will fail
RTCPeerConnection.prototype.getStats = window.getStats;
// it should be
RTCPeerConnection.prototype.intanceMethodNamae = window.getStats;
Usage
var rtcPeerConnection = new RTCPeerConnection(rtcConfig);
var repeatInterval = 2000; // 2000 ms == 2 seconds
rtcPeerConnection.getPeerStats(function(result) {
result.connectionType.remote.ipAddress
result.connectionType.remote.candidateType
result.connectionType.transport
result.bandwidth.speed // bandwidth download speed (bytes per second)
// to access native "results" array
result.results.forEach(function(item) {
if (item.type === 'ssrc' && item.transportId === 'Channel-audio-1') {
var packetsLost = item.packetsLost;
var packetsSent = item.packetsSent;
var audioInputLevel = item.audioInputLevel;
var trackId = item.googTrackId; // media stream track id
var isAudio = item.mediaType === 'audio'; // audio or video
var isSending = item.id.indexOf('_send') !== -1; // sender or receiver
console.log('SendRecv type', item.id.split('_send').pop());
console.log('MediaStream track type', item.mediaType);
}
});
}, repeatInterval);
Firefox?
peer.getStats(peer.getLocalStreams()[0].getAudioTracks()[0], function(results) {
// rest goes here
}, 5 * 1000);
result.datachannel
// states => open or close
alert(result.datachannel.state === 'open');
result.isOfferer
Offerer is the person who invoked createOffer
method.
result.encryption
To detect which tech is used to encrypt your connections.
alert(result.encryption === 'sha-256');
result.nomore()
This function can be used to ask to stop invoking getStats API.
btnStopGetStats.onclick = function() {
getStatsResult.nomore();
};
result.bandwidth
You can use result.bandwidth.speed
to detect your system's available download speed.
{
"speed": 25191,
"systemBandwidth": 0,
"sentPerSecond": 0,
"encodedPerSecond": 0,
"helper": {
"audioBytesSent": 103053,
"videoBytestSent": 0,
"videoBytesSent": 4316619
},
"availableSendBandwidth": "5181906",
"googActualEncBitrate": "294608",
"googAvailableSendBandwidth": "5181906",
"googAvailableReceiveBandwidth": "0",
"googRetransmitBitrate": "0",
"googTargetEncBitrate": "1700000",
"googBucketDelay": "0",
"googTransmitBitrate": "198296"
}
result.audio
{
"send": {
"tracks": ["ab5be64a-00b0-4c69-9ffa-b934a3cdaf92"],
"codecs": ["opus"],
"availableBandwidth": "0.4",
"streams": 1
},
"recv": {
"tracks": ["ab5be64a-00b0-4c69-9ffa-b934a3cdaf92"],
"codecs": ["opus"],
"availableBandwidth": "0.4",
"streams": 1
},
"bytesSent": 103053,
"bytesReceived": 103053
}
result.video
{
"send": {
"tracks": ["c07bbd24-4e99-4b94-b429-5174370f8d12"],
"codecs": ["VP9"],
"availableBandwidth": "24.2",
"streams": 1
},
"recv": {
"tracks": ["c07bbd24-4e99-4b94-b429-5174370f8d12"],
"codecs": ["VP9"],
"availableBandwidth": "25.5",
"streams": 1
},
"bytesSent": 4316619,
"bytesReceived": 4294692
}
result.connectionType
{
"systemNetworkType": "wifi",
"systemIpAddress": ["192.168.1.2:66666"],
"local": {
"candidateType": ["host"],
"transport": ["udp"],
"ipAddress": ["192.168.1.2:66666"],
"networkType": ["wlan"]
},
"remote": {
"candidateType": ["host"],
"transport": ["udp"],
"ipAddress": ["192.168.1.2:66666"],
"networkType": []
},
"transport": "udp"
}
result.resolutions
{
"send": {
"width": "640",
"height": "480"
},
"recv": {
"width": "640",
"height": "480"
}
}
result.results
It is an array that is returned by browser's native PeerConnection API.
// "getStatsResult" is your "result" object
getStatsResult.results.forEach(function(item) {
if (item.type === 'ssrc' && item.transportId === 'Channel-audio-1') {
var packetsLost = item.packetsLost;
var packetsSent = item.packetsSent;
var audioInputLevel = item.audioInputLevel;
var trackId = item.googTrackId; // media stream track id
var isAudio = item.mediaType === 'audio'; // audio or video
var isSending = item.id.indexOf('_send') !== -1; // sender or receiver
console.log('SendRecv type', item.id.split('_send').pop());
console.log('MediaStream track type', item.mediaType);
}
});
Above array looks like this:
[{
"googTrackId": "ab5be64a-00b0-4c69-9ffa-b934a3cdaf92",
"id": "googTrack_ab5be64a-00b0-4c69-9ffa-b934a3cdaf92",
"type": "googTrack",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"googInitiator": "true",
"id": "googLibjingleSession_2403774673032347671",
"type": "googLibjingleSession",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"googActualEncBitrate": "294608",
"googAvailableSendBandwidth": "5181906",
"googRetransmitBitrate": "0",
"googAvailableReceiveBandwidth": "0",
"googTargetEncBitrate": "1700000",
"googBucketDelay": "0",
"googTransmitBitrate": "198296",
"id": "bweforvideo",
"type": "VideoBwe",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"googTrackId": "c07bbd24-4e99-4b94-b429-5174370f8d12",
"id": "googTrack_c07bbd24-4e99-4b94-b429-5174370f8d12",
"type": "googTrack",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"googFingerprint": "8B:---:70",
"googFingerprintAlgorithm": "sha-256",
"googDerBase64": "MII----FdhT",
"id": "googCertificate_8B:---:70",
"type": "googCertificate",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"googFingerprint": "EE:---:80",
"googFingerprintAlgorithm": "sha-256",
"googDerBase64": "MI----kr",
"id": "googCertificate_EE:13:---:80",
"type": "googCertificate",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"googComponent": "1",
"remoteCertificateId": "googCertificate_EE:---:80",
"selectedCandidatePairId": "Conn-audio-1-0",
"dtlsCipher": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"localCertificateId": "googCertificate_8B:---:70",
"srtpCipher": "AES_CM_128_HMAC_SHA1_32",
"id": "Channel-audio-1",
"type": "googComponent",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"responsesSent": "12",
"requestsReceived": "12",
"googRemoteCandidateType": "local",
"googReadable": "true",
"googLocalAddress": "192.168.1.2:61885",
"consentRequestsSent": "1",
"googTransportType": "udp",
"googChannelId": "Channel-audio-1",
"googLocalCandidateType": "local",
"googWritable": "true",
"requestsSent": "12",
"googRemoteAddress": "192.168.1.2:60256",
"googRtt": "0",
"googActiveConnection": "true",
"packetsDiscardedOnSend": "0",
"bytesReceived": "4455306",
"responsesReceived": "12",
"remoteCandidateId": "Cand-aws75dws",
"localCandidateId": "Cand-4hzYq0su",
"bytesSent": "4466782",
"packetsSent": "5437",
"id": "Conn-audio-1-0",
"type": "googCandidatePair",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"portNumber": "61885",
"networkType": "wlan",
"ipAddress": "192.168.1.2",
"transport": "udp",
"candidateType": "host",
"priority": "2122260223",
"id": "Cand-4hzYq0su",
"type": "localcandidate",
"timestamp": "2017-11-21T04:09:48.850Z"
}, {
"portNumber": "60256",
"ipAddress": "192.168.1.2",
"transport": "udp",
"candidateType": "host",
"priority": "2122260223",
"id": "Cand-aws75dws",
"type": "remotecandidate",
"timestamp": "2017-11-21T04:09:48.850Z"
}, {
"googDecodingCTN": "2206",
"packetsLost": "0",
"googSecondaryDecodedRate": "0",
"googDecodingPLC": "21",
"packetsReceived": "1056",
"googExpandRate": "0.900574",
"googJitterReceived": "1",
"googDecodingCNG": "0",
"ssrc": "3719978459",
"googPreferredJitterBufferMs": "20",
"googSpeechExpandRate": "0.178711",
"totalSamplesDuration": "22.06",
"totalAudioEnergy": "0",
"transportId": "Channel-audio-1",
"mediaType": "audio",
"googDecodingPLCCNG": "72",
"googCodecName": "opus",
"googSecondaryDiscardedRate": "0",
"googDecodingNormal": "2113",
"googTrackId": "ab5be64a-00b0-4c69-9ffa-b934a3cdaf92",
"audioOutputLevel": "0",
"googAccelerateRate": "0",
"bytesReceived": "103053",
"googCurrentDelayMs": "61",
"googDecodingCTSG": "0",
"googCaptureStartNtpTimeMs": "3720226188883",
"googPreemptiveExpandRate": "0",
"googJitterBufferMs": "5",
"googDecodingMuted": "71",
"id": "ssrc_3719978459_recv",
"type": "ssrc",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"audioInputLevel": "38",
"packetsLost": "0",
"googTrackId": "ab5be64a-00b0-4c69-9ffa-b934a3cdaf92",
"googRtt": "1",
"googResidualEchoLikelihoodRecentMax": "0",
"googEchoCancellationReturnLossEnhancement": "-100",
"totalSamplesDuration": "0",
"googCodecName": "opus",
"transportId": "Channel-audio-1",
"mediaType": "audio",
"aecDivergentFilterFraction": "0",
"googEchoCancellationReturnLoss": "-100",
"googResidualEchoLikelihood": "0",
"googEchoCancellationQualityMin": "0",
"totalAudioEnergy": "0",
"ssrc": "4057371623",
"googJitterReceived": "1",
"googTypingNoiseState": "false",
"packetsSent": "1056",
"bytesSent": "103053",
"id": "ssrc_4057371623_send",
"type": "ssrc",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"googContentType": "realtime",
"googCaptureStartNtpTimeMs": "3720226188863",
"googTargetDelayMs": "28",
"packetsLost": "0",
"googDecodeMs": "3",
"googFrameHeightReceived": "480",
"googFrameRateOutput": "2",
"packetsReceived": "3843",
"ssrc": "2926085704",
"googRenderDelayMs": "10",
"googMaxDecodeMs": "4",
"googTrackId": "c07bbd24-4e99-4b94-b429-5174370f8d12",
"googFrameWidthReceived": "640",
"codecImplementationName": "libvpx",
"transportId": "Channel-audio-1",
"mediaType": "video",
"googTimingFrameInfo": "2498355253,490771956,490771960,490771967,490771967,490771974,0,1",
"googInterframeDelayMax": "67",
"googCodecName": "VP9",
"googFrameRateReceived": "2",
"qpSum": "35281",
"framesDecoded": "348",
"googNacksSent": "0",
"googFirsSent": "0",
"bytesReceived": "4294692",
"googCurrentDelayMs": "28",
"googMinPlayoutDelayMs": "0",
"googFrameRateDecoded": "2",
"googJitterBufferMs": "14",
"googPlisSent": "0",
"id": "ssrc_2926085704_recv",
"type": "ssrc",
"timestamp": "2017-11-21T04:10:10.905Z"
}, {
"googFrameWidthSent": "640",
"packetsLost": "0",
"googRtt": "1",
"googEncodeUsagePercent": "19",
"googCpuLimitedResolution": "false",
"googNacksReceived": "0",
"googBandwidthLimitedResolution": "false",
"googPlisReceived": "0",
"googAvgEncodeMs": "7",
"googTrackId": "c07bbd24-4e99-4b94-b429-5174370f8d12",
"googFrameRateInput": "2",
"framesEncoded": "348",
"codecImplementationName": "libvpx",
"transportId": "Channel-audio-1",
"mediaType": "video",
"googFrameHeightSent": "480",
"googFrameRateSent": "18",
"googCodecName": "VP9",
"qpSum": "35291",
"googAdaptationChanges": "0",
"ssrc": "3181351409",
"googFirsReceived": "0",
"packetsSent": "3884",
"bytesSent": "4316619",
"id": "ssrc_3181351409_send",
"type": "ssrc",
"timestamp": "2017-11-21T04:10:10.905Z"
}]
License
getStats.js is released under MIT licence . Copyright (c) Muaz Khan.