From 196c70aa07021684a7ea18209d94ebd23cac2ecb Mon Sep 17 00:00:00 2001 From: vrosenberg Date: Fri, 25 Feb 2022 13:52:00 +0100 Subject: [PATCH 01/11] add whitelist for cmcd http headers --- index.d.ts | 3 ++- src/core/Settings.js | 8 ++++++-- src/streaming/models/CmcdModel.js | 14 ++++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index 381b1b16d0..2ac041a545 100644 --- a/index.d.ts +++ b/index.d.ts @@ -320,7 +320,8 @@ declare namespace dashjs { cid?: string, rtp?: number, rtpSafetyFactor?: number, - mode?: 'query' | 'header' + mode?: 'query' | 'header', + enabledKeys?: Array } }; errors?: { diff --git a/src/core/Settings.js b/src/core/Settings.js index b8f247f4ac..04a047518e 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -200,7 +200,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest'; * cid: null, * rtp: null, * rtpSafetyFactor: 5, - * mode: Constants.CMCD_MODE_QUERY + * mode: Constants.CMCD_MODE_QUERY, + * enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] * } * }, * errors: { @@ -637,6 +638,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest'; * The method to use to attach cmcd metrics to the requests. 'query' to use query parameters, 'header' to use http headers. * * If not specified this value defaults to 'query'. + * @property {Array.} [enabledKeys] + * This value is used to specify the desired cmcd http headers in an array. */ /** @@ -923,7 +926,8 @@ function Settings() { cid: null, rtp: null, rtpSafetyFactor: 5, - mode: Constants.CMCD_MODE_QUERY + mode: Constants.CMCD_MODE_QUERY, + enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] } }, errors: { diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 3706d59216..d5fdff538e 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -174,10 +174,10 @@ function CmcdModel() { try { if (settings.get().streaming.cmcd && settings.get().streaming.cmcd.enabled) { const cmcdData = _getCmcdData(request); - const cmcdObjectHeader = _copyParameters(cmcdData, ['br', 'd', 'ot', 'tb']); - const cmcdRequestHeader = _copyParameters(cmcdData, ['bl', 'dl', 'mtp', 'nor', 'nrr', 'su']); - const cmcdStatusHeader = _copyParameters(cmcdData, ['bs', 'rtp']); - const cmcdSessionHeader = _copyParameters(cmcdData, ['cid', 'pr', 'sf', 'sid', 'st', 'v']); + const cmcdObjectHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['br', 'd', 'ot', 'tb'])); + const cmcdRequestHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['bl', 'dl', 'mtp', 'nor', 'nrr', 'su'])); + const cmcdStatusHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['bs', 'rtp'])); + const cmcdSessionHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['cid', 'pr', 'sf', 'sid', 'st', 'v'])); const headers = { 'CMCD-Object': _buildFinalString(cmcdObjectHeader), 'CMCD-Request': _buildFinalString(cmcdRequestHeader), @@ -200,6 +200,12 @@ function CmcdModel() { } } + function _applyEnabledKeysFilter(keys) { + + let enabledCMCDKeys = settings.get().streaming.cmcd.enabledKeys; + return keys.filter(key => enabledCMCDKeys.includes(key)); + } + function _getCmcdData(request) { try { let cmcdData = null; From 4f6b2254c465e71ccc8b442cf3b06802934ea8c4 Mon Sep 17 00:00:00 2001 From: vrosenberg Date: Fri, 25 Feb 2022 16:35:30 +0100 Subject: [PATCH 02/11] add reference player compatibility --- samples/dash-if-reference-player/app/main.js | 29 ++++++++++++++++++++ samples/dash-if-reference-player/index.html | 2 ++ 2 files changed, 31 insertions(+) diff --git a/samples/dash-if-reference-player/app/main.js b/samples/dash-if-reference-player/app/main.js index 6a382cda4d..b413322ba9 100644 --- a/samples/dash-if-reference-player/app/main.js +++ b/samples/dash-if-reference-player/app/main.js @@ -285,6 +285,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' $scope.audioTrackSwitchMode = 'neverReplace'; $scope.currentLogLevel = 'info'; $scope.cmcdMode = 'query'; + $scope.cmcdAllKeys = ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v'] // Persistent license $scope.persistentSessionId = {}; @@ -799,6 +800,28 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' }); } + $scope.getFormatedCmcdEnabledKeys = function() { + let cmcdEnabledKeys = $scope.cmcdEnabledKeys.split(','); + return $scope.cmcdAllKeys.map(key => { + let mappedKey = key; + if(!cmcdEnabledKeys.includes(key)) mappedKey = ""; + + return mappedKey; + }) + } + + $scope.updateCmcdEnabledKeys = function () { + let cmcdEnabledKeys = $scope.getFormatedCmcdEnabledKeys() + + $scope.player.updateSettings({ + streaming: { + cmcd: { + enabledKeys: cmcdEnabledKeys + } + } + }); + } + $scope.setStream = function (item) { $scope.selectedItem = JSON.parse(JSON.stringify(item)); $scope.protData = {}; @@ -946,6 +969,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' config.streaming.cmcd.cid = $scope.cmcdContentId ? $scope.cmcdContentId : null; config.streaming.cmcd.rtp = $scope.cmcdRtp ? $scope.cmcdRtp : null; config.streaming.cmcd.rtpSafetyFactor = $scope.cmcdRtpSafetyFactor ? $scope.cmcdRtpSafetyFactor : null; + config.streaming.cmcd.enabledKeys = $scope.cmcdEnabledKeys ? $scope.getFormatedCmcdEnabledKeys() : []; $scope.player.updateSettings(config); @@ -2057,7 +2081,12 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' if(currentConfig.streaming.cmcd.rtpSafetyFactor){ $scope.cmcdRtpSafetyFactor = currentConfig.streaming.cmcd.rtpSafetyFactor; } + $scope.cmcdMode = currentConfig.streaming.cmcd.mode; + + if(currentConfig.streaming.cmcd.enabledKeys){ + $scope.cmcdEnabledKeys = currentConfig.streaming.cmcd.enabledKeys; + } } function getUrlVars() { diff --git a/samples/dash-if-reference-player/index.html b/samples/dash-if-reference-player/index.html index 994f691905..08d39264f6 100644 --- a/samples/dash-if-reference-player/index.html +++ b/samples/dash-if-reference-player/index.html @@ -795,6 +795,8 @@ ng-click="setCmcdMode()"> Header + + From 92215c391130d965938075bfeb3e0336a08d30eb Mon Sep 17 00:00:00 2001 From: vrosenberg Date: Fri, 25 Feb 2022 13:52:00 +0100 Subject: [PATCH 03/11] add whitelist for cmcd http headers --- index.d.ts | 3 ++- src/core/Settings.js | 8 ++++++-- src/streaming/models/CmcdModel.js | 14 ++++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index b8305024ab..9f312385bc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -320,7 +320,8 @@ declare namespace dashjs { cid?: string, rtp?: number, rtpSafetyFactor?: number, - mode?: 'query' | 'header' + mode?: 'query' | 'header', + enabledKeys?: Array } }; errors?: { diff --git a/src/core/Settings.js b/src/core/Settings.js index b8f247f4ac..04a047518e 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -200,7 +200,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest'; * cid: null, * rtp: null, * rtpSafetyFactor: 5, - * mode: Constants.CMCD_MODE_QUERY + * mode: Constants.CMCD_MODE_QUERY, + * enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] * } * }, * errors: { @@ -637,6 +638,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest'; * The method to use to attach cmcd metrics to the requests. 'query' to use query parameters, 'header' to use http headers. * * If not specified this value defaults to 'query'. + * @property {Array.} [enabledKeys] + * This value is used to specify the desired cmcd http headers in an array. */ /** @@ -923,7 +926,8 @@ function Settings() { cid: null, rtp: null, rtpSafetyFactor: 5, - mode: Constants.CMCD_MODE_QUERY + mode: Constants.CMCD_MODE_QUERY, + enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] } }, errors: { diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 3706d59216..d5fdff538e 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -174,10 +174,10 @@ function CmcdModel() { try { if (settings.get().streaming.cmcd && settings.get().streaming.cmcd.enabled) { const cmcdData = _getCmcdData(request); - const cmcdObjectHeader = _copyParameters(cmcdData, ['br', 'd', 'ot', 'tb']); - const cmcdRequestHeader = _copyParameters(cmcdData, ['bl', 'dl', 'mtp', 'nor', 'nrr', 'su']); - const cmcdStatusHeader = _copyParameters(cmcdData, ['bs', 'rtp']); - const cmcdSessionHeader = _copyParameters(cmcdData, ['cid', 'pr', 'sf', 'sid', 'st', 'v']); + const cmcdObjectHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['br', 'd', 'ot', 'tb'])); + const cmcdRequestHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['bl', 'dl', 'mtp', 'nor', 'nrr', 'su'])); + const cmcdStatusHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['bs', 'rtp'])); + const cmcdSessionHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['cid', 'pr', 'sf', 'sid', 'st', 'v'])); const headers = { 'CMCD-Object': _buildFinalString(cmcdObjectHeader), 'CMCD-Request': _buildFinalString(cmcdRequestHeader), @@ -200,6 +200,12 @@ function CmcdModel() { } } + function _applyEnabledKeysFilter(keys) { + + let enabledCMCDKeys = settings.get().streaming.cmcd.enabledKeys; + return keys.filter(key => enabledCMCDKeys.includes(key)); + } + function _getCmcdData(request) { try { let cmcdData = null; From ebb41f93e4d3d0a4c03d8fb49adff0284d0de615 Mon Sep 17 00:00:00 2001 From: vrosenberg Date: Fri, 25 Feb 2022 16:35:30 +0100 Subject: [PATCH 04/11] add reference player compatibility --- samples/dash-if-reference-player/app/main.js | 29 ++++++++++++++++++++ samples/dash-if-reference-player/index.html | 2 ++ 2 files changed, 31 insertions(+) diff --git a/samples/dash-if-reference-player/app/main.js b/samples/dash-if-reference-player/app/main.js index 6a382cda4d..b413322ba9 100644 --- a/samples/dash-if-reference-player/app/main.js +++ b/samples/dash-if-reference-player/app/main.js @@ -285,6 +285,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' $scope.audioTrackSwitchMode = 'neverReplace'; $scope.currentLogLevel = 'info'; $scope.cmcdMode = 'query'; + $scope.cmcdAllKeys = ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v'] // Persistent license $scope.persistentSessionId = {}; @@ -799,6 +800,28 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' }); } + $scope.getFormatedCmcdEnabledKeys = function() { + let cmcdEnabledKeys = $scope.cmcdEnabledKeys.split(','); + return $scope.cmcdAllKeys.map(key => { + let mappedKey = key; + if(!cmcdEnabledKeys.includes(key)) mappedKey = ""; + + return mappedKey; + }) + } + + $scope.updateCmcdEnabledKeys = function () { + let cmcdEnabledKeys = $scope.getFormatedCmcdEnabledKeys() + + $scope.player.updateSettings({ + streaming: { + cmcd: { + enabledKeys: cmcdEnabledKeys + } + } + }); + } + $scope.setStream = function (item) { $scope.selectedItem = JSON.parse(JSON.stringify(item)); $scope.protData = {}; @@ -946,6 +969,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' config.streaming.cmcd.cid = $scope.cmcdContentId ? $scope.cmcdContentId : null; config.streaming.cmcd.rtp = $scope.cmcdRtp ? $scope.cmcdRtp : null; config.streaming.cmcd.rtpSafetyFactor = $scope.cmcdRtpSafetyFactor ? $scope.cmcdRtpSafetyFactor : null; + config.streaming.cmcd.enabledKeys = $scope.cmcdEnabledKeys ? $scope.getFormatedCmcdEnabledKeys() : []; $scope.player.updateSettings(config); @@ -2057,7 +2081,12 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' if(currentConfig.streaming.cmcd.rtpSafetyFactor){ $scope.cmcdRtpSafetyFactor = currentConfig.streaming.cmcd.rtpSafetyFactor; } + $scope.cmcdMode = currentConfig.streaming.cmcd.mode; + + if(currentConfig.streaming.cmcd.enabledKeys){ + $scope.cmcdEnabledKeys = currentConfig.streaming.cmcd.enabledKeys; + } } function getUrlVars() { diff --git a/samples/dash-if-reference-player/index.html b/samples/dash-if-reference-player/index.html index 994f691905..08d39264f6 100644 --- a/samples/dash-if-reference-player/index.html +++ b/samples/dash-if-reference-player/index.html @@ -795,6 +795,8 @@ ng-click="setCmcdMode()"> Header + + From e7697f0651cf3f5a8541ebeeb83d37953a897534 Mon Sep 17 00:00:00 2001 From: vrosenberg Date: Wed, 9 Mar 2022 14:31:42 +0100 Subject: [PATCH 05/11] fix issue with initial load on ref player --- samples/dash-if-reference-player/app/main.js | 27 ++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/samples/dash-if-reference-player/app/main.js b/samples/dash-if-reference-player/app/main.js index b413322ba9..06ac4f2b2e 100644 --- a/samples/dash-if-reference-player/app/main.js +++ b/samples/dash-if-reference-player/app/main.js @@ -800,18 +800,25 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' }); } - $scope.getFormatedCmcdEnabledKeys = function() { - let cmcdEnabledKeys = $scope.cmcdEnabledKeys.split(','); - return $scope.cmcdAllKeys.map(key => { - let mappedKey = key; - if(!cmcdEnabledKeys.includes(key)) mappedKey = ""; - - return mappedKey; - }) + $scope._getFormatedCmcdEnabledKeys = function() { + let formatedKeys; + if(!Array.isArray($scope.cmcdEnabledKeys)) { + let cmcdEnabledKeys = $scope.cmcdEnabledKeys.split(','); + formatedKeys = $scope.cmcdAllKeys.map(key => { + let mappedKey = key; + if(!cmcdEnabledKeys.includes(key)) mappedKey = ""; + + return mappedKey; + }); + } else { + formatedKeys = $scope.cmcdEnabledKeys; + } + + return formatedKeys } $scope.updateCmcdEnabledKeys = function () { - let cmcdEnabledKeys = $scope.getFormatedCmcdEnabledKeys() + let cmcdEnabledKeys = $scope.getFormatedCmcdEnabledKeys(); $scope.player.updateSettings({ streaming: { @@ -969,7 +976,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' config.streaming.cmcd.cid = $scope.cmcdContentId ? $scope.cmcdContentId : null; config.streaming.cmcd.rtp = $scope.cmcdRtp ? $scope.cmcdRtp : null; config.streaming.cmcd.rtpSafetyFactor = $scope.cmcdRtpSafetyFactor ? $scope.cmcdRtpSafetyFactor : null; - config.streaming.cmcd.enabledKeys = $scope.cmcdEnabledKeys ? $scope.getFormatedCmcdEnabledKeys() : []; + config.streaming.cmcd.enabledKeys = $scope.cmcdEnabledKeys ? $scope._getFormatedCmcdEnabledKeys() : []; $scope.player.updateSettings(config); From f3c07c6d0a2a668529b95b6f1d4cefd3d97ee702 Mon Sep 17 00:00:00 2001 From: vrosenberg Date: Wed, 9 Mar 2022 14:40:48 +0100 Subject: [PATCH 06/11] fix typo --- samples/dash-if-reference-player/app/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/dash-if-reference-player/app/main.js b/samples/dash-if-reference-player/app/main.js index 06ac4f2b2e..7865586fda 100644 --- a/samples/dash-if-reference-player/app/main.js +++ b/samples/dash-if-reference-player/app/main.js @@ -818,7 +818,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' } $scope.updateCmcdEnabledKeys = function () { - let cmcdEnabledKeys = $scope.getFormatedCmcdEnabledKeys(); + let cmcdEnabledKeys = $scope._getFormatedCmcdEnabledKeys(); $scope.player.updateSettings({ streaming: { From f80a9628f5e36c58cebfcef46fe673cdbc39413e Mon Sep 17 00:00:00 2001 From: vrosenberg Date: Wed, 9 Mar 2022 15:51:13 +0100 Subject: [PATCH 07/11] unit tests for enabled keys --- test/unit/streaming.models.CmcdModel.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/unit/streaming.models.CmcdModel.js b/test/unit/streaming.models.CmcdModel.js index e6a83d87d3..0ca174f23b 100644 --- a/test/unit/streaming.models.CmcdModel.js +++ b/test/unit/streaming.models.CmcdModel.js @@ -373,6 +373,26 @@ describe('CmcdModel', function () { expect(metrics.rtp).to.equal(10000); }); + it( 'getHeadersParameters() applies enabledKeys filter', function() { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + settings.update({streaming: {cmcd: {enabledKeys: ['d', 'ot', 'tb', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'rtp' , 'pr', 'sf', 'sid', 'st', 'v'] }}}); + let headers = cmcdModel.getHeaderParameters(request); + expect(headers[OBJECT_HEADER_NAME].split(',').map( e => { return e.split('=')[0]})).to.not.include('br'); + expect(headers[REQUEST_HEADER_NAME].split(',').map( e => { return e.split('=')[0]})).to.not.include('bl'); + expect(headers[STATUS_HEADER_NAME].split(',').map( e => { return e.split('=')[0]})).to.not.include('bs'); + expect(headers[SESSION_HEADER_NAME].split(',').map( e => { return e.split('=')[0]})).to.not.include('cid'); + + //revert settings + settings.update({streaming: {cmcd: {enabledKeys: ['br' , 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] }}}); + }); + it('getQueryParameter() returns correct metrics for MPD', function () { const REQUEST_TYPE = HTTPRequest.MPD_TYPE; const MEDIA_TYPE = 'video'; From e62b08650c9926148dea0b17c263039e82bb7586 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Fri, 8 Apr 2022 11:36:21 +0200 Subject: [PATCH 08/11] Update package-lock.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index f541d73fca..7c85199559 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "dashjs", - "version": "4.3.0", + "version": "4.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { From 6aa3751348315c2f893d41bb7d7d78cf74a47297 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Mon, 11 Apr 2022 11:45:45 +0200 Subject: [PATCH 09/11] Add CMCD whitelist for query parameters --- src/streaming/models/CmcdModel.js | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index d5fdff538e..636f554380 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -140,7 +140,8 @@ function CmcdModel() { try { if (settings.get().streaming.cmcd && settings.get().streaming.cmcd.enabled) { const cmcdData = _getCmcdData(request); - const finalPayloadString = _buildFinalString(cmcdData); + const filteredCmcdData = _applyWhitelist(cmcdData); + const finalPayloadString = _buildFinalString(filteredCmcdData); eventBus.trigger(MetricsReportingEvents.CMCD_DATA_GENERATED, { url: request.url, @@ -160,6 +161,22 @@ function CmcdModel() { } } + function _applyWhitelist(cmcdData) { + try { + const enabledCMCDKeys = settings.get().streaming.cmcd.enabledKeys; + + return Object.keys(cmcdData) + .filter(key => enabledCMCDKeys.includes(key)) + .reduce((obj, key) => { + obj[key] = cmcdData[key]; + + return obj; + }, {}); + } catch (e) { + return cmcdData; + } + } + function _copyParameters(data, parameterNames) { const copiedData = {}; for (let name of parameterNames) { @@ -174,10 +191,10 @@ function CmcdModel() { try { if (settings.get().streaming.cmcd && settings.get().streaming.cmcd.enabled) { const cmcdData = _getCmcdData(request); - const cmcdObjectHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['br', 'd', 'ot', 'tb'])); - const cmcdRequestHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['bl', 'dl', 'mtp', 'nor', 'nrr', 'su'])); - const cmcdStatusHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['bs', 'rtp'])); - const cmcdSessionHeader = _copyParameters(cmcdData, _applyEnabledKeysFilter(['cid', 'pr', 'sf', 'sid', 'st', 'v'])); + const cmcdObjectHeader = _copyParameters(cmcdData, _applyWhitelistByKeys(['br', 'd', 'ot', 'tb'])); + const cmcdRequestHeader = _copyParameters(cmcdData, _applyWhitelistByKeys(['bl', 'dl', 'mtp', 'nor', 'nrr', 'su'])); + const cmcdStatusHeader = _copyParameters(cmcdData, _applyWhitelistByKeys(['bs', 'rtp'])); + const cmcdSessionHeader = _copyParameters(cmcdData, _applyWhitelistByKeys(['cid', 'pr', 'sf', 'sid', 'st', 'v'])); const headers = { 'CMCD-Object': _buildFinalString(cmcdObjectHeader), 'CMCD-Request': _buildFinalString(cmcdRequestHeader), @@ -200,9 +217,9 @@ function CmcdModel() { } } - function _applyEnabledKeysFilter(keys) { + function _applyWhitelistByKeys(keys) { + const enabledCMCDKeys = settings.get().streaming.cmcd.enabledKeys; - let enabledCMCDKeys = settings.get().streaming.cmcd.enabledKeys; return keys.filter(key => enabledCMCDKeys.includes(key)); } From 0dc6230688faeda41b8ca1ec8cc30f1e6390b4f1 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Mon, 11 Apr 2022 11:50:30 +0200 Subject: [PATCH 10/11] Adjusts Settings.js to merge values that are provided as an array --- src/core/Settings.js | 4 +- test/unit/streaming.models.CmcdModel.js | 1304 ++++++++++++----------- 2 files changed, 709 insertions(+), 599 deletions(-) diff --git a/src/core/Settings.js b/src/core/Settings.js index b0c31d3d1c..6a3e7203bb 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -629,7 +629,7 @@ import Events from './events/Events'; * * If not specified this value defaults to 'query'. * @property {Array.} [enabledKeys] - * This value is used to specify the desired cmcd http headers in an array. + * This value is used to specify the desired CMCD parameters. Parameters not included in this list are not reported. */ /** @@ -934,7 +934,7 @@ function Settings() { for (let n in source) { if (source.hasOwnProperty(n)) { if (dest.hasOwnProperty(n)) { - if (typeof source[n] === 'object' && source[n] !== null) { + if (typeof source[n] === 'object' && !(source[n] instanceof Array) && source[n] !== null) { mixinSettings(source[n], dest[n], path.slice() + n + '.'); } else { dest[n] = Utils.clone(source[n]); diff --git a/test/unit/streaming.models.CmcdModel.js b/test/unit/streaming.models.CmcdModel.js index 0ca174f23b..a62049a0b8 100644 --- a/test/unit/streaming.models.CmcdModel.js +++ b/test/unit/streaming.models.CmcdModel.js @@ -31,13 +31,14 @@ describe('CmcdModel', function () { beforeEach(function () { cmcdModel = CmcdModel(context).getInstance(); cmcdModel.initialize(); - settings.update({streaming: {cmcd: {enabled: true, cid: null}}}); + settings.update({ streaming: { cmcd: { enabled: true, cid: null } } }); }); afterEach(function () { cmcdModel.reset(); cmcdModel = null; abrControllerMock.setTopBitrateInfo(null); + settings.reset(); }); describe('if configured', function () { @@ -49,650 +50,759 @@ describe('CmcdModel', function () { }); }); - it('getHeaderParameters() returns correct metrics for MPD', function () { - const REQUEST_TYPE = HTTPRequest.MPD_TYPE; - const MEDIA_TYPE = 'video'; - const MANIFEST_OBJECT_TYPE = 'm'; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; - - let headers = cmcdModel.getHeaderParameters(request); - expect(headers).to.have.property(SESSION_HEADER_NAME); - expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(OBJECT_HEADER_NAME); - expect(typeof headers[OBJECT_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(REQUEST_HEADER_NAME); - expect(typeof headers[REQUEST_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(STATUS_HEADER_NAME); - expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); - - let metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.have.property('sid'); - expect(metrics).to.not.have.property('cid'); - - metrics = parseQuery(headers[OBJECT_HEADER_NAME]); - expect(metrics).to.have.property('ot'); - expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); - }); - - it('getHeaderParameters() returns correct metrics for init segments', function () { - const REQUEST_TYPE = HTTPRequest.INIT_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const MANIFEST_OBJECT_TYPE = 'i'; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; - - let headers = cmcdModel.getHeaderParameters(request); - expect(headers).to.have.property(SESSION_HEADER_NAME); - expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(OBJECT_HEADER_NAME); - expect(typeof headers[OBJECT_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(REQUEST_HEADER_NAME); - expect(typeof headers[REQUEST_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(STATUS_HEADER_NAME); - expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); - - let metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.have.property('sid'); - expect(metrics).to.not.have.property('cid'); - - metrics = parseQuery(headers[OBJECT_HEADER_NAME]); - expect(metrics).to.have.property('ot'); - expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); - - metrics = parseQuery(headers[REQUEST_HEADER_NAME]); - expect(metrics).to.have.property('su'); - expect(metrics.su).to.equal(true); - }); - - it('getHeaderParameters() returns correct metrics for media segments', function () { - dashMetricsMock.setCurrentBufferLevel(15.34511); - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - const TOP_BITRATE = 20000; - const MEASURED_THROUGHPUT = 8327641; - const BUFFER_LEVEL = parseInt(dashMetricsMock.getCurrentBufferLevel() * 10) * 100; - const VIDEO_OBJECT_TYPE = 'v'; - const NEXT_OBJECT_URL = 'next_object'; - const NEXT_OBJECT_RANGE = '100-500'; - - abrControllerMock.setTopBitrateInfo({bitrate: TOP_BITRATE}); - abrControllerMock.setThroughputHistory({ - getSafeAverageThroughput: function () { - return MEASURED_THROUGHPUT; - } + describe('getHeaderParameters()', () => { + it('getHeaderParameters() returns correct metrics for MPD', function () { + const REQUEST_TYPE = HTTPRequest.MPD_TYPE; + const MEDIA_TYPE = 'video'; + const MANIFEST_OBJECT_TYPE = 'm'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + let headers = cmcdModel.getHeaderParameters(request); + expect(headers).to.have.property(SESSION_HEADER_NAME); + expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(OBJECT_HEADER_NAME); + expect(typeof headers[OBJECT_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(REQUEST_HEADER_NAME); + expect(typeof headers[REQUEST_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(STATUS_HEADER_NAME); + expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); + + let metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.have.property('sid'); + expect(metrics).to.not.have.property('cid'); + + metrics = parseQuery(headers[OBJECT_HEADER_NAME]); + expect(metrics).to.have.property('ot'); + expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); }); - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION, - url: 'http://test.url/firstRequest' - }; - - let headers = cmcdModel.getHeaderParameters(request); - expect(headers).to.have.property(SESSION_HEADER_NAME); - expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(OBJECT_HEADER_NAME); - expect(typeof headers[OBJECT_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(REQUEST_HEADER_NAME); - expect(typeof headers[REQUEST_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(STATUS_HEADER_NAME); - expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); - - let metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.have.property('sid'); - expect(metrics).to.not.have.property('cid'); - - metrics = parseQuery(headers[OBJECT_HEADER_NAME]); - expect(metrics).to.have.property('br'); - expect(metrics.br).to.equal(parseInt(BITRATE / 1000)); - expect(metrics).to.have.property('d'); - expect(metrics.d).to.equal(parseInt(DURATION * 1000)); - expect(metrics).to.have.property('ot'); - expect(metrics.ot).to.equal(VIDEO_OBJECT_TYPE); - expect(metrics).to.have.property('tb'); - expect(metrics.tb).to.equal(parseInt(TOP_BITRATE / 1000)); - - metrics = parseQuery(headers[REQUEST_HEADER_NAME]); - expect(metrics).to.have.property('bl'); - expect(metrics.bl).to.equal(BUFFER_LEVEL); - expect(metrics).to.have.property('dl'); - expect(metrics.dl).to.equal(BUFFER_LEVEL); - expect(metrics).to.have.property('mtp'); - expect(metrics.mtp).to.equal(parseInt(MEASURED_THROUGHPUT / 100) * 100); - expect(metrics).to.have.property('nor'); - expect(metrics.nor).to.equal(NEXT_OBJECT_URL); - - metrics = parseQuery(headers[STATUS_HEADER_NAME]); - expect(metrics).to.have.property('rtp'); - expect(typeof metrics.rtp).to.equal('number'); - expect(metrics.rtp % 100).to.equal(0); - - request.url = 'http://test.url/next_object'; - headers = cmcdModel.getHeaderParameters(request); - metrics = parseQuery(headers[REQUEST_HEADER_NAME]); - expect(metrics).to.have.property('nrr'); - expect(metrics.nrr).to.equal(NEXT_OBJECT_RANGE); - }); - - it('getHeaderParameters() returns correct metrics for other type', function () { - const REQUEST_TYPE = HTTPRequest.OTHER_TYPE; - const MEDIA_TYPE = 'video'; - const MANIFEST_OBJECT_TYPE = 'o'; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; - - let headers = cmcdModel.getHeaderParameters(request); - expect(headers).to.have.property(SESSION_HEADER_NAME); - expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(OBJECT_HEADER_NAME); - expect(typeof headers[OBJECT_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(REQUEST_HEADER_NAME); - expect(typeof headers[REQUEST_HEADER_NAME]).to.equal('string'); - expect(headers).to.have.property(STATUS_HEADER_NAME); - expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); - - let metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.have.property('sid'); - expect(metrics).to.not.have.property('cid'); - metrics = parseQuery(headers[OBJECT_HEADER_NAME]); - expect(metrics).to.have.property('ot'); - expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); - }); - it('getHeaderParameters() recognizes playback rate change through events', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - const CHANGED_PLAYBACK_RATE = 2.4; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION - }; - let headers = cmcdModel.getHeaderParameters(request); - let metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.not.have.property('pr'); - - eventBus.trigger(MediaPlayerEvents.PLAYBACK_RATE_CHANGED, {playbackRate: CHANGED_PLAYBACK_RATE}); - - headers = cmcdModel.getHeaderParameters(request); - metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.have.property('pr'); - expect(metrics.pr).to.equal(CHANGED_PLAYBACK_RATE); - }); + it('getHeaderParameters() returns correct metrics for init segments', function () { + const REQUEST_TYPE = HTTPRequest.INIT_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const MANIFEST_OBJECT_TYPE = 'i'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + let headers = cmcdModel.getHeaderParameters(request); + expect(headers).to.have.property(SESSION_HEADER_NAME); + expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(OBJECT_HEADER_NAME); + expect(typeof headers[OBJECT_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(REQUEST_HEADER_NAME); + expect(typeof headers[REQUEST_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(STATUS_HEADER_NAME); + expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); + + let metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.have.property('sid'); + expect(metrics).to.not.have.property('cid'); + + metrics = parseQuery(headers[OBJECT_HEADER_NAME]); + expect(metrics).to.have.property('ot'); + expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); + + metrics = parseQuery(headers[REQUEST_HEADER_NAME]); + expect(metrics).to.have.property('su'); + expect(metrics.su).to.equal(true); + }); - it('getHeaderParameters() recognizes playback seek through events', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION - }; - cmcdModel.getHeaderParameters(request); // first initial request will set startup to true - let headers = cmcdModel.getHeaderParameters(request); - let metrics = parseQuery(headers[STATUS_HEADER_NAME]); - expect(metrics).to.not.have.property('bs'); - metrics = parseQuery(headers[REQUEST_HEADER_NAME]); - expect(metrics).to.not.have.property('su'); - - eventBus.trigger(MediaPlayerEvents.PLAYBACK_SEEKED); - - headers = cmcdModel.getHeaderParameters(request); - metrics = parseQuery(headers[STATUS_HEADER_NAME]); - expect(metrics).to.have.property('bs'); - expect(metrics.bs).to.equal(true); - metrics = parseQuery(headers[REQUEST_HEADER_NAME]); - expect(metrics).to.have.property('su'); - expect(metrics.su).to.equal(true); - }); + it('getHeaderParameters() returns correct metrics for media segments', function () { + dashMetricsMock.setCurrentBufferLevel(15.34511); + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + const TOP_BITRATE = 20000; + const MEASURED_THROUGHPUT = 8327641; + const BUFFER_LEVEL = parseInt(dashMetricsMock.getCurrentBufferLevel() * 10) * 100; + const VIDEO_OBJECT_TYPE = 'v'; + const NEXT_OBJECT_URL = 'next_object'; + const NEXT_OBJECT_RANGE = '100-500'; + + abrControllerMock.setTopBitrateInfo({ bitrate: TOP_BITRATE }); + abrControllerMock.setThroughputHistory({ + getSafeAverageThroughput: function () { + return MEASURED_THROUGHPUT; + } + }); + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION, + url: 'http://test.url/firstRequest' + }; + + let headers = cmcdModel.getHeaderParameters(request); + expect(headers).to.have.property(SESSION_HEADER_NAME); + expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(OBJECT_HEADER_NAME); + expect(typeof headers[OBJECT_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(REQUEST_HEADER_NAME); + expect(typeof headers[REQUEST_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(STATUS_HEADER_NAME); + expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); + + let metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.have.property('sid'); + expect(metrics).to.not.have.property('cid'); + + metrics = parseQuery(headers[OBJECT_HEADER_NAME]); + expect(metrics).to.have.property('br'); + expect(metrics.br).to.equal(parseInt(BITRATE / 1000)); + expect(metrics).to.have.property('d'); + expect(metrics.d).to.equal(parseInt(DURATION * 1000)); + expect(metrics).to.have.property('ot'); + expect(metrics.ot).to.equal(VIDEO_OBJECT_TYPE); + expect(metrics).to.have.property('tb'); + expect(metrics.tb).to.equal(parseInt(TOP_BITRATE / 1000)); + + metrics = parseQuery(headers[REQUEST_HEADER_NAME]); + expect(metrics).to.have.property('bl'); + expect(metrics.bl).to.equal(BUFFER_LEVEL); + expect(metrics).to.have.property('dl'); + expect(metrics.dl).to.equal(BUFFER_LEVEL); + expect(metrics).to.have.property('mtp'); + expect(metrics.mtp).to.equal(parseInt(MEASURED_THROUGHPUT / 100) * 100); + expect(metrics).to.have.property('nor'); + expect(metrics.nor).to.equal(NEXT_OBJECT_URL); + + metrics = parseQuery(headers[STATUS_HEADER_NAME]); + expect(metrics).to.have.property('rtp'); + expect(typeof metrics.rtp).to.equal('number'); + expect(metrics.rtp % 100).to.equal(0); + + request.url = 'http://test.url/next_object'; + headers = cmcdModel.getHeaderParameters(request); + metrics = parseQuery(headers[REQUEST_HEADER_NAME]); + expect(metrics).to.have.property('nrr'); + expect(metrics.nrr).to.equal(NEXT_OBJECT_RANGE); + }); - it('getHeaderParameters() recognizes buffer starvation through events', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION - }; - cmcdModel.getHeaderParameters(request); // first initial request will set startup to true - let headers = cmcdModel.getHeaderParameters(request); - let metrics = parseQuery(headers[STATUS_HEADER_NAME]); - expect(metrics).to.not.have.property('bs'); - metrics = parseQuery(headers[REQUEST_HEADER_NAME]); - expect(metrics).to.not.have.property('su'); - - eventBus.trigger(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, { - state: MediaPlayerEvents.BUFFER_EMPTY, - mediaType: request.mediaType + it('getHeaderParameters() returns correct metrics for other type', function () { + const REQUEST_TYPE = HTTPRequest.OTHER_TYPE; + const MEDIA_TYPE = 'video'; + const MANIFEST_OBJECT_TYPE = 'o'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + let headers = cmcdModel.getHeaderParameters(request); + expect(headers).to.have.property(SESSION_HEADER_NAME); + expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(OBJECT_HEADER_NAME); + expect(typeof headers[OBJECT_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(REQUEST_HEADER_NAME); + expect(typeof headers[REQUEST_HEADER_NAME]).to.equal('string'); + expect(headers).to.have.property(STATUS_HEADER_NAME); + expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); + + let metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.have.property('sid'); + expect(metrics).to.not.have.property('cid'); + metrics = parseQuery(headers[OBJECT_HEADER_NAME]); + expect(metrics).to.have.property('ot'); + expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); }); - headers = cmcdModel.getHeaderParameters(request); - metrics = parseQuery(headers[STATUS_HEADER_NAME]); - expect(metrics).to.have.property('bs'); - expect(metrics.bs).to.equal(true); - metrics = parseQuery(headers[REQUEST_HEADER_NAME]); - expect(metrics).to.have.property('su'); - expect(metrics.su).to.equal(true); - }); + it('getHeaderParameters() recognizes playback rate change through events', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + const CHANGED_PLAYBACK_RATE = 2.4; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION + }; + let headers = cmcdModel.getHeaderParameters(request); + let metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.not.have.property('pr'); + + eventBus.trigger(MediaPlayerEvents.PLAYBACK_RATE_CHANGED, { playbackRate: CHANGED_PLAYBACK_RATE }); + + headers = cmcdModel.getHeaderParameters(request); + metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.have.property('pr'); + expect(metrics.pr).to.equal(CHANGED_PLAYBACK_RATE); + }); - it('getHeaderParameters() recognizes manifest load through events', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION - }; - let headers = cmcdModel.getHeaderParameters(request); - let metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.not.have.property('st'); - expect(metrics).to.not.have.property('sf'); - - eventBus.trigger(MediaPlayerEvents.MANIFEST_LOADED, {protocol: 'MSS', data: {type: DashConstants.DYNAMIC}}); - - headers = cmcdModel.getHeaderParameters(request); - metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.have.property('st'); - expect(metrics.st).to.equal('l'); - expect(metrics).to.have.property('sf'); - expect(metrics.sf).to.equal('s'); - }); + it('getHeaderParameters() recognizes playback seek through events', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION + }; + cmcdModel.getHeaderParameters(request); // first initial request will set startup to true + let headers = cmcdModel.getHeaderParameters(request); + let metrics = parseQuery(headers[STATUS_HEADER_NAME]); + expect(metrics).to.not.have.property('bs'); + metrics = parseQuery(headers[REQUEST_HEADER_NAME]); + expect(metrics).to.not.have.property('su'); + + eventBus.trigger(MediaPlayerEvents.PLAYBACK_SEEKED); + + headers = cmcdModel.getHeaderParameters(request); + metrics = parseQuery(headers[STATUS_HEADER_NAME]); + expect(metrics).to.have.property('bs'); + expect(metrics.bs).to.equal(true); + metrics = parseQuery(headers[REQUEST_HEADER_NAME]); + expect(metrics).to.have.property('su'); + expect(metrics.su).to.equal(true); + }); - it('getHeaderParameters() returns CID in metrics if explicitly set', function () { - const REQUEST_TYPE = HTTPRequest.MPD_TYPE; - const MEDIA_TYPE = 'video'; - const CID = 'content_id'; + it('getHeaderParameters() recognizes buffer starvation through events', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION + }; + cmcdModel.getHeaderParameters(request); // first initial request will set startup to true + let headers = cmcdModel.getHeaderParameters(request); + let metrics = parseQuery(headers[STATUS_HEADER_NAME]); + expect(metrics).to.not.have.property('bs'); + metrics = parseQuery(headers[REQUEST_HEADER_NAME]); + expect(metrics).to.not.have.property('su'); + + eventBus.trigger(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, { + state: MediaPlayerEvents.BUFFER_EMPTY, + mediaType: request.mediaType + }); + + headers = cmcdModel.getHeaderParameters(request); + metrics = parseQuery(headers[STATUS_HEADER_NAME]); + expect(metrics).to.have.property('bs'); + expect(metrics.bs).to.equal(true); + metrics = parseQuery(headers[REQUEST_HEADER_NAME]); + expect(metrics).to.have.property('su'); + expect(metrics.su).to.equal(true); + }); - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; + it('getHeaderParameters() recognizes manifest load through events', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION + }; + let headers = cmcdModel.getHeaderParameters(request); + let metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.not.have.property('st'); + expect(metrics).to.not.have.property('sf'); + + eventBus.trigger(MediaPlayerEvents.MANIFEST_LOADED, { + protocol: 'MSS', + data: { type: DashConstants.DYNAMIC } + }); + + headers = cmcdModel.getHeaderParameters(request); + metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.have.property('st'); + expect(metrics.st).to.equal('l'); + expect(metrics).to.have.property('sf'); + expect(metrics.sf).to.equal('s'); + }); - settings.update({streaming: {cmcd: {enabled: true, cid: CID}}}); + it('getHeaderParameters() returns CID in metrics if explicitly set', function () { + const REQUEST_TYPE = HTTPRequest.MPD_TYPE; + const MEDIA_TYPE = 'video'; + const CID = 'content_id'; - let headers = cmcdModel.getHeaderParameters(request); - expect(headers).to.have.property(SESSION_HEADER_NAME); - expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; - let metrics = parseQuery(headers[SESSION_HEADER_NAME]); - expect(metrics).to.have.property('cid'); - expect(metrics.cid).to.equal(CID); - }); + settings.update({ streaming: { cmcd: { enabled: true, cid: CID } } }); - it('getHeaderParameters() returns correct RTP value if set to static ', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; + let headers = cmcdModel.getHeaderParameters(request); + expect(headers).to.have.property(SESSION_HEADER_NAME); + expect(typeof headers[SESSION_HEADER_NAME]).to.equal('string'); - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; + let metrics = parseQuery(headers[SESSION_HEADER_NAME]); + expect(metrics).to.have.property('cid'); + expect(metrics.cid).to.equal(CID); + }); - settings.update({streaming: {cmcd: {enabled: true, rtp: 10000}}}); + it('getHeaderParameters() returns correct RTP value if set to static ', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; - let headers = cmcdModel.getHeaderParameters(request); - expect(headers).to.have.property(STATUS_HEADER_NAME); - expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; - let metrics = parseQuery(headers[STATUS_HEADER_NAME]); - expect(metrics).to.have.property('rtp'); - expect(metrics.rtp).to.equal(10000); - }); + settings.update({ streaming: { cmcd: { enabled: true, rtp: 10000 } } }); - it( 'getHeadersParameters() applies enabledKeys filter', function() { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; + let headers = cmcdModel.getHeaderParameters(request); + expect(headers).to.have.property(STATUS_HEADER_NAME); + expect(typeof headers[STATUS_HEADER_NAME]).to.equal('string'); - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; + let metrics = parseQuery(headers[STATUS_HEADER_NAME]); + expect(metrics).to.have.property('rtp'); + expect(metrics.rtp).to.equal(10000); + }); - settings.update({streaming: {cmcd: {enabledKeys: ['d', 'ot', 'tb', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'rtp' , 'pr', 'sf', 'sid', 'st', 'v'] }}}); - let headers = cmcdModel.getHeaderParameters(request); - expect(headers[OBJECT_HEADER_NAME].split(',').map( e => { return e.split('=')[0]})).to.not.include('br'); - expect(headers[REQUEST_HEADER_NAME].split(',').map( e => { return e.split('=')[0]})).to.not.include('bl'); - expect(headers[STATUS_HEADER_NAME].split(',').map( e => { return e.split('=')[0]})).to.not.include('bs'); - expect(headers[SESSION_HEADER_NAME].split(',').map( e => { return e.split('=')[0]})).to.not.include('cid'); + it('getHeadersParameters() applies enabledKeys filter', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + settings.update({ + streaming: { + cmcd: { + enabledKeys: ['ot', 'tb', 'mtp', 'nor', 'nrr', 'su', 'pr', 'sf', 'st', 'v'], + rtp: 1000 + } + } + }); + let headers = cmcdModel.getHeaderParameters(request); + expect(headers[OBJECT_HEADER_NAME].split(',').map(e => { + return e.split('=')[0] + })).to.not.include('d'); + expect(headers[REQUEST_HEADER_NAME].split(',').map(e => { + return e.split('=')[0] + })).to.not.include('dl'); + expect(headers[STATUS_HEADER_NAME].split(',').map(e => { + return e.split('=')[0] + })).to.not.include('rtp'); + expect(headers[SESSION_HEADER_NAME].split(',').map(e => { + return e.split('=')[0] + })).to.not.include('sid'); + }); - //revert settings - settings.update({streaming: {cmcd: {enabledKeys: ['br' , 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] }}}); - }); + it('getHeadersParameters() should return no parameters if enabled keys is empty', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + settings.update({ + streaming: { + cmcd: { + enabledKeys: [], + rtp: 1000 + } + } + }); + let headers = cmcdModel.getHeaderParameters(request); + expect(headers[OBJECT_HEADER_NAME]).to.be.empty; + expect(headers[REQUEST_HEADER_NAME]).to.be.empty; + expect(headers[STATUS_HEADER_NAME]).to.be.empty; + expect(headers[SESSION_HEADER_NAME]).to.be.empty; + }); + }) + + describe('getQueryParameter()', () => { + it('getQueryParameter() returns correct metrics for MPD', function () { + const REQUEST_TYPE = HTTPRequest.MPD_TYPE; + const MEDIA_TYPE = 'video'; + const MANIFEST_OBJECT_TYPE = 'm'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + let parameters = cmcdModel.getQueryParameter(request); + expect(parameters).to.have.property('key'); + expect(parameters.key).to.equal('CMCD'); + expect(parameters).to.have.property('value'); + expect(typeof parameters.value).to.equal('string'); + + let metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('sid'); + expect(metrics).to.not.have.property('cid'); + expect(metrics).to.have.property('ot'); + expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); + }); - it('getQueryParameter() returns correct metrics for MPD', function () { - const REQUEST_TYPE = HTTPRequest.MPD_TYPE; - const MEDIA_TYPE = 'video'; - const MANIFEST_OBJECT_TYPE = 'm'; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; - - let parameters = cmcdModel.getQueryParameter(request); - expect(parameters).to.have.property('key'); - expect(parameters.key).to.equal('CMCD'); - expect(parameters).to.have.property('value'); - expect(typeof parameters.value).to.equal('string'); - - let metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('sid'); - expect(metrics).to.not.have.property('cid'); - expect(metrics).to.have.property('ot'); - expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); - }); + it('getQueryParameter() returns correct metrics for init segments', function () { + const REQUEST_TYPE = HTTPRequest.INIT_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const MANIFEST_OBJECT_TYPE = 'i'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + let parameters = cmcdModel.getQueryParameter(request); + expect(parameters).to.have.property('key'); + expect(parameters.key).to.equal('CMCD'); + expect(parameters).to.have.property('value'); + expect(typeof parameters.value).to.equal('string'); + + let metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('sid'); + expect(metrics).to.not.have.property('cid'); + expect(metrics).to.have.property('ot'); + expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); + expect(metrics).to.have.property('su'); + expect(metrics.su).to.equal(true); + }); - it('getQueryParameter() returns correct metrics for init segments', function () { - const REQUEST_TYPE = HTTPRequest.INIT_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const MANIFEST_OBJECT_TYPE = 'i'; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; - - let parameters = cmcdModel.getQueryParameter(request); - expect(parameters).to.have.property('key'); - expect(parameters.key).to.equal('CMCD'); - expect(parameters).to.have.property('value'); - expect(typeof parameters.value).to.equal('string'); - - let metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('sid'); - expect(metrics).to.not.have.property('cid'); - expect(metrics).to.have.property('ot'); - expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); - expect(metrics).to.have.property('su'); - expect(metrics.su).to.equal(true); - }); + it('getQueryParameter() returns correct metrics for media segments', function () { + dashMetricsMock.setCurrentBufferLevel(15.34511); + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + const TOP_BITRATE = 20000; + const MEASURED_THROUGHPUT = 8327641; + const BUFFER_LEVEL = parseInt(dashMetricsMock.getCurrentBufferLevel() * 10) * 100; + const VIDEO_OBJECT_TYPE = 'v'; + const NEXT_OBJECT_URL = 'next_object'; + const NEXT_OBJECT_RANGE = '100-500'; + + abrControllerMock.setTopBitrateInfo({ bitrate: TOP_BITRATE }); + abrControllerMock.setThroughputHistory({ + getSafeAverageThroughput: function () { + return MEASURED_THROUGHPUT; + } + }); + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION, + url: 'http://test.url/firstRequest' + }; + + let parameters = cmcdModel.getQueryParameter(request); + expect(parameters).to.have.property('key'); + expect(parameters.key).to.equal('CMCD'); + expect(parameters).to.have.property('value'); + expect(typeof parameters.value).to.equal('string'); + + let metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('sid'); + expect(metrics).to.not.have.property('cid'); + expect(metrics).to.have.property('br'); + expect(metrics.br).to.equal(parseInt(BITRATE / 1000)); + expect(metrics).to.have.property('ot'); + expect(metrics.ot).to.equal(VIDEO_OBJECT_TYPE); + expect(metrics).to.have.property('d'); + expect(metrics.d).to.equal(parseInt(DURATION * 1000)); + expect(metrics).to.have.property('mtp'); + expect(metrics.mtp).to.equal(parseInt(MEASURED_THROUGHPUT / 100) * 100); + expect(metrics).to.have.property('dl'); + expect(metrics.dl).to.equal(BUFFER_LEVEL); + expect(metrics).to.have.property('bl'); + expect(metrics.bl).to.equal(BUFFER_LEVEL); + expect(metrics).to.have.property('tb'); + expect(metrics.tb).to.equal(parseInt(TOP_BITRATE / 1000)); + expect(metrics).to.have.property('nor'); + expect(metrics.nor).to.equal(NEXT_OBJECT_URL); + expect(metrics).to.have.property('rtp'); + expect(typeof metrics.rtp).to.equal('number'); + expect(metrics.rtp % 100).to.equal(0); + + request.url = 'http://test.url/next_object'; + parameters = cmcdModel.getQueryParameter(request); + metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('nrr'); + expect(metrics.nrr).to.equal(NEXT_OBJECT_RANGE); + }); - it('getQueryParameter() returns correct metrics for media segments', function () { - dashMetricsMock.setCurrentBufferLevel(15.34511); - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - const TOP_BITRATE = 20000; - const MEASURED_THROUGHPUT = 8327641; - const BUFFER_LEVEL = parseInt(dashMetricsMock.getCurrentBufferLevel() * 10) * 100; - const VIDEO_OBJECT_TYPE = 'v'; - const NEXT_OBJECT_URL = 'next_object'; - const NEXT_OBJECT_RANGE = '100-500'; - - abrControllerMock.setTopBitrateInfo({bitrate: TOP_BITRATE}); - abrControllerMock.setThroughputHistory({ - getSafeAverageThroughput: function () { - return MEASURED_THROUGHPUT; - } + it('getQueryParameter() returns correct metrics for other type', function () { + const REQUEST_TYPE = HTTPRequest.OTHER_TYPE; + const MEDIA_TYPE = 'video'; + const MANIFEST_OBJECT_TYPE = 'o'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + let parameters = cmcdModel.getQueryParameter(request); + expect(parameters).to.have.property('key'); + expect(parameters.key).to.equal('CMCD'); + expect(parameters).to.have.property('value'); + expect(typeof parameters.value).to.equal('string'); + + let metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('sid'); + expect(metrics).to.not.have.property('cid'); + expect(metrics).to.have.property('ot'); + expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); }); - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION, - url: 'http://test.url/firstRequest' - }; - - let parameters = cmcdModel.getQueryParameter(request); - expect(parameters).to.have.property('key'); - expect(parameters.key).to.equal('CMCD'); - expect(parameters).to.have.property('value'); - expect(typeof parameters.value).to.equal('string'); - - let metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('sid'); - expect(metrics).to.not.have.property('cid'); - expect(metrics).to.have.property('br'); - expect(metrics.br).to.equal(parseInt(BITRATE / 1000)); - expect(metrics).to.have.property('ot'); - expect(metrics.ot).to.equal(VIDEO_OBJECT_TYPE); - expect(metrics).to.have.property('d'); - expect(metrics.d).to.equal(parseInt(DURATION * 1000)); - expect(metrics).to.have.property('mtp'); - expect(metrics.mtp).to.equal(parseInt(MEASURED_THROUGHPUT / 100) * 100); - expect(metrics).to.have.property('dl'); - expect(metrics.dl).to.equal(BUFFER_LEVEL); - expect(metrics).to.have.property('bl'); - expect(metrics.bl).to.equal(BUFFER_LEVEL); - expect(metrics).to.have.property('tb'); - expect(metrics.tb).to.equal(parseInt(TOP_BITRATE / 1000)); - expect(metrics).to.have.property('nor'); - expect(metrics.nor).to.equal(NEXT_OBJECT_URL); - expect(metrics).to.have.property('rtp'); - expect(typeof metrics.rtp).to.equal('number'); - expect(metrics.rtp % 100).to.equal(0); - - request.url = 'http://test.url/next_object'; - parameters = cmcdModel.getQueryParameter(request); - metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('nrr'); - expect(metrics.nrr).to.equal(NEXT_OBJECT_RANGE); - }); - it('getQueryParameter() returns correct metrics for other type', function () { - const REQUEST_TYPE = HTTPRequest.OTHER_TYPE; - const MEDIA_TYPE = 'video'; - const MANIFEST_OBJECT_TYPE = 'o'; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; - - let parameters = cmcdModel.getQueryParameter(request); - expect(parameters).to.have.property('key'); - expect(parameters.key).to.equal('CMCD'); - expect(parameters).to.have.property('value'); - expect(typeof parameters.value).to.equal('string'); - - let metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('sid'); - expect(metrics).to.not.have.property('cid'); - expect(metrics).to.have.property('ot'); - expect(metrics.ot).to.equal(MANIFEST_OBJECT_TYPE); - }); + it('getQueryParameter() recognizes playback rate change through events', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + const CHANGED_PLAYBACK_RATE = 2.4; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION + }; + let parameters = cmcdModel.getQueryParameter(request); + let metrics = parseQuery(parameters.value); + expect(metrics).to.not.have.property('pr'); + + eventBus.trigger(MediaPlayerEvents.PLAYBACK_RATE_CHANGED, { playbackRate: CHANGED_PLAYBACK_RATE }); + + parameters = cmcdModel.getQueryParameter(request); + metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('pr'); + expect(metrics.pr).to.equal(CHANGED_PLAYBACK_RATE); + }); - it('getQueryParameter() recognizes playback rate change through events', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - const CHANGED_PLAYBACK_RATE = 2.4; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION - }; - let parameters = cmcdModel.getQueryParameter(request); - let metrics = parseQuery(parameters.value); - expect(metrics).to.not.have.property('pr'); - - eventBus.trigger(MediaPlayerEvents.PLAYBACK_RATE_CHANGED, {playbackRate: CHANGED_PLAYBACK_RATE}); - - parameters = cmcdModel.getQueryParameter(request); - metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('pr'); - expect(metrics.pr).to.equal(CHANGED_PLAYBACK_RATE); - }); + it('getQueryParameter() recognizes playback seek through events', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION + }; + cmcdModel.getQueryParameter(request); // first initial request will set startup to true + let parameters = cmcdModel.getQueryParameter(request); + let metrics = parseQuery(parameters.value); + expect(metrics).to.not.have.property('bs'); + expect(metrics).to.not.have.property('su'); + + eventBus.trigger(MediaPlayerEvents.PLAYBACK_SEEKED); + + parameters = cmcdModel.getQueryParameter(request); + metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('bs'); + expect(metrics.bs).to.equal(true); + expect(metrics).to.have.property('su'); + expect(metrics.su).to.equal(true); + }); - it('getQueryParameter() recognizes playback seek through events', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION - }; - cmcdModel.getQueryParameter(request); // first initial request will set startup to true - let parameters = cmcdModel.getQueryParameter(request); - let metrics = parseQuery(parameters.value); - expect(metrics).to.not.have.property('bs'); - expect(metrics).to.not.have.property('su'); - - eventBus.trigger(MediaPlayerEvents.PLAYBACK_SEEKED); - - parameters = cmcdModel.getQueryParameter(request); - metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('bs'); - expect(metrics.bs).to.equal(true); - expect(metrics).to.have.property('su'); - expect(metrics.su).to.equal(true); - }); + it('getQueryParameter() recognizes buffer starvation through events', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION + }; + cmcdModel.getQueryParameter(request); // first initial request will set startup to true + let parameters = cmcdModel.getQueryParameter(request); + let metrics = parseQuery(parameters.value); + expect(metrics).to.not.have.property('bs'); + expect(metrics).to.not.have.property('su'); + + eventBus.trigger(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, { + state: MediaPlayerEvents.BUFFER_EMPTY, + mediaType: request.mediaType + }); + + parameters = cmcdModel.getQueryParameter(request); + metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('bs'); + expect(metrics.bs).to.equal(true); + expect(metrics).to.have.property('su'); + expect(metrics.su).to.equal(true); + }); - it('getQueryParameter() recognizes buffer starvation through events', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION - }; - cmcdModel.getQueryParameter(request); // first initial request will set startup to true - let parameters = cmcdModel.getQueryParameter(request); - let metrics = parseQuery(parameters.value); - expect(metrics).to.not.have.property('bs'); - expect(metrics).to.not.have.property('su'); - - eventBus.trigger(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, { - state: MediaPlayerEvents.BUFFER_EMPTY, - mediaType: request.mediaType + it('getQueryParameter() recognizes manifest load through events', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + const BITRATE = 10000; + const DURATION = 987.213; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE, + quality: 0, + mediaInfo: { bitrateList: [{ bandwidth: BITRATE }] }, + duration: DURATION + }; + let parameters = cmcdModel.getQueryParameter(request); + let metrics = parseQuery(parameters.value); + expect(metrics).to.not.have.property('st'); + expect(metrics).to.not.have.property('sf'); + + eventBus.trigger(MediaPlayerEvents.MANIFEST_LOADED, { + protocol: 'MSS', + data: { type: DashConstants.DYNAMIC } + }); + + parameters = cmcdModel.getQueryParameter(request); + metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('st'); + expect(metrics.st).to.equal('l'); + expect(metrics).to.have.property('sf'); + expect(metrics.sf).to.equal('s'); }); - parameters = cmcdModel.getQueryParameter(request); - metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('bs'); - expect(metrics.bs).to.equal(true); - expect(metrics).to.have.property('su'); - expect(metrics.su).to.equal(true); - }); + it('getQueryParameter() returns CID in metrics if explicitly set', function () { + const REQUEST_TYPE = HTTPRequest.MPD_TYPE; + const MEDIA_TYPE = 'video'; + const CID = 'content_id'; - it('getQueryParameter() recognizes manifest load through events', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; - const BITRATE = 10000; - const DURATION = 987.213; - - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE, - quality: 0, - mediaInfo: {bitrateList: [{bandwidth: BITRATE}]}, - duration: DURATION - }; - let parameters = cmcdModel.getQueryParameter(request); - let metrics = parseQuery(parameters.value); - expect(metrics).to.not.have.property('st'); - expect(metrics).to.not.have.property('sf'); - - eventBus.trigger(MediaPlayerEvents.MANIFEST_LOADED, {protocol: 'MSS', data: {type: DashConstants.DYNAMIC}}); - - parameters = cmcdModel.getQueryParameter(request); - metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('st'); - expect(metrics.st).to.equal('l'); - expect(metrics).to.have.property('sf'); - expect(metrics.sf).to.equal('s'); - }); + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; - it('getQueryParameter() returns CID in metrics if explicitly set', function () { - const REQUEST_TYPE = HTTPRequest.MPD_TYPE; - const MEDIA_TYPE = 'video'; - const CID = 'content_id'; + settings.update({ streaming: { cmcd: { enabled: true, cid: CID } } }); - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; + let parameters = cmcdModel.getQueryParameter(request); + expect(parameters).to.have.property('key'); + expect(parameters.key).to.equal('CMCD'); + expect(parameters).to.have.property('value'); + expect(typeof parameters.value).to.equal('string'); - settings.update({streaming: {cmcd: {enabled: true, cid: CID}}}); + let metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('cid'); + expect(metrics.cid).to.equal(CID); + }); - let parameters = cmcdModel.getQueryParameter(request); - expect(parameters).to.have.property('key'); - expect(parameters.key).to.equal('CMCD'); - expect(parameters).to.have.property('value'); - expect(typeof parameters.value).to.equal('string'); + it('getQueryParameter() returns correct RTP value if set to static ', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; - let metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('cid'); - expect(metrics.cid).to.equal(CID); - }); + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; - it('getQueryParameter() returns correct RTP value if set to static ', function () { - const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; - const MEDIA_TYPE = 'video'; + settings.update({ streaming: { cmcd: { enabled: true, rtp: 10000 } } }); - let request = { - type: REQUEST_TYPE, - mediaType: MEDIA_TYPE - }; + let parameters = cmcdModel.getQueryParameter(request); + expect(parameters).to.have.property('key'); + expect(parameters.key).to.equal('CMCD'); + expect(parameters).to.have.property('value'); + expect(typeof parameters.value).to.equal('string'); - settings.update({streaming: {cmcd: {enabled: true, rtp: 10000}}}); + let metrics = parseQuery(parameters.value); + expect(metrics).to.have.property('rtp'); + expect(metrics.rtp).to.equal(10000); + }); - let parameters = cmcdModel.getQueryParameter(request); - expect(parameters).to.have.property('key'); - expect(parameters.key).to.equal('CMCD'); - expect(parameters).to.have.property('value'); - expect(typeof parameters.value).to.equal('string'); + it('getQueryParameter() applies enabledKeys filter', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + settings.update({ + streaming: { + cmcd: { + enabledKeys: ['br', 'ot', 'tb', 'bl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'cid', 'pr', 'sf', 'st', 'v'], + rtp: 1000 + } + } + }); + + let parameters = cmcdModel.getQueryParameter(request); + expect(parameters).to.have.property('key'); + expect(parameters.key).to.equal('CMCD'); + expect(parameters).to.have.property('value'); + expect(typeof parameters.value).to.equal('string'); + + let metrics = parseQuery(parameters.value); + expect(metrics).to.not.have.property('d'); + expect(metrics).to.not.have.property('dl'); + expect(metrics).to.not.have.property('rtp'); + expect(metrics).to.not.have.property('sid'); + }); + + it('getQueryParameter() should return no parameters if enabled keys is empty', function () { + const REQUEST_TYPE = HTTPRequest.MEDIA_SEGMENT_TYPE; + const MEDIA_TYPE = 'video'; + + let request = { + type: REQUEST_TYPE, + mediaType: MEDIA_TYPE + }; + + settings.update({ + streaming: { + cmcd: { + enabledKeys: [], + rtp: 1000 + } + } + }); + + let parameters = cmcdModel.getQueryParameter(request); + expect(parameters).to.have.property('key'); + expect(parameters.key).to.equal('CMCD'); + expect(parameters).to.have.property('value'); + expect(typeof parameters.value).to.equal('string'); + + let metrics = parseQuery(parameters.value); + expect(metrics).to.be.empty + }); + }) - let metrics = parseQuery(parameters.value); - expect(metrics).to.have.property('rtp'); - expect(metrics.rtp).to.equal(10000); - }); }); }); function parseQuery(query) { query = decodeURIComponent(query); let keyValues = query.split(','); + if (keyValues.length === 1 && keyValues[0] === '') { + return {}; + } return keyValues.map(keyValue => keyValue.indexOf('=') === -1 ? [keyValue, true] : keyValue.split('=')) .map(keyValue => isNumber(keyValue[1]) ? [keyValue[0], Number(keyValue[1])] : keyValue) .map(keyValue => isString(keyValue[1]) && keyValue[1].indexOf('"') !== -1 ? [keyValue[0], keyValue[1].replace(/"/g, '')] : keyValue) From 98624fac56971939b976514e19db8117b283123f Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Mon, 11 Apr 2022 13:32:15 +0200 Subject: [PATCH 11/11] Add enabledKeys to CMCD demo --- samples/advanced/cmcd.html | 1 + samples/dash-if-reference-player/app/main.js | 36 ++++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index c0dae1404a..905211b0fa 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -50,6 +50,7 @@ sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ cid: '21cf726cfe3d937b5f974f72bb5bd06a', /* content id send with each request */ mode: CMCD_MODE_QUERY, + enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] } } }); diff --git a/samples/dash-if-reference-player/app/main.js b/samples/dash-if-reference-player/app/main.js index 8e818c57bb..7c921b9b9a 100644 --- a/samples/dash-if-reference-player/app/main.js +++ b/samples/dash-if-reference-player/app/main.js @@ -786,13 +786,13 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' }); } - $scope._getFormatedCmcdEnabledKeys = function() { + $scope._getFormatedCmcdEnabledKeys = function () { let formatedKeys; - if(!Array.isArray($scope.cmcdEnabledKeys)) { - let cmcdEnabledKeys = $scope.cmcdEnabledKeys.split(','); + if (!Array.isArray($scope.cmcdEnabledKeys)) { + let cmcdEnabledKeys = $scope.cmcdEnabledKeys.split(','); formatedKeys = $scope.cmcdAllKeys.map(key => { let mappedKey = key; - if(!cmcdEnabledKeys.includes(key)) mappedKey = ""; + if (!cmcdEnabledKeys.includes(key)) mappedKey = ''; return mappedKey; }); @@ -805,7 +805,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' $scope.updateCmcdEnabledKeys = function () { let cmcdEnabledKeys = $scope._getFormatedCmcdEnabledKeys(); - + $scope.player.updateSettings({ streaming: { cmcd: { @@ -1472,18 +1472,18 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' if (settings.hasOwnProperty(setting)) { var k = prefix ? prefix + '.' + setting : setting, v = settings[setting]; - urlString.push((v != null && typeof v === "object") ? + urlString.push((v != null && typeof v === 'object') ? this.toQueryString(v, k) : - encodeURIComponent(decodeURIComponent(k)) + "=" + encodeURIComponent(decodeURIComponent(v))); + encodeURIComponent(decodeURIComponent(k)) + '=' + encodeURIComponent(decodeURIComponent(v))); } } // Make the string, then remove all cases of && caused by empty settings - return urlString.join("&").split('&&').join('&'); + return urlString.join('&').split('&&').join('&'); } /** Resolve nested query parameters */ $scope.resolveQueryNesting = function (base, nestedKey, value) { - var keyList = nestedKey.split("."); + var keyList = nestedKey.split('.'); var lastProperty = value !== null ? keyList.pop() : false; var obj = base; @@ -1505,7 +1505,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' $scope.toSettingsObject = function (queryString) { //Remove double & in case of empty settings field var querySegments = queryString.split('&&').join('&'); - querySegments = queryString.split("&"); + querySegments = queryString.split('&'); var settingsObject = {}; var drmObject = {}; var prioritiesEnabled = false; @@ -1513,7 +1513,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' var i = 1; for (var segment in querySegments) { - [key, value] = querySegments[segment].split("="); + [key, value] = querySegments[segment].split('='); value = decodeURIComponent(value); $scope.resolveQueryNesting(settingsObject, key, value); @@ -1576,22 +1576,22 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' queryProtectionData[drmObject[drm].drmKeySystem]['httpRequestHeaders'] = drmObject[drm].httpRequestHeaders; } } else { - alert("Kid and Key must be specified!"); + alert('Kid and Key must be specified!'); } } else { //check if priority is enabled if (prioritiesEnabled) { queryProtectionData[drmObject[drm].drmKeySystem] = { - "serverURL": decodeURIComponent(drmObject[drm].licenseServerUrl), - "priority": parseInt(drmObject[drm].priority) + 'serverURL': decodeURIComponent(drmObject[drm].licenseServerUrl), + 'priority': parseInt(drmObject[drm].priority) } if (drmObject[drm].httpRequestHeaders !== {}) queryProtectionData[drmObject[drm].drmKeySystem]['httpRequestHeaders'] = drmObject[drm].httpRequestHeaders; } else { queryProtectionData[drmObject[drm].drmKeySystem] = { - "serverURL": decodeURIComponent(drmObject[drm].licenseServerUrl), + 'serverURL': decodeURIComponent(drmObject[drm].licenseServerUrl), } } @@ -1681,7 +1681,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' } $scope.parseBoolean = function (value) { - return value === true || value === "true"; + return value === true || value === 'true'; } /** Takes a string value extracted from the query-string and transforms it into the appropriate type */ @@ -1709,7 +1709,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' //Alt2: If IE detected, copy settings-file content instead of creating a url, alert userto the change. } if (string.length > maxUrlLength) { - alert("The length of the URL may exceed the Browser url character limit.") + alert('The length of the URL may exceed the Browser url character limit.') } } @@ -2066,7 +2066,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' $scope.cmcdMode = currentConfig.streaming.cmcd.mode; - if(currentConfig.streaming.cmcd.enabledKeys){ + if (currentConfig.streaming.cmcd.enabledKeys) { $scope.cmcdEnabledKeys = currentConfig.streaming.cmcd.enabledKeys; } }