From 2b4caf937f54b589a46f8e5c3f8191bfeee1a5f4 Mon Sep 17 00:00:00 2001 From: Jan Fellner Date: Thu, 6 Aug 2020 00:08:59 +0200 Subject: [PATCH 1/4] Enhanced functionality in the created peerConnection object. getRtcStatsId() - to fetch the assigned id from the peerConnection statsCallback - an optional callback that is invoked after statistics have been fetched to modify the statistic data before the are handled (e.g. to remove data or to add own data from the signalling layer / switching of simulcast layers, which layers are available etc pp.) --- README.md | 21 +++++++++++++++++++++ rtcstats.d.ts | 22 ++++++++++++++++++++++ rtcstats.js | 17 +++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/README.md b/README.md index c54a0f2..c9067f7 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,27 @@ var pc = new RTCPeerConnection(yourConfiguration, { }) ``` +The new peerConnection object offers additional functions and callbacks: +```javascript +/** + * This is the rtcstatistic id ("PC_0", "PC_1", ...) assigned while constructing the peerConnection object + */ +var assignedID = pc.getRtcStatsId(); + +/** + * This is an optional callback that will be called after statistics have been fetched from the peerConnection + * It can be used to + * * modify or filter data from the statistics + * * add own data to the statistics you need to have in the results + * (reflect status changes on the signalling layer you want to see in association with the statistics) + * (e.g. switching from simulcast layers) + */ + pc.statsCallback = function(rawData: RTCStatsReport) => RTCStatsReport + + +``` + + If that integration is not possible there is a fallback integration which allows sending per-client information about the user id and conference id. This can be used by calling diff --git a/rtcstats.d.ts b/rtcstats.d.ts index 647e8b6..9a0bf4f 100644 --- a/rtcstats.d.ts +++ b/rtcstats.d.ts @@ -8,6 +8,28 @@ export type RTCStatsDataType = RTCConfiguration | RTCIceCandidate | RTCSignaling RTCPeerConnectionState | undefined | ondatachannelArgs | string | RTCOfferOptions | undefined | createOfferArgs | createAnswerArgs | RTCSessionDescriptionInit | addIceCandidateArgs | object; +/** + * The extended functions of the peerConnection are exposed with this interface. + * Simply cast the peerConnection to this type to make use of em. + * const pc = new RTCPeerConnection(arg) as RTCStatsPeerConnection; + */ +export interface RTCStatsPeerConnection extends RTCPeerConnection +{ + /** + * This is the rtcstatistic id ("PC_0", "PC_1", ...) assigned while constructing the peerConnection object + */ + public getRtcStatsId(): string; + /** + * This is an optional callback that will be called after statistics have been fetched from the peerConnection + * It can be used to + * * modify or filter data from the statistics + * * add own data to the statistics you need to have in the results + * (reflect status changes on the signalling layer you want to see in association with the statistics) + * (e.g. switching from simulcast layers) + */ + public statsCallback: (rawData: RTCStatsReport) => RTCStatsReport; +} + declare module "rtcstats" { /** * Initializes the logging and statistics callback into the trace method for all noted methods diff --git a/rtcstats.js b/rtcstats.js index 8278fee..b71237d 100644 --- a/rtcstats.js +++ b/rtcstats.js @@ -128,6 +128,15 @@ module.exports = function(trace, getStatsInterval, prefixesToWrap) { var id = 'PC_' + peerconnectioncounter++; pc.__rtcStatsId = id; + // The optional callback that is invoked after having received the statistics via getStats() + var callback = undefined; + pc.statsCallback = callback; + + // Retrieve the rtcstats id used from outside + pc.getRtcStatsId = function() { + return pc.__rtcStatsId; + }; + if (!config) { config = { nullConfig: true }; } @@ -188,6 +197,10 @@ module.exports = function(trace, getStatsInterval, prefixesToWrap) { var getStats = function() { if (isFirefox || isSafari) { pc.getStats(null).then(function(res) { + // If the callback has been set we call it now to let the implementation modify the statics + // e.g. add own properties from the signalling layer + if(pc.statsCallback) + res = pc.statsCallback(res); var now = map2obj(res); var base = JSON.parse(JSON.stringify(now)); // our new prev trace('getstats', id, deltaCompression(prev, now)); @@ -195,6 +208,10 @@ module.exports = function(trace, getStatsInterval, prefixesToWrap) { }); } else { pc.getStats(function(res) { + // If the callback has been set we call it now to let the implementation modify the statics + // e.g. add own properties from the signalling layer + if(pc.statsCallback) + res = pc.statsCallback(res); var now = mangleChromeStats(pc, res); var base = JSON.parse(JSON.stringify(now)); // our new prev trace('getstats', id, deltaCompression(prev, now)); From ed9478e6705d550b7e503c16c8f5ad361c8d8f2e Mon Sep 17 00:00:00 2001 From: Jan Fellner Date: Thu, 6 Aug 2020 12:23:07 +0200 Subject: [PATCH 2/4] the statsCallback is optional and may be undefined --- rtcstats.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtcstats.d.ts b/rtcstats.d.ts index 9a0bf4f..5ad4db3 100644 --- a/rtcstats.d.ts +++ b/rtcstats.d.ts @@ -22,14 +22,14 @@ export interface RTCStatsPeerConnection extends RTCPeerConnection /** * This is an optional callback that will be called after statistics have been fetched from the peerConnection * It can be used to - * * modify or filter data from the statistics - * * add own data to the statistics you need to have in the results + * * modify or filter data from the statistics + * * add own data to the statistics you need to have in the results * (reflect status changes on the signalling layer you want to see in association with the statistics) * (e.g. switching from simulcast layers) */ - public statsCallback: (rawData: RTCStatsReport) => RTCStatsReport; + public statsCallback?: (rawData: RTCStatsReport) => RTCStatsReport; } - + declare module "rtcstats" { /** * Initializes the logging and statistics callback into the trace method for all noted methods From ab78396072fd72afc56cfdf53ae1e93a5fdbb226 Mon Sep 17 00:00:00 2001 From: Jan Fellner Date: Thu, 6 Aug 2020 23:29:18 +0200 Subject: [PATCH 3/4] Fixes for chrome (does not allow custom constraints) use promise based statistics also for chrome --- rtcstats.d.ts | 14 ++++++++---- rtcstats.js | 63 ++++++++++++++++++--------------------------------- 2 files changed, 32 insertions(+), 45 deletions(-) diff --git a/rtcstats.d.ts b/rtcstats.d.ts index 5ad4db3..64c9629 100644 --- a/rtcstats.d.ts +++ b/rtcstats.d.ts @@ -8,6 +8,12 @@ export type RTCStatsDataType = RTCConfiguration | RTCIceCandidate | RTCSignaling RTCPeerConnectionState | undefined | ondatachannelArgs | string | RTCOfferOptions | undefined | createOfferArgs | createAnswerArgs | RTCSessionDescriptionInit | addIceCandidateArgs | object; +/** + * Type definition for an indexed object provided and returned in the statsCallback + */ +export interface RTCStatsData { + [index: string]: any +} /** * The extended functions of the peerConnection are exposed with this interface. * Simply cast the peerConnection to this type to make use of em. @@ -22,14 +28,14 @@ export interface RTCStatsPeerConnection extends RTCPeerConnection /** * This is an optional callback that will be called after statistics have been fetched from the peerConnection * It can be used to - * * modify or filter data from the statistics - * * add own data to the statistics you need to have in the results + * * modify or filter data from the statistics + * * add own data to the statistics you need to have in the results * (reflect status changes on the signalling layer you want to see in association with the statistics) * (e.g. switching from simulcast layers) */ - public statsCallback?: (rawData: RTCStatsReport) => RTCStatsReport; + public statsCallback?: (rawData: RTCStatsData) => RTCStatsData; } - + declare module "rtcstats" { /** * Initializes the logging and statistics callback into the trace method for all noted methods diff --git a/rtcstats.js b/rtcstats.js index b71237d..f9c75fe 100644 --- a/rtcstats.js +++ b/rtcstats.js @@ -52,23 +52,6 @@ function deltaCompression(oldStats, newStats) { return newStats; } -function mangleChromeStats(pc, response) { - var standardReport = {}; - var reports = response.result(); - reports.forEach(function(report) { - var standardStats = { - id: report.id, - timestamp: report.timestamp.getTime(), - type: report.type, - }; - report.names().forEach(function(name) { - standardStats[name] = report.stat(name); - }); - standardReport[standardStats.id] = standardStats; - }); - return standardReport; -} - function dumpStream(stream) { return { id: stream.id, @@ -124,7 +107,18 @@ module.exports = function(trace, getStatsInterval, prefixesToWrap) { } var origPeerConnection = window[prefix + 'RTCPeerConnection']; var peerconnection = function(config, constraints) { - var pc = new origPeerConnection(config, constraints); + + // Chrome does not accept if the constraints contain something it is not aware of + // So we make a real copy and remove stuff we use internally + var constraintsCopy = JSON.parse(JSON.stringify(constraints)); + if(constraintsCopy.rtcStatsClientId) + delete constraintsCopy.rtcStatsClientId + if(constraintsCopy.rtcStatsPeerId) + delete constraintsCopy.rtcStatsPeerId + if(constraintsCopy.rtcStatsConferenceId) + delete constraintsCopy.rtcStatsConferenceId + + var pc = new origPeerConnection(config, constraintsCopy); var id = 'PC_' + peerconnectioncounter++; pc.__rtcStatsId = id; @@ -195,29 +189,16 @@ module.exports = function(trace, getStatsInterval, prefixesToWrap) { var prev = {}; var getStats = function() { - if (isFirefox || isSafari) { - pc.getStats(null).then(function(res) { - // If the callback has been set we call it now to let the implementation modify the statics - // e.g. add own properties from the signalling layer - if(pc.statsCallback) - res = pc.statsCallback(res); - var now = map2obj(res); - var base = JSON.parse(JSON.stringify(now)); // our new prev - trace('getstats', id, deltaCompression(prev, now)); - prev = base; - }); - } else { - pc.getStats(function(res) { - // If the callback has been set we call it now to let the implementation modify the statics - // e.g. add own properties from the signalling layer - if(pc.statsCallback) - res = pc.statsCallback(res); - var now = mangleChromeStats(pc, res); - var base = JSON.parse(JSON.stringify(now)); // our new prev - trace('getstats', id, deltaCompression(prev, now)); - prev = base; - }); - } + pc.getStats(null).then(function(res) { + // If the callback has been set we call it now to let the implementation modify the statics + // e.g. add own properties from the signalling layer + var now = map2obj(res); + if(pc.statsCallback) + now = pc.statsCallback(now); + var base = JSON.parse(JSON.stringify(now)); // our new prev + trace('getstats', id, deltaCompression(prev, now)); + prev = base; + }); }; // TODO: do we want one big interval and all peerconnections // queried in that or one setInterval per PC? From d0dd4ff65e7f32b0abc0938095929a87912e7630 Mon Sep 17 00:00:00 2001 From: Jan Fellner Date: Fri, 7 Aug 2020 13:33:59 +0200 Subject: [PATCH 4/4] Added missing ; --- rtcstats.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtcstats.js b/rtcstats.js index f9c75fe..3f57c91 100644 --- a/rtcstats.js +++ b/rtcstats.js @@ -112,11 +112,11 @@ module.exports = function(trace, getStatsInterval, prefixesToWrap) { // So we make a real copy and remove stuff we use internally var constraintsCopy = JSON.parse(JSON.stringify(constraints)); if(constraintsCopy.rtcStatsClientId) - delete constraintsCopy.rtcStatsClientId + delete constraintsCopy.rtcStatsClientId; if(constraintsCopy.rtcStatsPeerId) - delete constraintsCopy.rtcStatsPeerId + delete constraintsCopy.rtcStatsPeerId; if(constraintsCopy.rtcStatsConferenceId) - delete constraintsCopy.rtcStatsConferenceId + delete constraintsCopy.rtcStatsConferenceId; var pc = new origPeerConnection(config, constraintsCopy); var id = 'PC_' + peerconnectioncounter++;