From ee0219bfbca8eda3a5cf4b5f42c407411e4eaf5c Mon Sep 17 00:00:00 2001 From: sannti97 Date: Thu, 15 Aug 2024 19:04:01 -0300 Subject: [PATCH 01/82] Added new version to streaming cmcd settings WIP: version 2 sends cmcd data to the remote server only for both modes --- samples/advanced/cmcd.html | 1 + src/core/Settings.js | 1 + src/streaming/net/HTTPLoader.js | 42 +++++++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 905211b0fa..0355e7fec9 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -47,6 +47,7 @@ streaming: { cmcd: { enabled: true, /* enable reporting of cmcd parameters */ + version: 2, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ cid: '21cf726cfe3d937b5f974f72bb5bd06a', /* content id send with each request */ mode: CMCD_MODE_QUERY, diff --git a/src/core/Settings.js b/src/core/Settings.js index 1a755c19f0..769cbf6f19 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1293,6 +1293,7 @@ function Settings() { cmcd: { applyParametersFromMpd: true, enabled: false, + version: 1, sid: null, cid: null, rtp: null, diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 056ce14e53..cb18032230 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -228,6 +228,31 @@ function HTTPLoader(cfg) { }; const _onRequestEnd = function (aborted = false) { + // TODO: Add url as a streaming cmcd config + var url = 'http://localhost:3000/server'; + // TODO: Refactor headers mode + var headers = { + 'Content-Type': 'application/json' + }; + const cmcdVersion = settings.get().streaming.cmcd.version ? settings.get().streaming.cmcd.version : 1; + // Get the CMCD data from the request + if (cmcdVersion === 2){ + if (httpRequest.customData.request.cmcdMode === Constants.CMCD_MODE_QUERY){ + url = Utils.addAditionalQueryParameterToUrl(url, httpRequest.customData.request.cmcdQueryParams); + } else if (httpRequest.customData.request.cmcdMode === Constants.CMCD_MODE_HEADER){ + headers = httpRequest.customData.request.cmcdHeaders; + } + + fetch(url, { + method: 'POST', + headers: headers, + }).then(response => { + console.log('CMCD data sent successfully:', response); + }).catch(error => { + console.error('Error sending CMCD data:', error); + }); + } + // Remove the request from our list of requests if (httpRequests.indexOf(httpRequest) !== -1) { httpRequests.splice(httpRequests.indexOf(httpRequest), 1); @@ -603,14 +628,27 @@ function HTTPLoader(cfg) { const currentAdaptationSetId = request?.mediaInfo?.id?.toString(); const isIncludedFilters = clientDataReportingController.isServiceLocationIncluded(request.type, currentServiceLocation) && clientDataReportingController.isAdaptationsIncluded(currentAdaptationSetId); + // TODO: Check if we can get this from cmcdParameters + const cmcdVersion = settings.get().streaming.cmcd.version || 1; if (isIncludedFilters && cmcdModel.isCmcdEnabled()) { const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; + request.customData = request.customData || {}; if (cmcdMode === Constants.CMCD_MODE_QUERY) { const additionalQueryParameter = _getAdditionalQueryParameter(request); - request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); + if (cmcdVersion === 1){ + request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); + } else if (cmcdVersion === 2){ + request.cmcdQueryParams = additionalQueryParameter; + request.cmcdMode = Constants.CMCD_MODE_QUERY; + } } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { - request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); + if (cmcdVersion === 1){ + request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); + } else if (cmcdVersion === 2){ + request.cmcdHeaders = cmcdModel.getHeaderParameters(request); + request.cmcdMode = Constants.CMCD_MODE_HEADER; + } } } } From b7093f0d2300b568104c83377e5c56ff883f1c24 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Sat, 17 Aug 2024 20:32:38 -0300 Subject: [PATCH 02/82] Added reporting settings and improved _onRequestEnd --- docs/cmcd-v2/reporting-modes.md | 10 ++++++ samples/advanced/cmcd.html | 5 +++ src/core/Settings.js | 8 ++++- src/streaming/net/HTTPLoader.js | 59 ++++++++++++++++++--------------- 4 files changed, 54 insertions(+), 28 deletions(-) create mode 100644 docs/cmcd-v2/reporting-modes.md diff --git a/docs/cmcd-v2/reporting-modes.md b/docs/cmcd-v2/reporting-modes.md new file mode 100644 index 0000000000..9cb8de225f --- /dev/null +++ b/docs/cmcd-v2/reporting-modes.md @@ -0,0 +1,10 @@ +## Reporting modes + +Here are notes about the challenge of implementing the new reporting modes of CMCD-v2. + +### Response Mode + +- Modified the `_updateRequestUrlAndHeadersWithCMCD` function to save the cmcdHeaders or `cmcdParams` in `customData` so they can be sent to the remote server in the `_onRequestEnd` function. + +- Observations: + - The mode that already exists in the configuration for the "transmition mode" might be confusing with the new mode for the "reporting mode". \ No newline at end of file diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 0355e7fec9..4ca4aad626 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -48,6 +48,11 @@ cmcd: { enabled: true, /* enable reporting of cmcd parameters */ version: 2, + reporting: { + mode: 2, + requestMethod: 'POST', + requestUrl: "http://localhost:3000/test_server" + }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ cid: '21cf726cfe3d937b5f974f72bb5bd06a', /* content id send with each request */ mode: CMCD_MODE_QUERY, diff --git a/src/core/Settings.js b/src/core/Settings.js index 769cbf6f19..760b92981c 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1300,7 +1300,13 @@ function Settings() { rtpSafetyFactor: 5, mode: Constants.CMCD_MODE_QUERY, enabledKeys: Constants.CMCD_AVAILABLE_KEYS, - includeInRequests: ['segment'] + includeInRequests: ['segment'], + reporting: { + mode: 1, + requestUrl: 'http://localhost:3000/cmcd_server', + requestMethod: 'POST', + requestHeaders: {} + } }, cmsd: { enabled: false, diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index cb18032230..fdf6dfc92e 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -228,24 +228,16 @@ function HTTPLoader(cfg) { }; const _onRequestEnd = function (aborted = false) { - // TODO: Add url as a streaming cmcd config - var url = 'http://localhost:3000/server'; - // TODO: Refactor headers mode - var headers = { - 'Content-Type': 'application/json' - }; - const cmcdVersion = settings.get().streaming.cmcd.version ? settings.get().streaming.cmcd.version : 1; - // Get the CMCD data from the request - if (cmcdVersion === 2){ - if (httpRequest.customData.request.cmcdMode === Constants.CMCD_MODE_QUERY){ - url = Utils.addAditionalQueryParameterToUrl(url, httpRequest.customData.request.cmcdQueryParams); - } else if (httpRequest.customData.request.cmcdMode === Constants.CMCD_MODE_HEADER){ - headers = httpRequest.customData.request.cmcdHeaders; - } - - fetch(url, { - method: 'POST', - headers: headers, + const cmcdVersion = httpRequest.customData.request.cmcdVersion; + const cmcdReportingMode = httpRequest.customData.request.cmcdReportingMode; + if (cmcdVersion === 2 && cmcdReportingMode === 2){ + const requestUrl = httpRequest.customData.request.cmcdReportingUrl; + const requestMethod = httpRequest.customData.request.cmcdReportingMethod; + const requestHeaders = httpRequest.customData.request.cmcdHeaders; + + fetch(requestUrl, { + method: requestMethod, + headers: requestHeaders, }).then(response => { console.log('CMCD data sent successfully:', response); }).catch(error => { @@ -628,26 +620,39 @@ function HTTPLoader(cfg) { const currentAdaptationSetId = request?.mediaInfo?.id?.toString(); const isIncludedFilters = clientDataReportingController.isServiceLocationIncluded(request.type, currentServiceLocation) && clientDataReportingController.isAdaptationsIncluded(currentAdaptationSetId); - // TODO: Check if we can get this from cmcdParameters - const cmcdVersion = settings.get().streaming.cmcd.version || 1; if (isIncludedFilters && cmcdModel.isCmcdEnabled()) { const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); + const cmcdVersion = cmcdParameters.version ? cmcdParameters.version : settings.get().streaming.cmcd.version; const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; - request.customData = request.customData || {}; + const cmcdReportingMode = settings.get().streaming.cmcd.reporting.mode; + const cmcdReportingMethod = settings.get().streaming.cmcd.reporting.method; + const cmcdReportingUrl = settings.get().streaming.cmcd.reporting.requestUrl; + // Set common cmcd params + if (cmcdVersion == 2 && cmcdReportingMode == 2){ + Object.assign(request, { + cmcdVersion, + cmcdMode, + cmcdReportingMode, + cmcdReportingMethod, + cmcdReportingUrl + }); + } if (cmcdMode === Constants.CMCD_MODE_QUERY) { const additionalQueryParameter = _getAdditionalQueryParameter(request); - if (cmcdVersion === 1){ + if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode === 1)){ request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); } else if (cmcdVersion === 2){ - request.cmcdQueryParams = additionalQueryParameter; - request.cmcdMode = Constants.CMCD_MODE_QUERY; + const reportingUrl = settings.get().streaming.cmcd.reporting.requestUrl; + request.cmcdReportingUrl = Utils.addAditionalQueryParameterToUrl(reportingUrl, additionalQueryParameter); } } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { - if (cmcdVersion === 1){ + if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode === 1)){ request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); } else if (cmcdVersion === 2){ - request.cmcdHeaders = cmcdModel.getHeaderParameters(request); - request.cmcdMode = Constants.CMCD_MODE_HEADER; + // Group custom and cmcd data headers + const reportingHeaders = settings.get().streaming.cmcd.reporting.requestHeaders; + const cmcdHeaders = cmcdModel.getHeaderParameters(request); + request.cmcdHeaders = { ...reportingHeaders, ...cmcdHeaders}; } } } From d8a4e092c5c03089641de2265b557a508654c91d Mon Sep 17 00:00:00 2001 From: sannti97 Date: Sat, 17 Aug 2024 20:41:36 -0300 Subject: [PATCH 03/82] Added handlers to _updateRequestUrlAndHeadersWithCMCD --- src/streaming/net/HTTPLoader.js | 48 ++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index fdf6dfc92e..a644b73f72 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -638,26 +638,44 @@ function HTTPLoader(cfg) { }); } if (cmcdMode === Constants.CMCD_MODE_QUERY) { - const additionalQueryParameter = _getAdditionalQueryParameter(request); - if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode === 1)){ - request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); - } else if (cmcdVersion === 2){ - const reportingUrl = settings.get().streaming.cmcd.reporting.requestUrl; - request.cmcdReportingUrl = Utils.addAditionalQueryParameterToUrl(reportingUrl, additionalQueryParameter); - } + _handleCmcdQueryMode(request, cmcdVersion, cmcdReportingMode); } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { - if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode === 1)){ - request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); - } else if (cmcdVersion === 2){ - // Group custom and cmcd data headers - const reportingHeaders = settings.get().streaming.cmcd.reporting.requestHeaders; - const cmcdHeaders = cmcdModel.getHeaderParameters(request); - request.cmcdHeaders = { ...reportingHeaders, ...cmcdHeaders}; - } + _handleCmcdHeadersMode(request, cmcdVersion, cmcdReportingMode); } } } + /** + * Handles the CMCD Query mode based on the CMCD version + * @param {object} request + * @private + */ + function _handleCmcdQueryMode(request, cmcdVersion, cmcdReportingMode) { + const additionalQueryParameter = _getAdditionalQueryParameter(request); + + if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode === 1)) { + request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); + } else if (cmcdVersion === 2 && cmcdReportingMode === 2) { + const reportingUrl = settings.get().streaming.cmcd.reporting.requestUrl; + request.cmcdReportingUrl = Utils.addAditionalQueryParameterToUrl(reportingUrl, additionalQueryParameter); + } + } + + /** + * Handles the CMCD Headers mode based on the CMCD version + * @param {object} request + * @private + */ + function _handleCmcdHeadersMode(request, cmcdVersion, cmcdReportingMode) { + const cmcdHeaders = cmcdModel.getHeaderParameters(request); + if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode === 1)) { + request.headers = Object.assign(request.headers, cmcdHeaders); + } else if (cmcdVersion === 2 && cmcdReportingMode === 2) { + const reportingHeaders = settings.get().streaming.cmcd.reporting.requestHeaders; + request.cmcdHeaders = { ...reportingHeaders, ...cmcdHeaders }; + } + } + /** * Generates the additional query parameters to be appended to the request url * @param {object} request From f905c98d809bcccf5eac4b4d2c696dba4eac1bdc Mon Sep 17 00:00:00 2001 From: sannti97 Date: Sat, 17 Aug 2024 20:57:46 -0300 Subject: [PATCH 04/82] Changed reporting mode to a list --- samples/advanced/cmcd.html | 2 +- src/core/Settings.js | 2 +- src/streaming/net/HTTPLoader.js | 14 ++++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 4ca4aad626..9630d0eeb1 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -49,7 +49,7 @@ enabled: true, /* enable reporting of cmcd parameters */ version: 2, reporting: { - mode: 2, + mode: [2], requestMethod: 'POST', requestUrl: "http://localhost:3000/test_server" }, diff --git a/src/core/Settings.js b/src/core/Settings.js index 760b92981c..94df6680e2 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1302,7 +1302,7 @@ function Settings() { enabledKeys: Constants.CMCD_AVAILABLE_KEYS, includeInRequests: ['segment'], reporting: { - mode: 1, + mode: [1], requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'POST', requestHeaders: {} diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index a644b73f72..e1564160b8 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -230,7 +230,7 @@ function HTTPLoader(cfg) { const _onRequestEnd = function (aborted = false) { const cmcdVersion = httpRequest.customData.request.cmcdVersion; const cmcdReportingMode = httpRequest.customData.request.cmcdReportingMode; - if (cmcdVersion === 2 && cmcdReportingMode === 2){ + if (cmcdVersion === 2 && cmcdReportingMode.includes(2)){ const requestUrl = httpRequest.customData.request.cmcdReportingUrl; const requestMethod = httpRequest.customData.request.cmcdReportingMethod; const requestHeaders = httpRequest.customData.request.cmcdHeaders; @@ -628,7 +628,7 @@ function HTTPLoader(cfg) { const cmcdReportingMethod = settings.get().streaming.cmcd.reporting.method; const cmcdReportingUrl = settings.get().streaming.cmcd.reporting.requestUrl; // Set common cmcd params - if (cmcdVersion == 2 && cmcdReportingMode == 2){ + if (cmcdVersion == 2 && cmcdReportingMode.includes(2)){ Object.assign(request, { cmcdVersion, cmcdMode, @@ -653,9 +653,10 @@ function HTTPLoader(cfg) { function _handleCmcdQueryMode(request, cmcdVersion, cmcdReportingMode) { const additionalQueryParameter = _getAdditionalQueryParameter(request); - if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode === 1)) { + if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode.includes(1))) { request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); - } else if (cmcdVersion === 2 && cmcdReportingMode === 2) { + } + if (cmcdVersion === 2 && cmcdReportingMode.includes(2)) { const reportingUrl = settings.get().streaming.cmcd.reporting.requestUrl; request.cmcdReportingUrl = Utils.addAditionalQueryParameterToUrl(reportingUrl, additionalQueryParameter); } @@ -668,9 +669,10 @@ function HTTPLoader(cfg) { */ function _handleCmcdHeadersMode(request, cmcdVersion, cmcdReportingMode) { const cmcdHeaders = cmcdModel.getHeaderParameters(request); - if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode === 1)) { + if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode.includes(1))) { request.headers = Object.assign(request.headers, cmcdHeaders); - } else if (cmcdVersion === 2 && cmcdReportingMode === 2) { + } + if (cmcdVersion === 2 && cmcdReportingMode.includes(2)) { const reportingHeaders = settings.get().streaming.cmcd.reporting.requestHeaders; request.cmcdHeaders = { ...reportingHeaders, ...cmcdHeaders }; } From 75d7b36512cf8c6624e4b5fc79b9a77c819ff4b8 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Mon, 19 Aug 2024 19:11:49 -0300 Subject: [PATCH 05/82] WIP: Making each mode configurable and removed cmcd version --- samples/advanced/cmcd.html | 20 +++-- samples/dash-if-reference-player/app/main.js | 3 +- src/core/Settings.js | 18 ++-- src/streaming/net/HTTPLoader.js | 88 ++++++++----------- .../controllers/ProtectionController.js | 5 +- 5 files changed, 66 insertions(+), 68 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 9630d0eeb1..0bfb9d7b34 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -47,15 +47,22 @@ streaming: { cmcd: { enabled: true, /* enable reporting of cmcd parameters */ - version: 2, reporting: { - mode: [2], - requestMethod: 'POST', - requestUrl: "http://localhost:3000/test_server" + enabledModes: [1,2], + requestMode: { + enabled: true, + mode: CMCD_MODE_QUERY, + }, + responseMode: { + enabled: false, + mode: CMCD_MODE_HEADER, + requestUrl: 'http://localhost:3000/cmcd_server', + requestMethod: 'GET', + requestHeaders: {} + } }, 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'] } } @@ -68,7 +75,8 @@ function handleCmcdDataGeneratedEvent(event) { log('type: ' + event.mediaType); log('file: ' + event.url.split('/').pop()) - var mode = player.getSettings().streaming.cmcd.mode; + // TODO: Add support for all modes + var mode = player.getSettings().streaming.cmcd.reporting.requestMode.mode; var data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); var keys = Object.keys(data); keys = keys.sort(); diff --git a/samples/dash-if-reference-player/app/main.js b/samples/dash-if-reference-player/app/main.js index d192ef680f..0abdb56a69 100644 --- a/samples/dash-if-reference-player/app/main.js +++ b/samples/dash-if-reference-player/app/main.js @@ -2394,7 +2394,8 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' $scope.cmcdRtpSafetyFactor = currentConfig.streaming.cmcd.rtpSafetyFactor; } - $scope.cmcdMode = currentConfig.streaming.cmcd.mode; + // TODO: Add support for all modes + $scope.cmcdMode = currentConfig.streaming.cmcd.reporting.responseMode.mode; if (currentConfig.streaming.cmcd.enabledKeys) { $scope.cmcdEnabledKeys = currentConfig.streaming.cmcd.enabledKeys; diff --git a/src/core/Settings.js b/src/core/Settings.js index 94df6680e2..0d1242da58 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1293,19 +1293,25 @@ function Settings() { cmcd: { applyParametersFromMpd: true, enabled: false, - version: 1, sid: null, cid: null, rtp: null, rtpSafetyFactor: 5, - mode: Constants.CMCD_MODE_QUERY, enabledKeys: Constants.CMCD_AVAILABLE_KEYS, includeInRequests: ['segment'], reporting: { - mode: [1], - requestUrl: 'http://localhost:3000/cmcd_server', - requestMethod: 'POST', - requestHeaders: {} + enabledModes: [1], + requestMode: { + enabled: true, + mode: Constants.CMCD_MODE_QUERY, + }, + responseMode: { + enabled: false, + mode: Constants.CMCD_MODE_QUERY, + requestUrl: 'http://localhost:3000/cmcd_server', + requestMethod: 'POST', + requestHeaders: {} + } } }, cmsd: { diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index e1564160b8..0869a99ad7 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -228,12 +228,12 @@ function HTTPLoader(cfg) { }; const _onRequestEnd = function (aborted = false) { - const cmcdVersion = httpRequest.customData.request.cmcdVersion; - const cmcdReportingMode = httpRequest.customData.request.cmcdReportingMode; - if (cmcdVersion === 2 && cmcdReportingMode.includes(2)){ - const requestUrl = httpRequest.customData.request.cmcdReportingUrl; - const requestMethod = httpRequest.customData.request.cmcdReportingMethod; - const requestHeaders = httpRequest.customData.request.cmcdHeaders; + const cmcdReportingModes = httpRequest.customData.request.cmcdReportingModes; + + if (cmcdReportingModes.includes(2)){ + const requestUrl = httpRequest.customData.request.cmcdMode2Url; + const requestMethod = httpRequest.customData.request.cmcdMode2Method; + const requestHeaders = httpRequest.customData.request.cmcdMode2Headers; fetch(requestUrl, { method: requestMethod, @@ -623,58 +623,40 @@ function HTTPLoader(cfg) { if (isIncludedFilters && cmcdModel.isCmcdEnabled()) { const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); const cmcdVersion = cmcdParameters.version ? cmcdParameters.version : settings.get().streaming.cmcd.version; - const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; - const cmcdReportingMode = settings.get().streaming.cmcd.reporting.mode; - const cmcdReportingMethod = settings.get().streaming.cmcd.reporting.method; - const cmcdReportingUrl = settings.get().streaming.cmcd.reporting.requestUrl; + const cmcdReportingModes = settings.get().streaming.cmcd.reporting.enabledModes; // Set common cmcd params - if (cmcdVersion == 2 && cmcdReportingMode.includes(2)){ + request.cmcdReportingModes = cmcdReportingModes; + + if (cmcdReportingModes.includes(1)){ + const cmcdMode1RequestMode = settings.get().streaming.cmcd.reporting.requestMode.mode; + if (cmcdMode1RequestMode === Constants.CMCD_MODE_QUERY) { + const additionalQueryParameter = _getAdditionalQueryParameter(request); + request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); + } else if (cmcdMode1RequestMode === Constants.CMCD_MODE_HEADER) { + request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); + } + + } + + if (cmcdReportingModes.includes(2)){ + const cmcdMode2Url = settings.get().streaming.cmcd.reporting.responseMode.requestUrl; + const cmcdMode2Method = settings.get().streaming.cmcd.reporting.responseMode.requestMethod; + const cmcdMode2RequestMode = settings.get().streaming.cmcd.reporting.responseMode.mode; Object.assign(request, { cmcdVersion, - cmcdMode, - cmcdReportingMode, - cmcdReportingMethod, - cmcdReportingUrl + cmcdMode2RequestMode, + cmcdMode2Method, + cmcdMode2Url }); + if (cmcdMode2RequestMode === Constants.CMCD_MODE_QUERY){ + const additionalQueryParameter = _getAdditionalQueryParameter(request); + request.cmcdMode2Url = Utils.addAditionalQueryParameterToUrl(cmcdMode2Url, additionalQueryParameter); + } else if (cmcdMode2RequestMode === Constants.CMCD_MODE_HEADER){ + const cmcdHeaders = cmcdModel.getHeaderParameters(request); + const reportingHeaders = settings.get().streaming.cmcd.reporting.responseMode.requestHeaders; + request.cmcdMode2Headers = { ...reportingHeaders, ...cmcdHeaders}; + } } - if (cmcdMode === Constants.CMCD_MODE_QUERY) { - _handleCmcdQueryMode(request, cmcdVersion, cmcdReportingMode); - } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { - _handleCmcdHeadersMode(request, cmcdVersion, cmcdReportingMode); - } - } - } - - /** - * Handles the CMCD Query mode based on the CMCD version - * @param {object} request - * @private - */ - function _handleCmcdQueryMode(request, cmcdVersion, cmcdReportingMode) { - const additionalQueryParameter = _getAdditionalQueryParameter(request); - - if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode.includes(1))) { - request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); - } - if (cmcdVersion === 2 && cmcdReportingMode.includes(2)) { - const reportingUrl = settings.get().streaming.cmcd.reporting.requestUrl; - request.cmcdReportingUrl = Utils.addAditionalQueryParameterToUrl(reportingUrl, additionalQueryParameter); - } - } - - /** - * Handles the CMCD Headers mode based on the CMCD version - * @param {object} request - * @private - */ - function _handleCmcdHeadersMode(request, cmcdVersion, cmcdReportingMode) { - const cmcdHeaders = cmcdModel.getHeaderParameters(request); - if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdReportingMode.includes(1))) { - request.headers = Object.assign(request.headers, cmcdHeaders); - } - if (cmcdVersion === 2 && cmcdReportingMode.includes(2)) { - const reportingHeaders = settings.get().streaming.cmcd.reporting.requestHeaders; - request.cmcdHeaders = { ...reportingHeaders, ...cmcdHeaders }; } } diff --git a/src/streaming/protection/controllers/ProtectionController.js b/src/streaming/protection/controllers/ProtectionController.js index 8c2213e9b3..f330616ec4 100644 --- a/src/streaming/protection/controllers/ProtectionController.js +++ b/src/streaming/protection/controllers/ProtectionController.js @@ -822,7 +822,8 @@ function ProtectionController(config) { const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); if (cmcdModel.isCmcdEnabled()) { - const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; + // TODO: Add support for all modes + const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.reporting.responseMode.mode; if (cmcdMode === Constants.CMCD_MODE_QUERY) { const cmcdParams = cmcdModel.getQueryParameter({ url: request.url, @@ -846,7 +847,7 @@ function ProtectionController(config) { } if (cmcdModel.isCmcdEnabled()) { - const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; + const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.reporting.requestMode.mode; if (cmcdMode === Constants.CMCD_MODE_HEADER) { const cmcdHeaders = cmcdModel.getHeaderParameters({ url: request.url, From cadfc046a2b5a8e2fdf9427e1b686b5bd7a15ade Mon Sep 17 00:00:00 2001 From: sannti97 Date: Tue, 20 Aug 2024 16:37:28 -0300 Subject: [PATCH 06/82] Added mode into each reporting mode --- samples/advanced/cmcd.html | 34 +++++++----- src/core/Settings.js | 2 +- src/streaming/net/HTTPLoader.js | 53 +++++++------------ .../controllers/ProtectionController.js | 3 +- 4 files changed, 41 insertions(+), 51 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 0bfb9d7b34..3b20bc19cd 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -48,13 +48,12 @@ cmcd: { enabled: true, /* enable reporting of cmcd parameters */ reporting: { - enabledModes: [1,2], requestMode: { enabled: true, mode: CMCD_MODE_QUERY, }, responseMode: { - enabled: false, + enabled: true, mode: CMCD_MODE_HEADER, requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'GET', @@ -72,18 +71,27 @@ player.attachSource(url); } + // TODO: Needs to be commented to make both modes work function handleCmcdDataGeneratedEvent(event) { - log('type: ' + event.mediaType); - log('file: ' + event.url.split('/').pop()) - // TODO: Add support for all modes - var mode = player.getSettings().streaming.cmcd.reporting.requestMode.mode; - var data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); - var keys = Object.keys(data); - keys = keys.sort(); - for (var key of keys) { - log(key.padEnd(4) + ': ' + event.cmcdData[key]); - } - log(''); + // log('type: ' + event.mediaType); + // log('file: ' + event.url.split('/').pop()) + // // TODO: Add support for all modes + // const settings = player.getSettings().streaming.cmcd.reporting; + // if (settings.requestMode?.enabled){ + // var mode = settings.requestMode.mode; + // var data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); + // } + // if(settings.responseMode?.enabled){ + // var mode = settings.responseMode.mode; + // var data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); + // } + // //var data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); + // var keys = Object.keys(data); + // keys = keys.sort(); + // for (var key of keys) { + // log(key.padEnd(4) + ': ' + event.cmcdData[key]); + // } + // log(''); } function getKeysForQueryMode(event) { diff --git a/src/core/Settings.js b/src/core/Settings.js index 0d1242da58..e658f21c2a 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1297,10 +1297,10 @@ function Settings() { cid: null, rtp: null, rtpSafetyFactor: 5, + mode: Constants.CMCD_MODE_QUERY, enabledKeys: Constants.CMCD_AVAILABLE_KEYS, includeInRequests: ['segment'], reporting: { - enabledModes: [1], requestMode: { enabled: true, mode: Constants.CMCD_MODE_QUERY, diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 0869a99ad7..7b9e422d9d 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -228,16 +228,11 @@ function HTTPLoader(cfg) { }; const _onRequestEnd = function (aborted = false) { - const cmcdReportingModes = httpRequest.customData.request.cmcdReportingModes; - - if (cmcdReportingModes.includes(2)){ - const requestUrl = httpRequest.customData.request.cmcdMode2Url; - const requestMethod = httpRequest.customData.request.cmcdMode2Method; - const requestHeaders = httpRequest.customData.request.cmcdMode2Headers; - - fetch(requestUrl, { - method: requestMethod, - headers: requestHeaders, + const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; + if (cmcdResponseMode.enabled){ + fetch(cmcdResponseMode.requestUrl, { + method: cmcdResponseMode.requestMethod, + headers: cmcdResponseMode.requestHeaders, }).then(response => { console.log('CMCD data sent successfully:', response); }).catch(error => { @@ -621,40 +616,27 @@ function HTTPLoader(cfg) { const isIncludedFilters = clientDataReportingController.isServiceLocationIncluded(request.type, currentServiceLocation) && clientDataReportingController.isAdaptationsIncluded(currentAdaptationSetId); if (isIncludedFilters && cmcdModel.isCmcdEnabled()) { - const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); - const cmcdVersion = cmcdParameters.version ? cmcdParameters.version : settings.get().streaming.cmcd.version; - const cmcdReportingModes = settings.get().streaming.cmcd.reporting.enabledModes; + const cmcdRequestMode = settings.get().streaming.cmcd.reporting.requestMode; + const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; // Set common cmcd params - request.cmcdReportingModes = cmcdReportingModes; - - if (cmcdReportingModes.includes(1)){ - const cmcdMode1RequestMode = settings.get().streaming.cmcd.reporting.requestMode.mode; - if (cmcdMode1RequestMode === Constants.CMCD_MODE_QUERY) { + request.cmcdResponseMode = cmcdResponseMode; + + if (cmcdRequestMode.enabled){ + if (cmcdRequestMode.mode === Constants.CMCD_MODE_QUERY) { const additionalQueryParameter = _getAdditionalQueryParameter(request); request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); - } else if (cmcdMode1RequestMode === Constants.CMCD_MODE_HEADER) { + } else if (cmcdRequestMode.mode === Constants.CMCD_MODE_HEADER) { request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); } - } - if (cmcdReportingModes.includes(2)){ - const cmcdMode2Url = settings.get().streaming.cmcd.reporting.responseMode.requestUrl; - const cmcdMode2Method = settings.get().streaming.cmcd.reporting.responseMode.requestMethod; - const cmcdMode2RequestMode = settings.get().streaming.cmcd.reporting.responseMode.mode; - Object.assign(request, { - cmcdVersion, - cmcdMode2RequestMode, - cmcdMode2Method, - cmcdMode2Url - }); - if (cmcdMode2RequestMode === Constants.CMCD_MODE_QUERY){ + if (cmcdResponseMode.enabled){ + if (cmcdResponseMode.mode === Constants.CMCD_MODE_QUERY){ const additionalQueryParameter = _getAdditionalQueryParameter(request); - request.cmcdMode2Url = Utils.addAditionalQueryParameterToUrl(cmcdMode2Url, additionalQueryParameter); - } else if (cmcdMode2RequestMode === Constants.CMCD_MODE_HEADER){ + request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); + } else if (cmcdResponseMode.mode === Constants.CMCD_MODE_HEADER){ const cmcdHeaders = cmcdModel.getHeaderParameters(request); - const reportingHeaders = settings.get().streaming.cmcd.reporting.responseMode.requestHeaders; - request.cmcdMode2Headers = { ...reportingHeaders, ...cmcdHeaders}; + request.cmcdResponseMode.requestHeaders = { ...cmcdResponseMode.requestHeaders, ...cmcdHeaders}; } } } @@ -670,6 +652,7 @@ function HTTPLoader(cfg) { try { const additionalQueryParameter = []; const cmcdQueryParameter = cmcdModel.getQueryParameter(request); + console.warn(`cmcdQueryparams: ${cmcdQueryParameter}`) if (cmcdQueryParameter) { additionalQueryParameter.push(cmcdQueryParameter); diff --git a/src/streaming/protection/controllers/ProtectionController.js b/src/streaming/protection/controllers/ProtectionController.js index f330616ec4..425ba04913 100644 --- a/src/streaming/protection/controllers/ProtectionController.js +++ b/src/streaming/protection/controllers/ProtectionController.js @@ -822,8 +822,7 @@ function ProtectionController(config) { const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); if (cmcdModel.isCmcdEnabled()) { - // TODO: Add support for all modes - const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.reporting.responseMode.mode; + const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; if (cmcdMode === Constants.CMCD_MODE_QUERY) { const cmcdParams = cmcdModel.getQueryParameter({ url: request.url, From f498e8afc94a8fae510776b83f9ca5e578979490 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Tue, 20 Aug 2024 16:38:53 -0300 Subject: [PATCH 07/82] Reverted change in main.js --- samples/dash-if-reference-player/app/main.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/dash-if-reference-player/app/main.js b/samples/dash-if-reference-player/app/main.js index 0abdb56a69..d192ef680f 100644 --- a/samples/dash-if-reference-player/app/main.js +++ b/samples/dash-if-reference-player/app/main.js @@ -2394,8 +2394,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors' $scope.cmcdRtpSafetyFactor = currentConfig.streaming.cmcd.rtpSafetyFactor; } - // TODO: Add support for all modes - $scope.cmcdMode = currentConfig.streaming.cmcd.reporting.responseMode.mode; + $scope.cmcdMode = currentConfig.streaming.cmcd.mode; if (currentConfig.streaming.cmcd.enabledKeys) { $scope.cmcdEnabledKeys = currentConfig.streaming.cmcd.enabledKeys; From 0d6e103c4d5a009783859d25ca3775e1321c759e Mon Sep 17 00:00:00 2001 From: sannti97 Date: Tue, 20 Aug 2024 16:42:02 -0300 Subject: [PATCH 08/82] Removed warning log --- src/streaming/net/HTTPLoader.js | 1 - src/streaming/protection/controllers/ProtectionController.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 7b9e422d9d..36bd0a3b76 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -652,7 +652,6 @@ function HTTPLoader(cfg) { try { const additionalQueryParameter = []; const cmcdQueryParameter = cmcdModel.getQueryParameter(request); - console.warn(`cmcdQueryparams: ${cmcdQueryParameter}`) if (cmcdQueryParameter) { additionalQueryParameter.push(cmcdQueryParameter); diff --git a/src/streaming/protection/controllers/ProtectionController.js b/src/streaming/protection/controllers/ProtectionController.js index 425ba04913..8c2213e9b3 100644 --- a/src/streaming/protection/controllers/ProtectionController.js +++ b/src/streaming/protection/controllers/ProtectionController.js @@ -846,7 +846,7 @@ function ProtectionController(config) { } if (cmcdModel.isCmcdEnabled()) { - const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.reporting.requestMode.mode; + const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; if (cmcdMode === Constants.CMCD_MODE_HEADER) { const cmcdHeaders = cmcdModel.getHeaderParameters({ url: request.url, From f30f2bbb340d30b467366b6b48550f4738d4b787 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 21 Aug 2024 17:40:11 -0300 Subject: [PATCH 09/82] Added version to config and fixed handleCmcdDataGeneratedEvent --- samples/advanced/cmcd.html | 37 ++++++++++++----------------- src/core/Settings.js | 5 ++-- src/streaming/models/CmcdModel.js | 16 +++++++------ src/streaming/net/HTTPLoader.js | 39 ++++++++++++++++++------------- 4 files changed, 50 insertions(+), 47 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 3b20bc19cd..21a9c6f799 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -47,14 +47,16 @@ streaming: { cmcd: { enabled: true, /* enable reporting of cmcd parameters */ + version: 2, + mode: CMCD_MODE_HEADER, reporting: { requestMode: { enabled: true, - mode: CMCD_MODE_QUERY, + mode: CMCD_MODE_HEADER, }, responseMode: { enabled: true, - mode: CMCD_MODE_HEADER, + mode: CMCD_MODE_QUERY, requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'GET', requestHeaders: {} @@ -71,27 +73,18 @@ player.attachSource(url); } - // TODO: Needs to be commented to make both modes work function handleCmcdDataGeneratedEvent(event) { - // log('type: ' + event.mediaType); - // log('file: ' + event.url.split('/').pop()) - // // TODO: Add support for all modes - // const settings = player.getSettings().streaming.cmcd.reporting; - // if (settings.requestMode?.enabled){ - // var mode = settings.requestMode.mode; - // var data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); - // } - // if(settings.responseMode?.enabled){ - // var mode = settings.responseMode.mode; - // var data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); - // } - // //var data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); - // var keys = Object.keys(data); - // keys = keys.sort(); - // for (var key of keys) { - // log(key.padEnd(4) + ': ' + event.cmcdData[key]); - // } - // log(''); + const cmcdEvent = JSON.parse(JSON.stringify(event)); + log('type: ' + cmcdEvent.mediaType); + log('file: ' + cmcdEvent.url.split('/').pop()) + var mode = player.getSettings().streaming.cmcd.mode; + var data = getKeysForHeaderMode(cmcdEvent); + var keys = Object.keys(data); + keys = keys.sort(); + for (var key of keys) { + log(key.padEnd(4) + ': ' + cmcdEvent.cmcdData[key]); + } + log(''); } function getKeysForQueryMode(event) { diff --git a/src/core/Settings.js b/src/core/Settings.js index e658f21c2a..0e1b730d01 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1293,6 +1293,7 @@ function Settings() { cmcd: { applyParametersFromMpd: true, enabled: false, + version: 2, sid: null, cid: null, rtp: null, @@ -1303,11 +1304,11 @@ function Settings() { reporting: { requestMode: { enabled: true, - mode: Constants.CMCD_MODE_QUERY, + mode: null, }, responseMode: { enabled: false, - mode: Constants.CMCD_MODE_QUERY, + mode: null, requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'POST', requestHeaders: {} diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index c689097671..a3ff4816f9 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -149,19 +149,21 @@ function CmcdModel() { streamProcessors = activeStream.getStreamProcessors(); } - function getQueryParameter(request) { + function getQueryParameter(request, triggerEvent = true) { try { if (isCmcdEnabled()) { const cmcdData = getCmcdData(request); const filteredCmcdData = _applyWhitelist(cmcdData); const finalPayloadString = encodeCmcd(filteredCmcdData); - eventBus.trigger(MetricsReportingEvents.CMCD_DATA_GENERATED, { - url: request.url, - mediaType: request.mediaType, - cmcdData, - cmcdString: finalPayloadString - }); + if (triggerEvent){ + eventBus.trigger(MetricsReportingEvents.CMCD_DATA_GENERATED, { + url: request.url, + mediaType: request.mediaType, + cmcdData, + cmcdString: finalPayloadString + }); + } return { key: CMCD_PARAM, value: finalPayloadString diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 36bd0a3b76..dab9018fd9 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -616,28 +616,35 @@ function HTTPLoader(cfg) { const isIncludedFilters = clientDataReportingController.isServiceLocationIncluded(request.type, currentServiceLocation) && clientDataReportingController.isAdaptationsIncluded(currentAdaptationSetId); if (isIncludedFilters && cmcdModel.isCmcdEnabled()) { + const cmcdVersion = settings.get().streaming.cmcd.version; const cmcdRequestMode = settings.get().streaming.cmcd.reporting.requestMode; const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - // Set common cmcd params - request.cmcdResponseMode = cmcdResponseMode; + const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); + var cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; - if (cmcdRequestMode.enabled){ - if (cmcdRequestMode.mode === Constants.CMCD_MODE_QUERY) { - const additionalQueryParameter = _getAdditionalQueryParameter(request); + const additionalQueryParameter = _getAdditionalQueryParameter(request); + const cmcdHeaders = cmcdModel.getHeaderParameters(request); + + if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdRequestMode.enabled)){ + cmcdMode = cmcdRequestMode.mode ? cmcdRequestMode.mode : cmcdMode; + if (cmcdMode === Constants.CMCD_MODE_QUERY) { request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); - } else if (cmcdRequestMode.mode === Constants.CMCD_MODE_HEADER) { - request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); + } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { + request.headers = Object.assign(request.headers, cmcdHeaders); } } - - if (cmcdResponseMode.enabled){ - if (cmcdResponseMode.mode === Constants.CMCD_MODE_QUERY){ - const additionalQueryParameter = _getAdditionalQueryParameter(request); - request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); - } else if (cmcdResponseMode.mode === Constants.CMCD_MODE_HEADER){ - const cmcdHeaders = cmcdModel.getHeaderParameters(request); - request.cmcdResponseMode.requestHeaders = { ...cmcdResponseMode.requestHeaders, ...cmcdHeaders}; + + if (cmcdVersion === 2) { + if (cmcdResponseMode.enabled){ + request.cmcdResponseMode = cmcdResponseMode; + cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : cmcdMode; + if (cmcdMode === Constants.CMCD_MODE_QUERY){ + request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); + } else if (cmcdMode === Constants.CMCD_MODE_HEADER){ + request.cmcdResponseMode.requestHeaders = { ...cmcdResponseMode.requestHeaders, ...cmcdHeaders}; + } } + // TODO: Add State-Interval Mode } } } @@ -651,7 +658,7 @@ function HTTPLoader(cfg) { function _getAdditionalQueryParameter(request) { try { const additionalQueryParameter = []; - const cmcdQueryParameter = cmcdModel.getQueryParameter(request); + const cmcdQueryParameter = cmcdModel.getQueryParameter(request, false); if (cmcdQueryParameter) { additionalQueryParameter.push(cmcdQueryParameter); From 86c40c9c44dbf45d6500feb7ef3b2d06b8f7b888 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 21 Aug 2024 17:45:20 -0300 Subject: [PATCH 10/82] Reverted changes in handleCmcdDataGeneratedEvent --- samples/advanced/cmcd.html | 11 +++++------ src/streaming/net/HTTPLoader.js | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 21a9c6f799..166ff3bec9 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -48,7 +48,6 @@ cmcd: { enabled: true, /* enable reporting of cmcd parameters */ version: 2, - mode: CMCD_MODE_HEADER, reporting: { requestMode: { enabled: true, @@ -64,6 +63,7 @@ }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ cid: '21cf726cfe3d937b5f974f72bb5bd06a', /* content id send with each request */ + mode: CMCD_MODE_HEADER, enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] } } @@ -74,15 +74,14 @@ } function handleCmcdDataGeneratedEvent(event) { - const cmcdEvent = JSON.parse(JSON.stringify(event)); - log('type: ' + cmcdEvent.mediaType); - log('file: ' + cmcdEvent.url.split('/').pop()) + log('type: ' + event.mediaType); + log('file: ' + event.url.split('/').pop()) var mode = player.getSettings().streaming.cmcd.mode; - var data = getKeysForHeaderMode(cmcdEvent); + var data = getKeysForHeaderMode(event); var keys = Object.keys(data); keys = keys.sort(); for (var key of keys) { - log(key.padEnd(4) + ': ' + cmcdEvent.cmcdData[key]); + log(key.padEnd(4) + ': ' + event.cmcdData[key]); } log(''); } diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index dab9018fd9..cdec0254c7 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -622,6 +622,7 @@ function HTTPLoader(cfg) { const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); var cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; + // Always get both queries and headers for now const additionalQueryParameter = _getAdditionalQueryParameter(request); const cmcdHeaders = cmcdModel.getHeaderParameters(request); From 33e9983bd7fe68977c87fb9f34358b9f7f13c14a Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 21 Aug 2024 18:18:44 -0300 Subject: [PATCH 11/82] Improved _updateRequestUrlAndHeadersWithCMCD code --- samples/advanced/cmcd.html | 11 +++++- src/streaming/net/HTTPLoader.js | 70 +++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 166ff3bec9..7c9738ae61 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -63,7 +63,7 @@ }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ cid: '21cf726cfe3d937b5f974f72bb5bd06a', /* content id send with each request */ - mode: CMCD_MODE_HEADER, + mode: CMCD_MODE_QUERY, enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] } } @@ -77,7 +77,14 @@ log('type: ' + event.mediaType); log('file: ' + event.url.split('/').pop()) var mode = player.getSettings().streaming.cmcd.mode; - var data = getKeysForHeaderMode(event); + const cmcdVersion = player.getSettings().streaming.cmcd.version; + var data = {}; + if (cmcdVersion === 1) { + data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); + } else if (cmcdVersion === 2){ + // Currently, headers are always generated in cmcd version 2 + data = getKeysForHeaderMode(event); + } var keys = Object.keys(data); keys = keys.sort(); for (var key of keys) { diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index cdec0254c7..2e8767fad0 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -228,16 +228,18 @@ function HTTPLoader(cfg) { }; const _onRequestEnd = function (aborted = false) { - const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; - if (cmcdResponseMode.enabled){ - fetch(cmcdResponseMode.requestUrl, { - method: cmcdResponseMode.requestMethod, - headers: cmcdResponseMode.requestHeaders, - }).then(response => { - console.log('CMCD data sent successfully:', response); - }).catch(error => { - console.error('Error sending CMCD data:', error); - }); + if (httpRequest.customData.request.cmcdVersion === 2) { + const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; + if (cmcdResponseMode.enabled){ + fetch(cmcdResponseMode.requestUrl, { + method: cmcdResponseMode.requestMethod, + headers: cmcdResponseMode.requestHeaders, + }).then(response => { + console.log('CMCD data sent successfully:', response); + }).catch(error => { + console.error('Error sending CMCD data:', error); + }); + } } // Remove the request from our list of requests @@ -617,36 +619,46 @@ function HTTPLoader(cfg) { clientDataReportingController.isAdaptationsIncluded(currentAdaptationSetId); if (isIncludedFilters && cmcdModel.isCmcdEnabled()) { const cmcdVersion = settings.get().streaming.cmcd.version; - const cmcdRequestMode = settings.get().streaming.cmcd.reporting.requestMode; - const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); - var cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; - - // Always get both queries and headers for now - const additionalQueryParameter = _getAdditionalQueryParameter(request); - const cmcdHeaders = cmcdModel.getHeaderParameters(request); - - if (cmcdVersion === 1 || (cmcdVersion === 2 && cmcdRequestMode.enabled)){ - cmcdMode = cmcdRequestMode.mode ? cmcdRequestMode.mode : cmcdMode; + request.cmcdVersion = cmcdVersion; + if (cmcdVersion === 1){ + const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); + const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; if (cmcdMode === Constants.CMCD_MODE_QUERY) { + const additionalQueryParameter = _getAdditionalQueryParameter(request); request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { - request.headers = Object.assign(request.headers, cmcdHeaders); + request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); } - } - - if (cmcdVersion === 2) { + } else if (cmcdVersion === 2){ + const cmcdRequestMode = settings.get().streaming.cmcd.reporting.requestMode; + const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; + + // Currently, both headers and queries are always generated + // to easily handle the CMCD_DATA_GENERATED event trigger. + // TODO: They should only be generated if the corresponding mode is activated. + const cmcdHeaders = cmcdModel.getHeaderParameters(request); + const additionalQueryParameter = _getAdditionalQueryParameter(request, false); + + if (cmcdRequestMode.enabled){ + const cmcdMode = cmcdRequestMode.mode ? cmcdRequestMode.mode : settings.get().streaming.cmcd.mode; + if (cmcdMode === Constants.CMCD_MODE_QUERY) { + request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); + } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { + request.headers = Object.assign(request.headers, cmcdHeaders); + } + } + if (cmcdResponseMode.enabled){ request.cmcdResponseMode = cmcdResponseMode; - cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : cmcdMode; + const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; if (cmcdMode === Constants.CMCD_MODE_QUERY){ request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); } else if (cmcdMode === Constants.CMCD_MODE_HEADER){ request.cmcdResponseMode.requestHeaders = { ...cmcdResponseMode.requestHeaders, ...cmcdHeaders}; } } - // TODO: Add State-Interval Mode } + // TODO: Add State-Interval Mode } } @@ -656,10 +668,10 @@ function HTTPLoader(cfg) { * @return {array} * @private */ - function _getAdditionalQueryParameter(request) { + function _getAdditionalQueryParameter(request, eventTrigger = true) { try { const additionalQueryParameter = []; - const cmcdQueryParameter = cmcdModel.getQueryParameter(request, false); + const cmcdQueryParameter = cmcdModel.getQueryParameter(request, eventTrigger); if (cmcdQueryParameter) { additionalQueryParameter.push(cmcdQueryParameter); From dd4ab143a603602543436db284665dbb546bbfaa Mon Sep 17 00:00:00 2001 From: sannti97 Date: Fri, 23 Aug 2024 13:12:54 -0300 Subject: [PATCH 12/82] Added mandatory keys for responseMode Also added enabledKeys for each cmcd v2 mode --- samples/advanced/cmcd.html | 3 +- src/core/Settings.js | 2 + src/streaming/constants/Constants.js | 2 +- src/streaming/models/CmcdModel.js | 65 +++++++++++++++++++--------- src/streaming/net/HTTPLoader.js | 16 +++---- 5 files changed, 57 insertions(+), 31 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 7c9738ae61..465cc86580 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -47,7 +47,7 @@ streaming: { cmcd: { enabled: true, /* enable reporting of cmcd parameters */ - version: 2, + version: 1, reporting: { requestMode: { enabled: true, @@ -56,6 +56,7 @@ responseMode: { enabled: true, mode: CMCD_MODE_QUERY, + enabledKeys: ['br', 'd', 'ot', 'tb'], requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'GET', requestHeaders: {} diff --git a/src/core/Settings.js b/src/core/Settings.js index 0e1b730d01..83d7998837 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1305,10 +1305,12 @@ function Settings() { requestMode: { enabled: true, mode: null, + enabledKeys: null, }, responseMode: { enabled: false, mode: null, + enabledKeys: null, requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'POST', requestHeaders: {} diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index 390619222c..d7e92b643e 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -220,7 +220,7 @@ export default { * @memberof Constants# * @static */ - CMCD_AVAILABLE_KEYS: ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v'], + CMCD_AVAILABLE_KEYS: ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'ts', 'url'], /** * @constant {string} CMCD_AVAILABLE_REQUESTS specifies all the availables requests type for CMCD metrics. diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index a3ff4816f9..c214161ec2 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -149,11 +149,11 @@ function CmcdModel() { streamProcessors = activeStream.getStreamProcessors(); } - function getQueryParameter(request, triggerEvent = true) { + function getQueryParameter(request, triggerEvent = true, cmcdReportingMode = null) { try { if (isCmcdEnabled()) { const cmcdData = getCmcdData(request); - const filteredCmcdData = _applyWhitelist(cmcdData); + const filteredCmcdData = _applyWhitelist(cmcdData, cmcdReportingMode); const finalPayloadString = encodeCmcd(filteredCmcdData); if (triggerEvent){ @@ -176,11 +176,24 @@ function CmcdModel() { } } - function _applyWhitelist(cmcdData) { + function _applyWhitelist(cmcdData, cmcdReportingMode) { try { const cmcdParametersFromManifest = getCmcdParametersFromManifest(); - const enabledCMCDKeys = cmcdParametersFromManifest.version ? cmcdParametersFromManifest.keys : settings.get().streaming.cmcd.enabledKeys; - + var enabledCMCDKeys = cmcdParametersFromManifest.version ? cmcdParametersFromManifest.keys : settings.get().streaming.cmcd.enabledKeys; + + // For CMCD v2 use the reporting mode keys or global ones as default + if (cmcdReportingMode === 1){ + enabledCMCDKeys = settings.get().streaming.cmcd.reporting.requestMode.enabledKeys ? settings.get().streaming.cmcd.reporting.requestMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; + } else if (cmcdReportingMode === 2) { + enabledCMCDKeys = settings.get().streaming.cmcd.reporting.responseMode.enabledKeys ? settings.get().streaming.cmcd.reporting.responseMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; + // Add CMCD v2 response mode mandatory keys + const requiredKeys = ['ts', 'url']; + requiredKeys.forEach(key => { + if (!enabledCMCDKeys.includes(key)) { + enabledCMCDKeys.push(key); + } + }); + } return Object.keys(cmcdData) .filter(key => enabledCMCDKeys.includes(key)) .reduce((obj, key) => { @@ -192,19 +205,21 @@ function CmcdModel() { } } - function getHeaderParameters(request) { + function getHeaderParameters(request, triggerEvent = true, cmcdReportingMode = null) { try { if (isCmcdEnabled()) { const cmcdData = getCmcdData(request); - const filteredCmcdData = _applyWhitelist(cmcdData); + const filteredCmcdData = _applyWhitelist(cmcdData, cmcdReportingMode); const headers = toCmcdHeaders(filteredCmcdData) - eventBus.trigger(MetricsReportingEvents.CMCD_DATA_GENERATED, { - url: request.url, - mediaType: request.mediaType, - cmcdData, - headers - }); + if (triggerEvent) { + eventBus.trigger(MetricsReportingEvents.CMCD_DATA_GENERATED, { + url: request.url, + mediaType: request.mediaType, + cmcdData, + headers + }); + } return headers; } @@ -361,8 +376,8 @@ function CmcdModel() { return data; } - function _getCmcdDataForMpd() { - const data = _getGenericCmcdData(); + function _getCmcdDataForMpd(request) { + const data = _getGenericCmcdData(request); data.ot = CmcdObjectType.MANIFEST; @@ -371,7 +386,7 @@ function CmcdModel() { function _getCmcdDataForMediaSegment(request, mediaType) { _initForMediaType(mediaType); - const data = _getGenericCmcdData(); + const data = _getGenericCmcdData(request); const encodedBitrate = _getBitrateByRequest(request); const d = _getObjectDurationByRequest(request); const mtp = _getMeasuredThroughputByType(mediaType); @@ -474,8 +489,8 @@ function CmcdModel() { } } - function _getCmcdDataForInitSegment() { - const data = _getGenericCmcdData(); + function _getCmcdDataForInitSegment(request) { + const data = _getGenericCmcdData(request); data.ot = CmcdObjectType.INIT; data.su = true; @@ -483,8 +498,8 @@ function CmcdModel() { return data; } - function _getCmcdDataForOther() { - const data = _getGenericCmcdData(); + function _getCmcdDataForOther(request) { + const data = _getGenericCmcdData(request); data.ot = CmcdObjectType.OTHER; @@ -492,7 +507,7 @@ function CmcdModel() { } - function _getGenericCmcdData() { + function _getGenericCmcdData(request) { const cmcdParametersFromManifest = getCmcdParametersFromManifest(); const data = {}; @@ -522,6 +537,14 @@ function CmcdModel() { data.sf = internalData.sf; } + // Add v2 mandatory keys + const cmcdVersion = settings.get().streaming.cmcd.version; + const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; + if (cmcdVersion === 2 && cmcdResponseMode.enabled) { + data.url = request.url; + data.ts = Date.now(); + } + return data; } diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 2e8767fad0..4e22edf4a1 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -632,18 +632,16 @@ function HTTPLoader(cfg) { } else if (cmcdVersion === 2){ const cmcdRequestMode = settings.get().streaming.cmcd.reporting.requestMode; const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - - // Currently, both headers and queries are always generated - // to easily handle the CMCD_DATA_GENERATED event trigger. - // TODO: They should only be generated if the corresponding mode is activated. - const cmcdHeaders = cmcdModel.getHeaderParameters(request); - const additionalQueryParameter = _getAdditionalQueryParameter(request, false); + // Needs to be called to trigger the CMCD_DATA_GENERATED event only once + cmcdModel.getHeaderParameters(request); if (cmcdRequestMode.enabled){ const cmcdMode = cmcdRequestMode.mode ? cmcdRequestMode.mode : settings.get().streaming.cmcd.mode; if (cmcdMode === Constants.CMCD_MODE_QUERY) { + const additionalQueryParameter = _getAdditionalQueryParameter(request, false, 1); request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { + const cmcdHeaders = cmcdModel.getHeaderParameters(request, false, 1); request.headers = Object.assign(request.headers, cmcdHeaders); } } @@ -652,8 +650,10 @@ function HTTPLoader(cfg) { request.cmcdResponseMode = cmcdResponseMode; const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; if (cmcdMode === Constants.CMCD_MODE_QUERY){ + const additionalQueryParameter = _getAdditionalQueryParameter(request, false, 2); request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); } else if (cmcdMode === Constants.CMCD_MODE_HEADER){ + const cmcdHeaders = cmcdModel.getHeaderParameters(request, false, 2); request.cmcdResponseMode.requestHeaders = { ...cmcdResponseMode.requestHeaders, ...cmcdHeaders}; } } @@ -668,10 +668,10 @@ function HTTPLoader(cfg) { * @return {array} * @private */ - function _getAdditionalQueryParameter(request, eventTrigger = true) { + function _getAdditionalQueryParameter(request, triggerEvent = true, reportingMode = null) { try { const additionalQueryParameter = []; - const cmcdQueryParameter = cmcdModel.getQueryParameter(request, eventTrigger); + const cmcdQueryParameter = cmcdModel.getQueryParameter(request, triggerEvent, reportingMode); if (cmcdQueryParameter) { additionalQueryParameter.push(cmcdQueryParameter); From 01bfb0ca3f15b56d848b6ce44e188e86d68d4d37 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Fri, 23 Aug 2024 13:19:49 -0300 Subject: [PATCH 13/82] Added observation about ts key --- docs/cmcd-v2/reporting-modes.md | 4 +++- src/streaming/models/CmcdModel.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/cmcd-v2/reporting-modes.md b/docs/cmcd-v2/reporting-modes.md index 9cb8de225f..49ce59bc23 100644 --- a/docs/cmcd-v2/reporting-modes.md +++ b/docs/cmcd-v2/reporting-modes.md @@ -7,4 +7,6 @@ Here are notes about the challenge of implementing the new reporting modes of CM - Modified the `_updateRequestUrlAndHeadersWithCMCD` function to save the cmcdHeaders or `cmcdParams` in `customData` so they can be sent to the remote server in the `_onRequestEnd` function. - Observations: - - The mode that already exists in the configuration for the "transmition mode" might be confusing with the new mode for the "reporting mode". \ No newline at end of file + - The mode that already exists in the configuration for the "transmition mode" might be confusing with the new mode for the "reporting mode". + + - The ‘ts’ key is not entirely accurate since the CMCD parameters are generated before the request starts. \ No newline at end of file diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index c214161ec2..ddde4500b8 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -542,6 +542,7 @@ function CmcdModel() { const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; if (cmcdVersion === 2 && cmcdResponseMode.enabled) { data.url = request.url; + // TODO: This key needs to be generated when loading the media request in _loadRequest data.ts = Date.now(); } From 8643aa7cb40b914c870e5448570d2d309a58b3cd Mon Sep 17 00:00:00 2001 From: sannti97 Date: Fri, 23 Aug 2024 13:42:26 -0300 Subject: [PATCH 14/82] Remove cmcd params from url key --- src/streaming/models/CmcdModel.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index ddde4500b8..c7cc22f096 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -184,6 +184,8 @@ function CmcdModel() { // For CMCD v2 use the reporting mode keys or global ones as default if (cmcdReportingMode === 1){ enabledCMCDKeys = settings.get().streaming.cmcd.reporting.requestMode.enabledKeys ? settings.get().streaming.cmcd.reporting.requestMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; + // Remove unsupported keys + enabledCMCDKeys = enabledCMCDKeys.filter(item => item !== 'url'); } else if (cmcdReportingMode === 2) { enabledCMCDKeys = settings.get().streaming.cmcd.reporting.responseMode.enabledKeys ? settings.get().streaming.cmcd.reporting.responseMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; // Add CMCD v2 response mode mandatory keys @@ -541,7 +543,7 @@ function CmcdModel() { const cmcdVersion = settings.get().streaming.cmcd.version; const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; if (cmcdVersion === 2 && cmcdResponseMode.enabled) { - data.url = request.url; + data.url = request.url.split('?')[0]; // remove potential cmcd query params // TODO: This key needs to be generated when loading the media request in _loadRequest data.ts = Date.now(); } From 1371edf2e3944aaf25ef922877443105d2f07586 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Fri, 23 Aug 2024 15:01:44 -0300 Subject: [PATCH 15/82] Explained cmcd example config --- samples/advanced/cmcd.html | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 465cc86580..7db056c718 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -46,25 +46,25 @@ player.updateSettings({ streaming: { cmcd: { - enabled: true, /* enable reporting of cmcd parameters */ - version: 1, + enabled: true, /* global enable reporting of cmcd parameters */ + version: 2, /* 1 is the default version */ reporting: { requestMode: { - enabled: true, - mode: CMCD_MODE_HEADER, + enabled: true, /* enable cmcdv2 request mode */ + mode: CMCD_MODE_HEADER, /*overrides global mode */ }, responseMode: { - enabled: true, - mode: CMCD_MODE_QUERY, - enabledKeys: ['br', 'd', 'ot', 'tb'], - requestUrl: 'http://localhost:3000/cmcd_server', - requestMethod: 'GET', - requestHeaders: {} + enabled: true, /* enable cmcdv2 response mode */ + mode: CMCD_MODE_QUERY, /*overrides global mode */ + enabledKeys: ['br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ + requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ + requestMethod: 'POST', /*optional, get by default */ + requestHeaders: {} /*optional, additional headers for report request */ } }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ cid: '21cf726cfe3d937b5f974f72bb5bd06a', /* content id send with each request */ - mode: CMCD_MODE_QUERY, + mode: CMCD_MODE_QUERY, /* global mode if not specified in each mode */ enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] } } From bd8d0c35833daa28f50b07045fa9af2a6e33f9ef Mon Sep 17 00:00:00 2001 From: sannti97 Date: Sat, 24 Aug 2024 17:36:49 -0300 Subject: [PATCH 16/82] Fix bug when desabling cmcdResponseMode --- src/streaming/net/HTTPLoader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 4e22edf4a1..4b7c6f1536 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -230,7 +230,7 @@ function HTTPLoader(cfg) { const _onRequestEnd = function (aborted = false) { if (httpRequest.customData.request.cmcdVersion === 2) { const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; - if (cmcdResponseMode.enabled){ + if (cmcdResponseMode && cmcdResponseMode.enabled){ fetch(cmcdResponseMode.requestUrl, { method: cmcdResponseMode.requestMethod, headers: cmcdResponseMode.requestHeaders, From 73545fbc4216dcc9b3f748cb68c1650ef076b38a Mon Sep 17 00:00:00 2001 From: sannti97 Date: Sat, 24 Aug 2024 18:27:22 -0300 Subject: [PATCH 17/82] Init commit for adding state-interval mode --- samples/advanced/cmcd.html | 9 +++- src/core/Settings.js | 7 +++ src/streaming/models/CmcdModel.js | 81 ++++++++++++++++++++++++++++++- src/streaming/net/HTTPLoader.js | 10 +++- 4 files changed, 103 insertions(+), 4 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 7db056c718..21914fcc82 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -54,12 +54,19 @@ mode: CMCD_MODE_HEADER, /*overrides global mode */ }, responseMode: { - enabled: true, /* enable cmcdv2 response mode */ + enabled: false, /* enable cmcdv2 response mode */ mode: CMCD_MODE_QUERY, /*overrides global mode */ enabledKeys: ['br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ requestHeaders: {} /*optional, additional headers for report request */ + }, + stateIntervalMode: { + enabled: true, + interval: 10000, // 10 seconds + requestUrl: 'http://localhost:3000/cmcd_state', + requestMethod: 'POST', + enabledKeys: ['br', 'd', 'ot', 'tb'] } }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ diff --git a/src/core/Settings.js b/src/core/Settings.js index 83d7998837..c9e8744b85 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1314,6 +1314,13 @@ function Settings() { requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'POST', requestHeaders: {} + }, + stateIntervalMode: { + enabled: false, + interval: 30000, + requestUrl: 'http://localhost:3000/cmcd_server', + requestMethod: 'POST', + enabledKeys: null } } }, diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index c7cc22f096..78988ebc2d 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -65,6 +65,9 @@ function CmcdModel() { _isStartup, _bufferLevelStarved, _initialMediaRequestsDone; + + let stateIntervalData = []; + let stateIntervalTimer = null; let context = this.context; let eventBus = EventBus(context).getInstance(); @@ -83,6 +86,9 @@ function CmcdModel() { eventBus.on(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance); eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); eventBus.on(MediaPlayerEvents.PERIOD_SWITCH_COMPLETED, _onPeriodSwitchComplete, instance); + // TODO: Add more player states + eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('playing'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('paused'), instance); } function setConfig(config) { @@ -195,6 +201,9 @@ function CmcdModel() { enabledCMCDKeys.push(key); } }); + } else if (cmcdReportingMode === 3) { + enabledCMCDKeys = settings.get().streaming.cmcd.reporting.stateIntervalMode.enabledKeys ? settings.get().streaming.cmcd.reporting.stateIntervalMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; + // TODO: Add required keys } return Object.keys(cmcdData) .filter(key => enabledCMCDKeys.includes(key)) @@ -207,6 +216,74 @@ function CmcdModel() { } } + function handleStateIntervalData(request) { + try { + if (isCmcdEnabled()) { + const cmcdData = getCmcdData(request); + const filteredCmcdData = _applyWhitelist(cmcdData, 3); + if (filteredCmcdData) { + stateIntervalData.push(filteredCmcdData); + } + if (!stateIntervalTimer) { + stateIntervalTimer = setTimeout(_sendStateIntervalData, settings.get().streaming.cmcd.reporting.stateIntervalMode.interval); + } + } + } catch (e) { + return null; + } + } + + function _sendStateIntervalData() { + const cmcdStateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; + if (cmcdStateIntervalMode.enabled && stateIntervalData.length > 0) { + const aggregatedData = _mergeCmcdData(stateIntervalData); + + // TODO: Check mandatory keys + const payload = { + ts: Date.now(), + cmcdData: aggregatedData // Array of CMCD data objects + }; + + fetch(cmcdStateIntervalMode.requestUrl, { + method: cmcdStateIntervalMode.requestMethod, + headers: { + ...cmcdStateIntervalMode.requestHeaders, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }).then(response => { + console.log('State-interval CMCD data sent successfully:', response); + }).catch(error => { + console.error('Error sending state-interval CMCD data:', error); + }).finally(() => { + stateIntervalData = []; // Reset the data array + stateIntervalTimer = null; // Clear the timer + }); + } + } + + function _mergeCmcdData(dataArray) { + // TODO: Map configured keys + return dataArray. + filter(data => Object.keys(data).length > 0). + map(data => { + return { + br: data.br, + d: data.d, + ot: data.ot, + tb: data.tb, + }; + }); + } + + function _onStateChange(state) { + console.log(`Player state changed to: ${state}`) + const stateData = { sta: state, ts: Date.now() }; + stateIntervalData.push(stateData); + _sendStateIntervalData(); + } + + function getHeaderParameters(request, triggerEvent = true, cmcdReportingMode = null) { try { if (isCmcdEnabled()) { @@ -716,7 +793,8 @@ function CmcdModel() { eventBus.off(MediaPlayerEvents.MANIFEST_LOADED, _onManifestLoaded, this); eventBus.off(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance); eventBus.off(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); - + eventBus.off(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('playing'), instance); + eventBus.off(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('paused'), instance); _resetInitialSettings(); } @@ -729,6 +807,7 @@ function CmcdModel() { reset, initialize, isCmcdEnabled, + handleStateIntervalData }; setup(); diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 4e22edf4a1..812d71ce81 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -230,7 +230,7 @@ function HTTPLoader(cfg) { const _onRequestEnd = function (aborted = false) { if (httpRequest.customData.request.cmcdVersion === 2) { const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; - if (cmcdResponseMode.enabled){ + if (cmcdResponseMode && cmcdResponseMode.enabled){ fetch(cmcdResponseMode.requestUrl, { method: cmcdResponseMode.requestMethod, headers: cmcdResponseMode.requestHeaders, @@ -632,6 +632,7 @@ function HTTPLoader(cfg) { } else if (cmcdVersion === 2){ const cmcdRequestMode = settings.get().streaming.cmcd.reporting.requestMode; const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; + const stateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; // Needs to be called to trigger the CMCD_DATA_GENERATED event only once cmcdModel.getHeaderParameters(request); @@ -657,8 +658,13 @@ function HTTPLoader(cfg) { request.cmcdResponseMode.requestHeaders = { ...cmcdResponseMode.requestHeaders, ...cmcdHeaders}; } } + + if (stateIntervalMode.enabled){ + // const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; + // Only JSON mode for now + cmcdModel.handleStateIntervalData(request) + } } - // TODO: Add State-Interval Mode } } From aa35d525c7786780a01a0377e6a165d837222497 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Mon, 26 Aug 2024 17:05:56 -0300 Subject: [PATCH 18/82] Revert "Init commit for adding state-interval mode" This reverts commit 73545fbc4216dcc9b3f748cb68c1650ef076b38a. --- samples/advanced/cmcd.html | 9 +--- src/core/Settings.js | 7 --- src/streaming/models/CmcdModel.js | 81 +------------------------------ src/streaming/net/HTTPLoader.js | 10 +--- 4 files changed, 4 insertions(+), 103 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 21914fcc82..7db056c718 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -54,19 +54,12 @@ mode: CMCD_MODE_HEADER, /*overrides global mode */ }, responseMode: { - enabled: false, /* enable cmcdv2 response mode */ + enabled: true, /* enable cmcdv2 response mode */ mode: CMCD_MODE_QUERY, /*overrides global mode */ enabledKeys: ['br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ requestHeaders: {} /*optional, additional headers for report request */ - }, - stateIntervalMode: { - enabled: true, - interval: 10000, // 10 seconds - requestUrl: 'http://localhost:3000/cmcd_state', - requestMethod: 'POST', - enabledKeys: ['br', 'd', 'ot', 'tb'] } }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ diff --git a/src/core/Settings.js b/src/core/Settings.js index c9e8744b85..83d7998837 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1314,13 +1314,6 @@ function Settings() { requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'POST', requestHeaders: {} - }, - stateIntervalMode: { - enabled: false, - interval: 30000, - requestUrl: 'http://localhost:3000/cmcd_server', - requestMethod: 'POST', - enabledKeys: null } } }, diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 78988ebc2d..c7cc22f096 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -65,9 +65,6 @@ function CmcdModel() { _isStartup, _bufferLevelStarved, _initialMediaRequestsDone; - - let stateIntervalData = []; - let stateIntervalTimer = null; let context = this.context; let eventBus = EventBus(context).getInstance(); @@ -86,9 +83,6 @@ function CmcdModel() { eventBus.on(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance); eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); eventBus.on(MediaPlayerEvents.PERIOD_SWITCH_COMPLETED, _onPeriodSwitchComplete, instance); - // TODO: Add more player states - eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('playing'), instance); - eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('paused'), instance); } function setConfig(config) { @@ -201,9 +195,6 @@ function CmcdModel() { enabledCMCDKeys.push(key); } }); - } else if (cmcdReportingMode === 3) { - enabledCMCDKeys = settings.get().streaming.cmcd.reporting.stateIntervalMode.enabledKeys ? settings.get().streaming.cmcd.reporting.stateIntervalMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; - // TODO: Add required keys } return Object.keys(cmcdData) .filter(key => enabledCMCDKeys.includes(key)) @@ -216,74 +207,6 @@ function CmcdModel() { } } - function handleStateIntervalData(request) { - try { - if (isCmcdEnabled()) { - const cmcdData = getCmcdData(request); - const filteredCmcdData = _applyWhitelist(cmcdData, 3); - if (filteredCmcdData) { - stateIntervalData.push(filteredCmcdData); - } - if (!stateIntervalTimer) { - stateIntervalTimer = setTimeout(_sendStateIntervalData, settings.get().streaming.cmcd.reporting.stateIntervalMode.interval); - } - } - } catch (e) { - return null; - } - } - - function _sendStateIntervalData() { - const cmcdStateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; - if (cmcdStateIntervalMode.enabled && stateIntervalData.length > 0) { - const aggregatedData = _mergeCmcdData(stateIntervalData); - - // TODO: Check mandatory keys - const payload = { - ts: Date.now(), - cmcdData: aggregatedData // Array of CMCD data objects - }; - - fetch(cmcdStateIntervalMode.requestUrl, { - method: cmcdStateIntervalMode.requestMethod, - headers: { - ...cmcdStateIntervalMode.requestHeaders, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(payload) - }).then(response => { - console.log('State-interval CMCD data sent successfully:', response); - }).catch(error => { - console.error('Error sending state-interval CMCD data:', error); - }).finally(() => { - stateIntervalData = []; // Reset the data array - stateIntervalTimer = null; // Clear the timer - }); - } - } - - function _mergeCmcdData(dataArray) { - // TODO: Map configured keys - return dataArray. - filter(data => Object.keys(data).length > 0). - map(data => { - return { - br: data.br, - d: data.d, - ot: data.ot, - tb: data.tb, - }; - }); - } - - function _onStateChange(state) { - console.log(`Player state changed to: ${state}`) - const stateData = { sta: state, ts: Date.now() }; - stateIntervalData.push(stateData); - _sendStateIntervalData(); - } - - function getHeaderParameters(request, triggerEvent = true, cmcdReportingMode = null) { try { if (isCmcdEnabled()) { @@ -793,8 +716,7 @@ function CmcdModel() { eventBus.off(MediaPlayerEvents.MANIFEST_LOADED, _onManifestLoaded, this); eventBus.off(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance); eventBus.off(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); - eventBus.off(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('playing'), instance); - eventBus.off(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('paused'), instance); + _resetInitialSettings(); } @@ -807,7 +729,6 @@ function CmcdModel() { reset, initialize, isCmcdEnabled, - handleStateIntervalData }; setup(); diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 812d71ce81..4e22edf4a1 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -230,7 +230,7 @@ function HTTPLoader(cfg) { const _onRequestEnd = function (aborted = false) { if (httpRequest.customData.request.cmcdVersion === 2) { const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; - if (cmcdResponseMode && cmcdResponseMode.enabled){ + if (cmcdResponseMode.enabled){ fetch(cmcdResponseMode.requestUrl, { method: cmcdResponseMode.requestMethod, headers: cmcdResponseMode.requestHeaders, @@ -632,7 +632,6 @@ function HTTPLoader(cfg) { } else if (cmcdVersion === 2){ const cmcdRequestMode = settings.get().streaming.cmcd.reporting.requestMode; const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - const stateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; // Needs to be called to trigger the CMCD_DATA_GENERATED event only once cmcdModel.getHeaderParameters(request); @@ -658,13 +657,8 @@ function HTTPLoader(cfg) { request.cmcdResponseMode.requestHeaders = { ...cmcdResponseMode.requestHeaders, ...cmcdHeaders}; } } - - if (stateIntervalMode.enabled){ - // const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; - // Only JSON mode for now - cmcdModel.handleStateIntervalData(request) - } } + // TODO: Add State-Interval Mode } } From bfab35fb1aae2ddfbe1de780907f9da9492c1f59 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Mon, 26 Aug 2024 18:28:44 -0300 Subject: [PATCH 19/82] Send cmcd data after player state change --- samples/advanced/cmcd.html | 10 +++- src/core/Settings.js | 8 +++ src/streaming/constants/Constants.js | 2 +- src/streaming/models/CmcdModel.js | 83 +++++++++++++++++++++++++++- src/streaming/net/HTTPLoader.js | 3 +- 5 files changed, 100 insertions(+), 6 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 7db056c718..24a1ac890b 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -54,12 +54,20 @@ mode: CMCD_MODE_HEADER, /*overrides global mode */ }, responseMode: { - enabled: true, /* enable cmcdv2 response mode */ + enabled: false, /* enable cmcdv2 response mode */ mode: CMCD_MODE_QUERY, /*overrides global mode */ enabledKeys: ['br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ requestHeaders: {} /*optional, additional headers for report request */ + }, + stateIntervalMode: { + enabled: true, + mode: CMCD_MODE_QUERY, + interval: 10000, + requestUrl: 'http://localhost:3000/cmcd_server', + requestMethod: 'POST', + enabledKeys: ['v'] } }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ diff --git a/src/core/Settings.js b/src/core/Settings.js index 83d7998837..4d342738f8 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1314,6 +1314,14 @@ function Settings() { requestUrl: 'http://localhost:3000/cmcd_server', requestMethod: 'POST', requestHeaders: {} + }, + stateIntervalMode: { + enabled: false, + mode: null, + interval: 30000, + requestUrl: 'http://localhost:3000/cmcd_server', + requestMethod: 'POST', + enabledKeys: null } } }, diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index d7e92b643e..76112bd197 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -220,7 +220,7 @@ export default { * @memberof Constants# * @static */ - CMCD_AVAILABLE_KEYS: ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'ts', 'url'], + CMCD_AVAILABLE_KEYS: ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'ts', 'url', 'sta'], /** * @constant {string} CMCD_AVAILABLE_REQUESTS specifies all the availables requests type for CMCD metrics. diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index c7cc22f096..ab140bfbcf 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -83,6 +83,8 @@ function CmcdModel() { eventBus.on(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance); eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); eventBus.on(MediaPlayerEvents.PERIOD_SWITCH_COMPLETED, _onPeriodSwitchComplete, instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('playing'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('paused'), instance); } function setConfig(config) { @@ -131,6 +133,40 @@ function CmcdModel() { _updateStreamProcessors(); } + function _onStateChange(state) { + const cmcdVersion = settings.get().streaming.cmcd.version; + if (isCmcdEnabled() && cmcdVersion === 2) { + const cmcdStateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; + if (cmcdStateIntervalMode && cmcdStateIntervalMode.enabled) { + const cmcdData = _getStateIntervalCmcdData(state); + const filteredCmcdData = _applyWhitelist(cmcdData, 3); + + var requestUrl = cmcdStateIntervalMode.requestUrl; + var headers = {} + + if (cmcdStateIntervalMode.mode === Constants.CMCD_MODE_QUERY) { + const additionalQueryParameter = []; + const cmcdQueryParams = encodeCmcd(filteredCmcdData); + if (cmcdQueryParams) { + additionalQueryParameter.push({key: CMCD_PARAM, value: cmcdQueryParams}); + } + requestUrl = Utils.addAditionalQueryParameterToUrl(requestUrl, additionalQueryParameter); + } else if (cmcdStateIntervalMode.mode === Constants.CMCD_MODE_HEADER) { + headers = toCmcdHeaders(filteredCmcdData) + } + + fetch(requestUrl, { + method: cmcdStateIntervalMode.requestMethod, + headers: headers + }).then(response => { + console.log('State-interval CMCD data sent successfully:', response); + }).catch(error => { + console.error('Error sending state-interval CMCD data:', error); + }); + } + } + } + function _updateStreamProcessors() { if (!playbackController) { return; @@ -195,6 +231,15 @@ function CmcdModel() { enabledCMCDKeys.push(key); } }); + } else if (cmcdReportingMode === 3) { + enabledCMCDKeys = settings.get().streaming.cmcd.reporting.stateIntervalMode.enabledKeys ? settings.get().streaming.cmcd.reporting.stateIntervalMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; + // Add CMCD v2 State-interval mode mandatory keys + const requiredKeys = ['sta']; + requiredKeys.forEach(key => { + if (!enabledCMCDKeys.includes(key)) { + enabledCMCDKeys.push(key); + } + }); } return Object.keys(cmcdData) .filter(key => enabledCMCDKeys.includes(key)) @@ -516,7 +561,8 @@ function CmcdModel() { let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid; cid = cmcdParametersFromManifest.contentID ? cmcdParametersFromManifest.contentID : cid; - data.v = CMCD_VERSION; + const cmcdVersion = settings.get().streaming.cmcd.version; + data.v = cmcdVersion === 2 ? 2 : CMCD_VERSION; data.sid = settings.get().streaming.cmcd.sid ? settings.get().streaming.cmcd.sid : internalData.sid; data.sid = cmcdParametersFromManifest.sessionID ? cmcdParametersFromManifest.sessionID : data.sid; @@ -540,7 +586,6 @@ function CmcdModel() { } // Add v2 mandatory keys - const cmcdVersion = settings.get().streaming.cmcd.version; const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; if (cmcdVersion === 2 && cmcdResponseMode.enabled) { data.url = request.url.split('?')[0]; // remove potential cmcd query params @@ -551,6 +596,40 @@ function CmcdModel() { return data; } + function _getStateIntervalCmcdData(state) { + const data = {}; + + // Adds mandatory state key + data.sta = state; + + let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid; + + const cmcdVersion = settings.get().streaming.cmcd.version; + data.v = cmcdVersion === 2 ? 2 : CMCD_VERSION; + + data.sid = settings.get().streaming.cmcd.sid ? settings.get().streaming.cmcd.sid : internalData.sid; + + data.sid = `${data.sid}`; + + if (cid) { + data.cid = `${cid}`; + } + + if (!isNaN(internalData.pr) && internalData.pr !== 1 && internalData.pr !== null) { + data.pr = internalData.pr; + } + + if (internalData.st) { + data.st = internalData.st; + } + + if (internalData.sf) { + data.sf = internalData.sf; + } + + return data; + } + function _getBitrateByRequest(request) { try { return parseInt(request.bandwidth / 1000); diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 4e22edf4a1..d1802486d1 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -230,7 +230,7 @@ function HTTPLoader(cfg) { const _onRequestEnd = function (aborted = false) { if (httpRequest.customData.request.cmcdVersion === 2) { const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; - if (cmcdResponseMode.enabled){ + if (cmcdResponseMode && cmcdResponseMode.enabled){ fetch(cmcdResponseMode.requestUrl, { method: cmcdResponseMode.requestMethod, headers: cmcdResponseMode.requestHeaders, @@ -658,7 +658,6 @@ function HTTPLoader(cfg) { } } } - // TODO: Add State-Interval Mode } } From 42b1d4fe663b7e8f2c64d413eb39e843fd39573d Mon Sep 17 00:00:00 2001 From: sannti97 Date: Mon, 26 Aug 2024 19:02:48 -0300 Subject: [PATCH 20/82] Send cmcd data every x seconds --- src/streaming/models/CmcdModel.js | 81 +++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index ab140bfbcf..09335ebf52 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -71,6 +71,9 @@ function CmcdModel() { let settings = Settings(context).getInstance(); let debug = Debug(context).getInstance(); + // eslint-disable-next-line + let intervalTimer; + function setup() { dashManifestModel = DashManifestModel(context).getInstance(); logger = debug.getLogger(instance); @@ -85,6 +88,14 @@ function CmcdModel() { eventBus.on(MediaPlayerEvents.PERIOD_SWITCH_COMPLETED, _onPeriodSwitchComplete, instance); eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('playing'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('paused'), instance); + + const cmcdStateIntervalMode = _getCmcdStateIntervalData(); + if (cmcdStateIntervalMode){ + const interval = settings.get().streaming.cmcd.reporting.stateIntervalMode.interval; + if (interval !== 0) { + _startCmcdStateIntervalTimer(interval, cmcdStateIntervalMode); + } + } } function setConfig(config) { @@ -134,37 +145,57 @@ function CmcdModel() { } function _onStateChange(state) { + const cmcdStateIntervalMode = _getCmcdStateIntervalData(); + if (cmcdStateIntervalMode){ + _sendCmcdStateIntervalData(state, cmcdStateIntervalMode); + } + } + + function _sendCmcdStateIntervalData(state, cmcdStateIntervalMode) { + const cmcdData = _getStateIntervalCmcdData(state); + const filteredCmcdData = _applyWhitelist(cmcdData, 3); + + var requestUrl = cmcdStateIntervalMode.requestUrl; + var headers = {} + + if (cmcdStateIntervalMode.mode === Constants.CMCD_MODE_QUERY) { + const additionalQueryParameter = []; + const cmcdQueryParams = encodeCmcd(filteredCmcdData); + if (cmcdQueryParams) { + additionalQueryParameter.push({key: CMCD_PARAM, value: cmcdQueryParams}); + } + requestUrl = Utils.addAditionalQueryParameterToUrl(requestUrl, additionalQueryParameter); + } else if (cmcdStateIntervalMode.mode === Constants.CMCD_MODE_HEADER) { + headers = toCmcdHeaders(filteredCmcdData) + } + + fetch(requestUrl, { + method: cmcdStateIntervalMode.requestMethod, + headers: headers + }).then(response => { + console.log('State-interval CMCD data sent successfully:', response); + }).catch(error => { + console.error('Error sending state-interval CMCD data:', error); + }); + } + + function _startCmcdStateIntervalTimer(interval, stateIntervalMode) { + intervalTimer = setTimeout(() => { + _sendCmcdStateIntervalData(interval, stateIntervalMode) + // Restart the timer + _startCmcdStateIntervalTimer(interval, stateIntervalMode); + }, interval); + } + + function _getCmcdStateIntervalData() { const cmcdVersion = settings.get().streaming.cmcd.version; if (isCmcdEnabled() && cmcdVersion === 2) { const cmcdStateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; if (cmcdStateIntervalMode && cmcdStateIntervalMode.enabled) { - const cmcdData = _getStateIntervalCmcdData(state); - const filteredCmcdData = _applyWhitelist(cmcdData, 3); - - var requestUrl = cmcdStateIntervalMode.requestUrl; - var headers = {} - - if (cmcdStateIntervalMode.mode === Constants.CMCD_MODE_QUERY) { - const additionalQueryParameter = []; - const cmcdQueryParams = encodeCmcd(filteredCmcdData); - if (cmcdQueryParams) { - additionalQueryParameter.push({key: CMCD_PARAM, value: cmcdQueryParams}); - } - requestUrl = Utils.addAditionalQueryParameterToUrl(requestUrl, additionalQueryParameter); - } else if (cmcdStateIntervalMode.mode === Constants.CMCD_MODE_HEADER) { - headers = toCmcdHeaders(filteredCmcdData) - } - - fetch(requestUrl, { - method: cmcdStateIntervalMode.requestMethod, - headers: headers - }).then(response => { - console.log('State-interval CMCD data sent successfully:', response); - }).catch(error => { - console.error('Error sending state-interval CMCD data:', error); - }); + return cmcdStateIntervalMode } } + return null } function _updateStreamProcessors() { From 8c70f9d31e17afc8fcfaffa008cc7e0a57914bb6 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Tue, 27 Aug 2024 17:29:51 -0300 Subject: [PATCH 21/82] Removed sta key when reporting after interval --- src/streaming/models/CmcdModel.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 09335ebf52..4530c9e86a 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -181,7 +181,7 @@ function CmcdModel() { function _startCmcdStateIntervalTimer(interval, stateIntervalMode) { intervalTimer = setTimeout(() => { - _sendCmcdStateIntervalData(interval, stateIntervalMode) + _sendCmcdStateIntervalData(null, stateIntervalMode) // Restart the timer _startCmcdStateIntervalTimer(interval, stateIntervalMode); }, interval); @@ -631,7 +631,9 @@ function CmcdModel() { const data = {}; // Adds mandatory state key - data.sta = state; + if (state) { + data.sta = state; + } let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid; From 2bb2f310fc611b066452452fa3dbd72d11c7cac8 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 28 Aug 2024 15:59:47 -0300 Subject: [PATCH 22/82] Remove unsopported mode 3 keys --- src/streaming/constants/Constants.js | 8 ++++++++ src/streaming/models/CmcdModel.js | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index 76112bd197..2b70d16986 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -222,6 +222,14 @@ export default { */ CMCD_AVAILABLE_KEYS: ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'ts', 'url', 'sta'], + /** + * @constant {string} CMCD_AVAILABLE_KEYS_STATE_INTERVAL specifies all the availables keys for the State-Interval Mode of CMCD v2. + * @memberof Constants# + * @static + * TODO: Confirm keys and create CMCD AVAILABLE KEYS arrays for CMCD v2 and Response Mode + */ + CMCD_AVAILABLE_KEYS_STATE_INTERVAL: ['sta', 'sid', 'sf', 'st', 'v', 'ts', 'mtp', 'msd', 'lb', 'tb', 'tab', 'lab'], + /** * @constant {string} CMCD_AVAILABLE_REQUESTS specifies all the availables requests type for CMCD metrics. * @memberof Constants# diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 4530c9e86a..05aa907818 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -264,6 +264,8 @@ function CmcdModel() { }); } else if (cmcdReportingMode === 3) { enabledCMCDKeys = settings.get().streaming.cmcd.reporting.stateIntervalMode.enabledKeys ? settings.get().streaming.cmcd.reporting.stateIntervalMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; + // Remove unsupported State-Interval mode keys + enabledCMCDKeys = enabledCMCDKeys.filter(key => Constants.CMCD_AVAILABLE_KEYS_STATE_INTERVAL.includes(key)); // Add CMCD v2 State-interval mode mandatory keys const requiredKeys = ['sta']; requiredKeys.forEach(key => { From b36f34754e3458eaf60186bedb445cb384987c28 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 28 Aug 2024 16:46:25 -0300 Subject: [PATCH 23/82] Added comments to state interval conf --- samples/advanced/cmcd.html | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 24a1ac890b..d48e7d845e 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -51,23 +51,23 @@ reporting: { requestMode: { enabled: true, /* enable cmcdv2 request mode */ - mode: CMCD_MODE_HEADER, /*overrides global mode */ + mode: CMCD_MODE_HEADER, /* overrides global mode */ }, responseMode: { - enabled: false, /* enable cmcdv2 response mode */ - mode: CMCD_MODE_QUERY, /*overrides global mode */ + enabled: true, /* enable cmcdv2 response mode. FALSE by default */ + mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ enabledKeys: ['br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ - requestHeaders: {} /*optional, additional headers for report request */ + requestHeaders: {} /* optional, additional headers for report request */ }, stateIntervalMode: { - enabled: true, - mode: CMCD_MODE_QUERY, - interval: 10000, - requestUrl: 'http://localhost:3000/cmcd_server', - requestMethod: 'POST', - enabledKeys: ['v'] + enabled: true, /* enable cmcdv2 state-interval mode. FALSE by default */ + mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ + interval: 10000, /* time in ms between regular requests. Default 30s or 0 to omit */ + enabledKeys: ['v', 'url'], /* optional, overrides global keys */ + requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ + requestMethod: 'POST', /* optional, get by default */ } }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ From f1d7686982bbf88ed3330303295b723cddf9dfb1 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 28 Aug 2024 16:58:09 -0300 Subject: [PATCH 24/82] Removed requestHeaders optional config --- samples/advanced/cmcd.html | 1 - src/core/Settings.js | 3 +-- src/streaming/net/HTTPLoader.js | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index d48e7d845e..09f1bea235 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -59,7 +59,6 @@ enabledKeys: ['br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ - requestHeaders: {} /* optional, additional headers for report request */ }, stateIntervalMode: { enabled: true, /* enable cmcdv2 state-interval mode. FALSE by default */ diff --git a/src/core/Settings.js b/src/core/Settings.js index 4d342738f8..6c74106adf 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -1312,8 +1312,7 @@ function Settings() { mode: null, enabledKeys: null, requestUrl: 'http://localhost:3000/cmcd_server', - requestMethod: 'POST', - requestHeaders: {} + requestMethod: 'POST' }, stateIntervalMode: { enabled: false, diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index d1802486d1..ccabd8e7dd 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -654,7 +654,7 @@ function HTTPLoader(cfg) { request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); } else if (cmcdMode === Constants.CMCD_MODE_HEADER){ const cmcdHeaders = cmcdModel.getHeaderParameters(request, false, 2); - request.cmcdResponseMode.requestHeaders = { ...cmcdResponseMode.requestHeaders, ...cmcdHeaders}; + request.cmcdResponseMode.requestHeaders = cmcdHeaders; } } } From 83d66166e388a15b20c6480860b9ffb8710728f5 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 28 Aug 2024 17:32:27 -0300 Subject: [PATCH 25/82] Added more playback events --- docs/cmcd-v2/reporting-modes.md | 7 ++++++- src/streaming/models/CmcdModel.js | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/cmcd-v2/reporting-modes.md b/docs/cmcd-v2/reporting-modes.md index 49ce59bc23..b4890ed182 100644 --- a/docs/cmcd-v2/reporting-modes.md +++ b/docs/cmcd-v2/reporting-modes.md @@ -9,4 +9,9 @@ Here are notes about the challenge of implementing the new reporting modes of CM - Observations: - The mode that already exists in the configuration for the "transmition mode" might be confusing with the new mode for the "reporting mode". - - The ‘ts’ key is not entirely accurate since the CMCD parameters are generated before the request starts. \ No newline at end of file + - The ‘ts’ key is not entirely accurate since the CMCD parameters are generated before the request starts. + + +### State-Interval Mode + +- Should the state be set to rebuffering when the video buffer gets stalled? or also the audio buffer? \ No newline at end of file diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 05aa907818..edb2c7f695 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -86,8 +86,12 @@ function CmcdModel() { eventBus.on(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance); eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); eventBus.on(MediaPlayerEvents.PERIOD_SWITCH_COMPLETED, _onPeriodSwitchComplete, instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_INITIALIZED, () => _onStateChange('starting'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('playing'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('paused'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('seeking'), instance); + eventBus.on(MediaPlayerEvents.BUFFER_EMPTY, () => _onStateChange('rebuffering'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_ERROR, () => _onStateChange('error'), instance); const cmcdStateIntervalMode = _getCmcdStateIntervalData(); if (cmcdStateIntervalMode){ From f3dfbaf9f94f8139589edfb359156cf8e28dd942 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 28 Aug 2024 18:44:46 -0300 Subject: [PATCH 26/82] Improved version handling in cmcdModel --- samples/advanced/cmcd.html | 8 ++++---- src/streaming/models/CmcdModel.js | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 09f1bea235..cd28632772 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -56,16 +56,16 @@ responseMode: { enabled: true, /* enable cmcdv2 response mode. FALSE by default */ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ - enabledKeys: ['br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ - requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ + enabledKeys: ['sid', 'cid', 'br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ + requestUrl: 'http://localhost:3000/cmcd_response_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ }, stateIntervalMode: { enabled: true, /* enable cmcdv2 state-interval mode. FALSE by default */ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ interval: 10000, /* time in ms between regular requests. Default 30s or 0 to omit */ - enabledKeys: ['v', 'url'], /* optional, overrides global keys */ - requestUrl: 'http://localhost:3000/cmcd_server', /*mandatory, URL to send report */ + enabledKeys: ['sid', 'v', 'sf'], /* optional, overrides global keys */ + requestUrl: 'http://localhost:3000/cmcd_state_server', /*mandatory, URL to send report */ requestMethod: 'POST', /* optional, get by default */ } }, diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index edb2c7f695..3661ca994a 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -71,9 +71,6 @@ function CmcdModel() { let settings = Settings(context).getInstance(); let debug = Debug(context).getInstance(); - // eslint-disable-next-line - let intervalTimer; - function setup() { dashManifestModel = DashManifestModel(context).getInstance(); logger = debug.getLogger(instance); @@ -142,6 +139,7 @@ function CmcdModel() { _initialMediaRequestsDone = {}; _lastMediaTypeRequest = undefined; _updateStreamProcessors(); + _setCmcdVersion(); } function _onPeriodSwitchComplete() { @@ -184,7 +182,7 @@ function CmcdModel() { } function _startCmcdStateIntervalTimer(interval, stateIntervalMode) { - intervalTimer = setTimeout(() => { + setTimeout(() => { _sendCmcdStateIntervalData(null, stateIntervalMode) // Restart the timer _startCmcdStateIntervalTimer(interval, stateIntervalMode); @@ -192,8 +190,7 @@ function CmcdModel() { } function _getCmcdStateIntervalData() { - const cmcdVersion = settings.get().streaming.cmcd.version; - if (isCmcdEnabled() && cmcdVersion === 2) { + if (isCmcdEnabled() && internalData.v === 2) { const cmcdStateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; if (cmcdStateIntervalMode && cmcdStateIntervalMode.enabled) { return cmcdStateIntervalMode @@ -372,6 +369,11 @@ function CmcdModel() { return true; } + function _setCmcdVersion() { + const cmcdVersion = settings.get().streaming.cmcd?.version + internalData.v = cmcdVersion ? cmcdVersion : CMCD_VERSION; + } + function getCmcdParametersFromManifest() { let cmcdParametersFromManifest = {}; if (serviceDescriptionController) { @@ -598,8 +600,7 @@ function CmcdModel() { let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid; cid = cmcdParametersFromManifest.contentID ? cmcdParametersFromManifest.contentID : cid; - const cmcdVersion = settings.get().streaming.cmcd.version; - data.v = cmcdVersion === 2 ? 2 : CMCD_VERSION; + data.v = internalData.v === 2 ? 2 : CMCD_VERSION; data.sid = settings.get().streaming.cmcd.sid ? settings.get().streaming.cmcd.sid : internalData.sid; data.sid = cmcdParametersFromManifest.sessionID ? cmcdParametersFromManifest.sessionID : data.sid; @@ -624,7 +625,7 @@ function CmcdModel() { // Add v2 mandatory keys const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - if (cmcdVersion === 2 && cmcdResponseMode.enabled) { + if (internalData.v === 2 && cmcdResponseMode.enabled) { data.url = request.url.split('?')[0]; // remove potential cmcd query params // TODO: This key needs to be generated when loading the media request in _loadRequest data.ts = Date.now(); @@ -643,8 +644,7 @@ function CmcdModel() { let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid; - const cmcdVersion = settings.get().streaming.cmcd.version; - data.v = cmcdVersion === 2 ? 2 : CMCD_VERSION; + data.v = internalData.v === 2 ? 2 : CMCD_VERSION; data.sid = settings.get().streaming.cmcd.sid ? settings.get().streaming.cmcd.sid : internalData.sid; From 81502a2da299937d795a90c6468264c23e3e6c96 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 28 Aug 2024 18:48:17 -0300 Subject: [PATCH 27/82] Added cid to cmcd keys state itnerval --- samples/advanced/cmcd.html | 2 +- src/streaming/constants/Constants.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index cd28632772..514953e0f2 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -64,7 +64,7 @@ enabled: true, /* enable cmcdv2 state-interval mode. FALSE by default */ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ interval: 10000, /* time in ms between regular requests. Default 30s or 0 to omit */ - enabledKeys: ['sid', 'v', 'sf'], /* optional, overrides global keys */ + enabledKeys: ['sid', 'cid', 'v', 'sf'], /* optional, overrides global keys */ requestUrl: 'http://localhost:3000/cmcd_state_server', /*mandatory, URL to send report */ requestMethod: 'POST', /* optional, get by default */ } diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index 2b70d16986..41a1b8d531 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -228,7 +228,7 @@ export default { * @static * TODO: Confirm keys and create CMCD AVAILABLE KEYS arrays for CMCD v2 and Response Mode */ - CMCD_AVAILABLE_KEYS_STATE_INTERVAL: ['sta', 'sid', 'sf', 'st', 'v', 'ts', 'mtp', 'msd', 'lb', 'tb', 'tab', 'lab'], + CMCD_AVAILABLE_KEYS_STATE_INTERVAL: ['sta', 'sid', 'cid', 'sf', 'st', 'v', 'ts', 'mtp', 'msd', 'lb', 'tb', 'tab', 'lab'], /** * @constant {string} CMCD_AVAILABLE_REQUESTS specifies all the availables requests type for CMCD metrics. From d08d600f66c172f861b8db5ce1f99afc05e9c83b Mon Sep 17 00:00:00 2001 From: sannti97 Date: Wed, 28 Aug 2024 19:04:58 -0300 Subject: [PATCH 28/82] Fixed comment in state config --- samples/advanced/cmcd.html | 4 ++-- src/streaming/constants/Constants.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 514953e0f2..bff59b2e51 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -56,7 +56,7 @@ responseMode: { enabled: true, /* enable cmcdv2 response mode. FALSE by default */ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ - enabledKeys: ['sid', 'cid', 'br', 'd', 'ot', 'tb'], /*optional, overrides global keys */ + enabledKeys: ['sid', 'cid', 'br', 'd', 'ot', 'tb', 'v'], /*optional, overrides global keys */ requestUrl: 'http://localhost:3000/cmcd_response_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ }, @@ -64,7 +64,7 @@ enabled: true, /* enable cmcdv2 state-interval mode. FALSE by default */ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ interval: 10000, /* time in ms between regular requests. Default 30s or 0 to omit */ - enabledKeys: ['sid', 'cid', 'v', 'sf'], /* optional, overrides global keys */ + enabledKeys: ['sta', 'sid', 'cid', 'sf', 'v'], /* Currently only supports: 'sta', 'sid', 'cid', 'sf', 'v', 'lb', 'pr' */ requestUrl: 'http://localhost:3000/cmcd_state_server', /*mandatory, URL to send report */ requestMethod: 'POST', /* optional, get by default */ } diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index 41a1b8d531..b38fd73ee9 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -227,8 +227,9 @@ export default { * @memberof Constants# * @static * TODO: Confirm keys and create CMCD AVAILABLE KEYS arrays for CMCD v2 and Response Mode + * TODO: Add support for more keys */ - CMCD_AVAILABLE_KEYS_STATE_INTERVAL: ['sta', 'sid', 'cid', 'sf', 'st', 'v', 'ts', 'mtp', 'msd', 'lb', 'tb', 'tab', 'lab'], + CMCD_AVAILABLE_KEYS_STATE_INTERVAL: ['sta', 'sid', 'cid', 'sf', 'v', 'lb', 'pr'], /** * @constant {string} CMCD_AVAILABLE_REQUESTS specifies all the availables requests type for CMCD metrics. From 22d5557d77b186897d588a28acd9bbd65eccdbaa Mon Sep 17 00:00:00 2001 From: sannti97 Date: Thu, 29 Aug 2024 11:06:53 -0300 Subject: [PATCH 29/82] Removed _getStateIntervalCmcdData --- src/streaming/models/CmcdModel.js | 45 +++++-------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 3661ca994a..df67341a19 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -154,7 +154,7 @@ function CmcdModel() { } function _sendCmcdStateIntervalData(state, cmcdStateIntervalMode) { - const cmcdData = _getStateIntervalCmcdData(state); + const cmcdData = _getGenericCmcdData(null, state); const filteredCmcdData = _applyWhitelist(cmcdData, 3); var requestUrl = cmcdStateIntervalMode.requestUrl; @@ -593,10 +593,14 @@ function CmcdModel() { } - function _getGenericCmcdData(request) { + function _getGenericCmcdData(request, playerState = null) { const cmcdParametersFromManifest = getCmcdParametersFromManifest(); const data = {}; + if (playerState) { + data.sta = playerState; + } + let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid; cid = cmcdParametersFromManifest.contentID ? cmcdParametersFromManifest.contentID : cid; @@ -625,7 +629,7 @@ function CmcdModel() { // Add v2 mandatory keys const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - if (internalData.v === 2 && cmcdResponseMode.enabled) { + if (request && internalData.v === 2 && cmcdResponseMode.enabled) { data.url = request.url.split('?')[0]; // remove potential cmcd query params // TODO: This key needs to be generated when loading the media request in _loadRequest data.ts = Date.now(); @@ -634,41 +638,6 @@ function CmcdModel() { return data; } - function _getStateIntervalCmcdData(state) { - const data = {}; - - // Adds mandatory state key - if (state) { - data.sta = state; - } - - let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid; - - data.v = internalData.v === 2 ? 2 : CMCD_VERSION; - - data.sid = settings.get().streaming.cmcd.sid ? settings.get().streaming.cmcd.sid : internalData.sid; - - data.sid = `${data.sid}`; - - if (cid) { - data.cid = `${cid}`; - } - - if (!isNaN(internalData.pr) && internalData.pr !== 1 && internalData.pr !== null) { - data.pr = internalData.pr; - } - - if (internalData.st) { - data.st = internalData.st; - } - - if (internalData.sf) { - data.sf = internalData.sf; - } - - return data; - } - function _getBitrateByRequest(request) { try { return parseInt(request.bandwidth / 1000); From 5bd1914404a501043ce8987382a0c150ed8591ba Mon Sep 17 00:00:00 2001 From: sannti97 Date: Thu, 29 Aug 2024 15:53:24 -0300 Subject: [PATCH 30/82] Added ltc msd and ts keys --- samples/advanced/cmcd.html | 2 +- src/streaming/constants/Constants.js | 4 ++-- src/streaming/models/CmcdModel.js | 33 +++++++++++++++++++++++++--- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index bff59b2e51..28ccf9fc4c 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -64,7 +64,7 @@ enabled: true, /* enable cmcdv2 state-interval mode. FALSE by default */ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ interval: 10000, /* time in ms between regular requests. Default 30s or 0 to omit */ - enabledKeys: ['sta', 'sid', 'cid', 'sf', 'v'], /* Currently only supports: 'sta', 'sid', 'cid', 'sf', 'v', 'lb', 'pr' */ + enabledKeys: ['sta', 'sid', 'cid', 'sf', 'v', 'ltc', 'msd'], /* Currently only supports: 'sta', 'ts', 'sid', 'cid', 'sf', 'v', 'lb', 'pr' */ requestUrl: 'http://localhost:3000/cmcd_state_server', /*mandatory, URL to send report */ requestMethod: 'POST', /* optional, get by default */ } diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index b38fd73ee9..cc978d615e 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -220,7 +220,7 @@ export default { * @memberof Constants# * @static */ - CMCD_AVAILABLE_KEYS: ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'ts', 'url', 'sta'], + CMCD_AVAILABLE_KEYS: ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'ts', 'url', 'sta', 'ltc', 'msd'], /** * @constant {string} CMCD_AVAILABLE_KEYS_STATE_INTERVAL specifies all the availables keys for the State-Interval Mode of CMCD v2. @@ -229,7 +229,7 @@ export default { * TODO: Confirm keys and create CMCD AVAILABLE KEYS arrays for CMCD v2 and Response Mode * TODO: Add support for more keys */ - CMCD_AVAILABLE_KEYS_STATE_INTERVAL: ['sta', 'sid', 'cid', 'sf', 'v', 'lb', 'pr'], + CMCD_AVAILABLE_KEYS_STATE_INTERVAL: ['sta', 'ts', 'sid', 'cid', 'sf', 'v', 'lb', 'pr', 'ltc', 'msd'], /** * @constant {string} CMCD_AVAILABLE_REQUESTS specifies all the availables requests type for CMCD metrics. diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index df67341a19..d02bea8c0c 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -64,7 +64,8 @@ function CmcdModel() { _lastMediaTypeRequest, _isStartup, _bufferLevelStarved, - _initialMediaRequestsDone; + _initialMediaRequestsDone, + _playbackStartedTime; let context = this.context; let eventBus = EventBus(context).getInstance(); @@ -147,6 +148,17 @@ function CmcdModel() { } function _onStateChange(state) { + if (state === 'starting') { + if (!_playbackStartedTime) { + _playbackStartedTime = Date.now() + } + } + if (state === 'playing') { + if (!_playbackStartedTime || internalData.msd) { + return + } + internalData.msd = Date.now() - _playbackStartedTime + } const cmcdStateIntervalMode = _getCmcdStateIntervalData(); if (cmcdStateIntervalMode){ _sendCmcdStateIntervalData(state, cmcdStateIntervalMode); @@ -250,6 +262,7 @@ function CmcdModel() { var enabledCMCDKeys = cmcdParametersFromManifest.version ? cmcdParametersFromManifest.keys : settings.get().streaming.cmcd.enabledKeys; // For CMCD v2 use the reporting mode keys or global ones as default + // TODO: Try to improve this block by using _checkAvailableKeys(cmcdParametersFromManifest) if (cmcdReportingMode === 1){ enabledCMCDKeys = settings.get().streaming.cmcd.reporting.requestMode.enabledKeys ? settings.get().streaming.cmcd.reporting.requestMode.enabledKeys : settings.get().streaming.cmcd.enabledKeys; // Remove unsupported keys @@ -268,7 +281,7 @@ function CmcdModel() { // Remove unsupported State-Interval mode keys enabledCMCDKeys = enabledCMCDKeys.filter(key => Constants.CMCD_AVAILABLE_KEYS_STATE_INTERVAL.includes(key)); // Add CMCD v2 State-interval mode mandatory keys - const requiredKeys = ['sta']; + const requiredKeys = ['ts', 'sta']; requiredKeys.forEach(key => { if (!enabledCMCDKeys.includes(key)) { enabledCMCDKeys.push(key); @@ -615,6 +628,16 @@ function CmcdModel() { data.cid = `${cid}`; } + // Add new ltc and msd cmcd v2 keys + let ltc = playbackController.getCurrentLiveLatency() * 1000; + if (!isNaN(ltc)) { + data.ltc = ltc; + } + // TODO: Send this key only once for each reporting mode + if (!isNaN(internalData.msd)) { + data.msd = internalData.msd; + } + if (!isNaN(internalData.pr) && internalData.pr !== 1 && internalData.pr !== null) { data.pr = internalData.pr; } @@ -629,9 +652,13 @@ function CmcdModel() { // Add v2 mandatory keys const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; + const cmcdStateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; if (request && internalData.v === 2 && cmcdResponseMode.enabled) { data.url = request.url.split('?')[0]; // remove potential cmcd query params - // TODO: This key needs to be generated when loading the media request in _loadRequest + } + if (internalData.v === 2 && (cmcdResponseMode.enabled || cmcdStateIntervalMode.enabled)) { + // TODO: This key needs to be generated when loading the media request in _loadRequest for response Mode + // or in the onStateChange() for the state-interval mode data.ts = Date.now(); } From c7fcb25614941d72e3e102fb7ec1eb9dd9f06d23 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Thu, 29 Aug 2024 16:00:12 -0300 Subject: [PATCH 31/82] Fixed state names --- src/streaming/models/CmcdModel.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index d02bea8c0c..3e4bcc5001 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -84,12 +84,12 @@ function CmcdModel() { eventBus.on(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance); eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); eventBus.on(MediaPlayerEvents.PERIOD_SWITCH_COMPLETED, _onPeriodSwitchComplete, instance); - eventBus.on(MediaPlayerEvents.PLAYBACK_INITIALIZED, () => _onStateChange('starting'), instance); - eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('playing'), instance); - eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('paused'), instance); - eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('seeking'), instance); - eventBus.on(MediaPlayerEvents.BUFFER_EMPTY, () => _onStateChange('rebuffering'), instance); - eventBus.on(MediaPlayerEvents.PLAYBACK_ERROR, () => _onStateChange('error'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_INITIALIZED, () => _onStateChange('s'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('p'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('a'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKING, () => _onStateChange('k'), instance); + eventBus.on(MediaPlayerEvents.BUFFER_EMPTY, () => _onStateChange('r'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_ERROR, () => _onStateChange('e'), instance); const cmcdStateIntervalMode = _getCmcdStateIntervalData(); if (cmcdStateIntervalMode){ @@ -148,12 +148,12 @@ function CmcdModel() { } function _onStateChange(state) { - if (state === 'starting') { + if (state === 's') { if (!_playbackStartedTime) { _playbackStartedTime = Date.now() } } - if (state === 'playing') { + if (state === 'p') { if (!_playbackStartedTime || internalData.msd) { return } From ae19c83ee31012dd35fc48d75776b9da14508b99 Mon Sep 17 00:00:00 2001 From: sannti97 Date: Fri, 30 Aug 2024 13:14:26 -0300 Subject: [PATCH 32/82] fixed seeking state and improved state handling --- samples/advanced/cmcd.html | 6 ++--- src/streaming/models/CmcdModel.js | 38 +++++++++++++++++-------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 28ccf9fc4c..3d160b3f77 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -50,13 +50,13 @@ version: 2, /* 1 is the default version */ reporting: { requestMode: { - enabled: true, /* enable cmcdv2 request mode */ + enabled: false, /* enable cmcdv2 request mode */ mode: CMCD_MODE_HEADER, /* overrides global mode */ }, responseMode: { enabled: true, /* enable cmcdv2 response mode. FALSE by default */ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ - enabledKeys: ['sid', 'cid', 'br', 'd', 'ot', 'tb', 'v'], /*optional, overrides global keys */ + //enabledKeys: ['sid', 'cid', 'br', 'd', 'ot', 'tb', 'v', 'sta'], /*optional, overrides global keys */ requestUrl: 'http://localhost:3000/cmcd_response_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ }, @@ -72,7 +72,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, /* global mode if not specified in each mode */ - enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v'] + enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'sta', 'ltc', 'msd'] } } }); diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 3e4bcc5001..c872e8da39 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -85,11 +85,12 @@ function CmcdModel() { eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); eventBus.on(MediaPlayerEvents.PERIOD_SWITCH_COMPLETED, _onPeriodSwitchComplete, instance); eventBus.on(MediaPlayerEvents.PLAYBACK_INITIALIZED, () => _onStateChange('s'), instance); - eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING, () => _onStateChange('p'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_STARTED, () => _onStateChange('p'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('a'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKING, () => _onStateChange('k'), instance); - eventBus.on(MediaPlayerEvents.BUFFER_EMPTY, () => _onStateChange('r'), instance); - eventBus.on(MediaPlayerEvents.PLAYBACK_ERROR, () => _onStateChange('e'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_STALLED, () => _onStateChange('r'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_ERROR, () => _onStateChange('f'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_ENDED, () => _onStateChange('e'), instance); const cmcdStateIntervalMode = _getCmcdStateIntervalData(); if (cmcdStateIntervalMode){ @@ -133,7 +134,8 @@ function CmcdModel() { st: null, sf: null, sid: `${Utils.generateUuid()}`, - cid: null + cid: null, + sta: null }; _bufferLevelStarved = {}; _isStartup = {}; @@ -150,23 +152,25 @@ function CmcdModel() { function _onStateChange(state) { if (state === 's') { if (!_playbackStartedTime) { - _playbackStartedTime = Date.now() + _playbackStartedTime = Date.now(); } } if (state === 'p') { - if (!_playbackStartedTime || internalData.msd) { - return + if (_playbackStartedTime && !internalData.msd) { + internalData.msd = Date.now() - _playbackStartedTime; } - internalData.msd = Date.now() - _playbackStartedTime } - const cmcdStateIntervalMode = _getCmcdStateIntervalData(); - if (cmcdStateIntervalMode){ - _sendCmcdStateIntervalData(state, cmcdStateIntervalMode); + if (internalData.state !== state) { + const cmcdStateIntervalMode = _getCmcdStateIntervalData(); + internalData.sta = state; + if (cmcdStateIntervalMode){ + _sendCmcdStateIntervalData(cmcdStateIntervalMode); + } } } - function _sendCmcdStateIntervalData(state, cmcdStateIntervalMode) { - const cmcdData = _getGenericCmcdData(null, state); + function _sendCmcdStateIntervalData(cmcdStateIntervalMode) { + const cmcdData = _getGenericCmcdData(null); const filteredCmcdData = _applyWhitelist(cmcdData, 3); var requestUrl = cmcdStateIntervalMode.requestUrl; @@ -195,7 +199,7 @@ function CmcdModel() { function _startCmcdStateIntervalTimer(interval, stateIntervalMode) { setTimeout(() => { - _sendCmcdStateIntervalData(null, stateIntervalMode) + _sendCmcdStateIntervalData(stateIntervalMode) // Restart the timer _startCmcdStateIntervalTimer(interval, stateIntervalMode); }, interval); @@ -606,12 +610,12 @@ function CmcdModel() { } - function _getGenericCmcdData(request, playerState = null) { + function _getGenericCmcdData(request) { const cmcdParametersFromManifest = getCmcdParametersFromManifest(); const data = {}; - if (playerState) { - data.sta = playerState; + if (internalData.sta) { + data.sta = internalData.sta; } let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid; From 046a1c1bc94c8e372606a60813ab141eadd90d5f Mon Sep 17 00:00:00 2001 From: sannti97 Date: Fri, 30 Aug 2024 14:35:39 -0300 Subject: [PATCH 33/82] Refactored _updateRequestUrlAndHeadersWithCMCD --- samples/advanced/cmcd.html | 2 +- src/streaming/net/HTTPLoader.js | 90 ++++++++++++++------------------- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 3d160b3f77..c3287b2454 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -50,7 +50,7 @@ version: 2, /* 1 is the default version */ reporting: { requestMode: { - enabled: false, /* enable cmcdv2 request mode */ + enabled: true, /* enable cmcdv2 request mode */ mode: CMCD_MODE_HEADER, /* overrides global mode */ }, responseMode: { diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index ccabd8e7dd..2edb7ad314 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -228,18 +228,16 @@ function HTTPLoader(cfg) { }; const _onRequestEnd = function (aborted = false) { - if (httpRequest.customData.request.cmcdVersion === 2) { - const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; - if (cmcdResponseMode && cmcdResponseMode.enabled){ - fetch(cmcdResponseMode.requestUrl, { - method: cmcdResponseMode.requestMethod, - headers: cmcdResponseMode.requestHeaders, - }).then(response => { - console.log('CMCD data sent successfully:', response); - }).catch(error => { - console.error('Error sending CMCD data:', error); - }); - } + const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; + if (cmcdResponseMode && cmcdResponseMode.enabled){ + fetch(cmcdResponseMode.requestUrl, { + method: cmcdResponseMode.requestMethod, + headers: cmcdResponseMode.requestHeaders, + }).then(response => { + console.log('CMCD data sent successfully:', response); + }).catch(error => { + console.error('Error sending CMCD data:', error); + }); } // Remove the request from our list of requests @@ -617,46 +615,36 @@ function HTTPLoader(cfg) { const currentAdaptationSetId = request?.mediaInfo?.id?.toString(); const isIncludedFilters = clientDataReportingController.isServiceLocationIncluded(request.type, currentServiceLocation) && clientDataReportingController.isAdaptationsIncluded(currentAdaptationSetId); - if (isIncludedFilters && cmcdModel.isCmcdEnabled()) { - const cmcdVersion = settings.get().streaming.cmcd.version; - request.cmcdVersion = cmcdVersion; - if (cmcdVersion === 1){ - const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); - const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : settings.get().streaming.cmcd.mode; - if (cmcdMode === Constants.CMCD_MODE_QUERY) { - const additionalQueryParameter = _getAdditionalQueryParameter(request); - request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); - } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { - request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request)); - } - } else if (cmcdVersion === 2){ - const cmcdRequestMode = settings.get().streaming.cmcd.reporting.requestMode; - const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - // Needs to be called to trigger the CMCD_DATA_GENERATED event only once - cmcdModel.getHeaderParameters(request); - - if (cmcdRequestMode.enabled){ - const cmcdMode = cmcdRequestMode.mode ? cmcdRequestMode.mode : settings.get().streaming.cmcd.mode; - if (cmcdMode === Constants.CMCD_MODE_QUERY) { - const additionalQueryParameter = _getAdditionalQueryParameter(request, false, 1); - request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); - } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { - const cmcdHeaders = cmcdModel.getHeaderParameters(request, false, 1); - request.headers = Object.assign(request.headers, cmcdHeaders); - } - } + const cmcdRequestModeEnabled = settings.get().streaming.cmcd.reporting.requestMode.enabled; + if (isIncludedFilters && cmcdModel.isCmcdEnabled() && cmcdRequestModeEnabled) { + // Needs to be called to trigger the CMCD_DATA_GENERATED event only once + // TODO: Check how to generate the event only once + cmcdModel.getHeaderParameters(request); + const cmcdParameters = cmcdModel.getCmcdParametersFromManifest(); + const cmcdResponseMode = settings.get().streaming.cmcd.reporting.requestMode.mode; + const cmcdMode = cmcdParameters.mode ? cmcdParameters.mode : (cmcdResponseMode ? cmcdResponseMode : settings.get().streaming.cmcd.mode); + if (cmcdMode === Constants.CMCD_MODE_QUERY) { + const additionalQueryParameter = _getAdditionalQueryParameter(request, false, 1); + request.url = Utils.addAditionalQueryParameterToUrl(request.url, additionalQueryParameter); + } else if (cmcdMode === Constants.CMCD_MODE_HEADER) { + request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request, false, 1)); + } + } - if (cmcdResponseMode.enabled){ - request.cmcdResponseMode = cmcdResponseMode; - const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; - if (cmcdMode === Constants.CMCD_MODE_QUERY){ - const additionalQueryParameter = _getAdditionalQueryParameter(request, false, 2); - request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); - } else if (cmcdMode === Constants.CMCD_MODE_HEADER){ - const cmcdHeaders = cmcdModel.getHeaderParameters(request, false, 2); - request.cmcdResponseMode.requestHeaders = cmcdHeaders; - } - } + const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; + if (isIncludedFilters && cmcdModel.isCmcdEnabled() && cmcdResponseMode.enabled) { + // Needs to be called to trigger the CMCD_DATA_GENERATED event only once + // TODO: Check how to generate the event only once + cmcdModel.getHeaderParameters(request); + + request.cmcdResponseMode = cmcdResponseMode; + const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; + if (cmcdMode === Constants.CMCD_MODE_QUERY){ + const additionalQueryParameter = _getAdditionalQueryParameter(request, false, 2); + request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); + } else if (cmcdMode === Constants.CMCD_MODE_HEADER){ + const cmcdHeaders = cmcdModel.getHeaderParameters(request, false, 2); + request.cmcdResponseMode.requestHeaders = cmcdHeaders; } } } From 8582c1720efadd49feaae8ffb37af394c3137df6 Mon Sep 17 00:00:00 2001 From: Nicolas Levy Date: Fri, 30 Aug 2024 16:05:19 -0300 Subject: [PATCH 34/82] fix: msd, add keys in cmcd.html example --- samples/advanced/cmcd.html | 14 +++++++------- src/streaming/models/CmcdModel.js | 27 +++++++++++++++++++-------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index c3287b2454..b3be24888b 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -55,24 +55,24 @@ }, responseMode: { enabled: true, /* enable cmcdv2 response mode. FALSE by default */ - mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ - //enabledKeys: ['sid', 'cid', 'br', 'd', 'ot', 'tb', 'v', 'sta'], /*optional, overrides global keys */ - requestUrl: 'http://localhost:3000/cmcd_response_server', /*mandatory, URL to send report */ + mode: CMCD_MODE_HEADER, /* overrides global mode. JSON not supported */ + enabledKeys: ['sid', 'cid', 'ts', 'url', 'br', 'd', 'ot', 'tb', 'v', 'sta'], /* optional, overrides global keys */ + requestUrl: 'http://localhost:3100/cmcd_response_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ }, stateIntervalMode: { enabled: true, /* enable cmcdv2 state-interval mode. FALSE by default */ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ interval: 10000, /* time in ms between regular requests. Default 30s or 0 to omit */ - enabledKeys: ['sta', 'sid', 'cid', 'sf', 'v', 'ltc', 'msd'], /* Currently only supports: 'sta', 'ts', 'sid', 'cid', 'sf', 'v', 'lb', 'pr' */ - requestUrl: 'http://localhost:3000/cmcd_state_server', /*mandatory, URL to send report */ + enabledKeys: ['sta', 'sid', 'cid', 'sf', 'v', 'ltc', 'msd', 'sta'], /* Currently only supports: 'sta', 'ts', 'sid', 'cid', 'sf', 'v', 'lb', 'pr' */ + requestUrl: 'http://localhost:3100/cmcd_state_server', /* mandatory, URL to send report */ requestMethod: 'POST', /* optional, get by default */ } }, sid: 'b248658d-1d1a-4039-91d0-8c08ba597da5', /* session id send with each request */ cid: '21cf726cfe3d937b5f974f72bb5bd06a', /* content id send with each request */ mode: CMCD_MODE_QUERY, /* global mode if not specified in each mode */ - enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'sta', 'ltc', 'msd'] + enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v', 'sta', 'ltc', 'msd', 'sta'] } } }); @@ -90,7 +90,7 @@ if (cmcdVersion === 1) { data = mode === CMCD_MODE_HEADER ? getKeysForHeaderMode(event) : getKeysForQueryMode(event); } else if (cmcdVersion === 2){ - // Currently, headers are always generated in cmcd version 2 + /* Currently, headers are always generated in cmcd version 2 */ data = getKeysForHeaderMode(event); } var keys = Object.keys(data); diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index c872e8da39..aa61bc0fae 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -87,6 +87,7 @@ function CmcdModel() { eventBus.on(MediaPlayerEvents.PLAYBACK_INITIALIZED, () => _onStateChange('s'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_STARTED, () => _onStateChange('p'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('a'), instance); + eventBus.on(MediaPlayerEvents.PLAYBACK_PLAYING,() => _onStateChange('pl'), instance) eventBus.on(MediaPlayerEvents.PLAYBACK_SEEKING, () => _onStateChange('k'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_STALLED, () => _onStateChange('r'), instance); eventBus.on(MediaPlayerEvents.PLAYBACK_ERROR, () => _onStateChange('f'), instance); @@ -150,16 +151,17 @@ function CmcdModel() { } function _onStateChange(state) { - if (state === 's') { - if (!_playbackStartedTime) { + if (!internalData.msd){ + // Calculate msd key + if (!_playbackStartedTime && state === 'p'){ _playbackStartedTime = Date.now(); } - } - if (state === 'p') { - if (_playbackStartedTime && !internalData.msd) { - internalData.msd = Date.now() - _playbackStartedTime; + if (_playbackStartedTime && state === 'pl'){ + internalData.msd = Date.now() - _playbackStartedTime + return } } + if (internalData.state !== state) { const cmcdStateIntervalMode = _getCmcdStateIntervalData(); internalData.sta = state; @@ -830,10 +832,19 @@ function CmcdModel() { } function reset() { - eventBus.off(MediaPlayerEvents.PLAYBACK_RATE_CHANGED, _onPlaybackRateChanged, this); - eventBus.off(MediaPlayerEvents.MANIFEST_LOADED, _onManifestLoaded, this); + eventBus.off(MediaPlayerEvents.PLAYBACK_RATE_CHANGED, _onPlaybackRateChanged, instance); + eventBus.off(MediaPlayerEvents.MANIFEST_LOADED, _onManifestLoaded, instance); eventBus.off(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance); eventBus.off(MediaPlayerEvents.PLAYBACK_SEEKED, _onPlaybackSeeked, instance); + eventBus.off(MediaPlayerEvents.PERIOD_SWITCH_COMPLETED, _onPeriodSwitchComplete, instance); + eventBus.off(MediaPlayerEvents.PLAYBACK_INITIALIZED, () => _onStateChange('s'), instance); + eventBus.off(MediaPlayerEvents.PLAYBACK_STARTED, () => _onStateChange('p'), instance); + eventBus.off(MediaPlayerEvents.PLAYBACK_PAUSED, () => _onStateChange('a'), instance); + eventBus.off(MediaPlayerEvents.PLAYBACK_PLAYING,() => _onStateChange('pl'), instance) + eventBus.off(MediaPlayerEvents.PLAYBACK_SEEKING, () => _onStateChange('k'), instance); + eventBus.off(MediaPlayerEvents.PLAYBACK_STALLED, () => _onStateChange('r'), instance); + eventBus.off(MediaPlayerEvents.PLAYBACK_ERROR, () => _onStateChange('f'), instance); + eventBus.off(MediaPlayerEvents.PLAYBACK_ENDED, () => _onStateChange('e'), instance); _resetInitialSettings(); } From 488abf3919f756df4aa1923c822b0c2e61edd80d Mon Sep 17 00:00:00 2001 From: Nicolas Levy Date: Fri, 30 Aug 2024 17:45:35 -0300 Subject: [PATCH 35/82] added ttfb, ttlb and rc keys --- samples/advanced/cmcd.html | 6 ++-- src/streaming/models/CmcdModel.js | 11 ++++++ src/streaming/net/HTTPLoader.js | 57 +++++++++++++++---------------- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index b3be24888b..9bb577c267 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -56,8 +56,8 @@ responseMode: { enabled: true, /* enable cmcdv2 response mode. FALSE by default */ mode: CMCD_MODE_HEADER, /* overrides global mode. JSON not supported */ - enabledKeys: ['sid', 'cid', 'ts', 'url', 'br', 'd', 'ot', 'tb', 'v', 'sta'], /* optional, overrides global keys */ - requestUrl: 'http://localhost:3100/cmcd_response_server', /*mandatory, URL to send report */ + enabledKeys: ['sid', 'cid', 'ts', 'url', 'br', 'd', 'ot', 'tb', 'v', 'sta', 'ttfb', 'ttlb','rc'], /* optional, overrides global keys */ + requestUrl: 'http://localhost:3000/cmcd_response_server', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ }, stateIntervalMode: { @@ -65,7 +65,7 @@ mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ interval: 10000, /* time in ms between regular requests. Default 30s or 0 to omit */ enabledKeys: ['sta', 'sid', 'cid', 'sf', 'v', 'ltc', 'msd', 'sta'], /* Currently only supports: 'sta', 'ts', 'sid', 'cid', 'sf', 'v', 'lb', 'pr' */ - requestUrl: 'http://localhost:3100/cmcd_state_server', /* mandatory, URL to send report */ + requestUrl: 'http://localhost:3000/cmcd_state_server', /* mandatory, URL to send report */ requestMethod: 'POST', /* optional, get by default */ } }, diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index aa61bc0fae..e92179a87e 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -576,6 +576,17 @@ function CmcdModel() { _initialMediaRequestsDone[mediaType] = true; } + // Response timing and status code + if (request.startDate && request.firstByteDate){ + data.ttfb = request.firstByteDate - request.startDate + } + if (request.endDate && request.startDate){ + data.ttlb = request.endDate - request.startDate + } + if (request.status){ + data.rc = request.status + } + return data; } diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index 2edb7ad314..ddbdde573e 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -228,18 +228,6 @@ function HTTPLoader(cfg) { }; const _onRequestEnd = function (aborted = false) { - const cmcdResponseMode = httpRequest.customData.request.cmcdResponseMode; - if (cmcdResponseMode && cmcdResponseMode.enabled){ - fetch(cmcdResponseMode.requestUrl, { - method: cmcdResponseMode.requestMethod, - headers: cmcdResponseMode.requestHeaders, - }).then(response => { - console.log('CMCD data sent successfully:', response); - }).catch(error => { - console.error('Error sending CMCD data:', error); - }); - } - // Remove the request from our list of requests if (httpRequests.indexOf(httpRequest) !== -1) { httpRequests.splice(httpRequests.indexOf(httpRequest), 1); @@ -298,6 +286,33 @@ function HTTPLoader(cfg) { } }); + /* CMCD V2 ResponseMode */ + const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; + if (cmcdModel.isCmcdEnabled() && cmcdResponseMode.enabled) { + + const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; + let requestUrl = cmcdResponseMode.requestUrl; + let requestHeaderes = null; + const request = httpRequest.customData.request; + request.status = httpResponse.status + + if (cmcdMode === Constants.CMCD_MODE_QUERY){ + const additionalQueryParameter = _getAdditionalQueryParameter(request, false, 2); + requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); + } else if (cmcdMode === Constants.CMCD_MODE_HEADER){ + requestHeaderes = cmcdModel.getHeaderParameters(request, false, 2); + } + + fetch(requestUrl, { + method: cmcdResponseMode.requestMethod, + headers: requestHeaderes, + }).then(response => { + console.log('CMCD data sent successfully:', response); + }).catch(error => { + console.error('Error sending CMCD data:', error); + }); + } + }; const _updateRequestTimingInfo = function () { @@ -616,6 +631,7 @@ function HTTPLoader(cfg) { const isIncludedFilters = clientDataReportingController.isServiceLocationIncluded(request.type, currentServiceLocation) && clientDataReportingController.isAdaptationsIncluded(currentAdaptationSetId); const cmcdRequestModeEnabled = settings.get().streaming.cmcd.reporting.requestMode.enabled; + if (isIncludedFilters && cmcdModel.isCmcdEnabled() && cmcdRequestModeEnabled) { // Needs to be called to trigger the CMCD_DATA_GENERATED event only once // TODO: Check how to generate the event only once @@ -630,23 +646,6 @@ function HTTPLoader(cfg) { request.headers = Object.assign(request.headers, cmcdModel.getHeaderParameters(request, false, 1)); } } - - const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - if (isIncludedFilters && cmcdModel.isCmcdEnabled() && cmcdResponseMode.enabled) { - // Needs to be called to trigger the CMCD_DATA_GENERATED event only once - // TODO: Check how to generate the event only once - cmcdModel.getHeaderParameters(request); - - request.cmcdResponseMode = cmcdResponseMode; - const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; - if (cmcdMode === Constants.CMCD_MODE_QUERY){ - const additionalQueryParameter = _getAdditionalQueryParameter(request, false, 2); - request.cmcdResponseMode.requestUrl = Utils.addAditionalQueryParameterToUrl(cmcdResponseMode.requestUrl, additionalQueryParameter); - } else if (cmcdMode === Constants.CMCD_MODE_HEADER){ - const cmcdHeaders = cmcdModel.getHeaderParameters(request, false, 2); - request.cmcdResponseMode.requestHeaders = cmcdHeaders; - } - } } /** From 9e78810e7df7bf17e3a757798c4bd81080d693ec Mon Sep 17 00:00:00 2001 From: Nicolas Levy Date: Tue, 10 Sep 2024 11:44:29 -0300 Subject: [PATCH 36/82] fixed query mode --- src/streaming/net/HTTPLoader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/streaming/net/HTTPLoader.js b/src/streaming/net/HTTPLoader.js index ddbdde573e..60417571bd 100644 --- a/src/streaming/net/HTTPLoader.js +++ b/src/streaming/net/HTTPLoader.js @@ -292,7 +292,7 @@ function HTTPLoader(cfg) { const cmcdMode = cmcdResponseMode.mode ? cmcdResponseMode.mode : settings.get().streaming.cmcd.mode; let requestUrl = cmcdResponseMode.requestUrl; - let requestHeaderes = null; + let requestHeaderes = {}; const request = httpRequest.customData.request; request.status = httpResponse.status From 4a78492e703544e5d9d6d9c2424c06af6019b58d Mon Sep 17 00:00:00 2001 From: Nicolas Levy Date: Tue, 10 Sep 2024 12:18:21 -0300 Subject: [PATCH 37/82] example fixed --- build/webpack.dev.cjs | 2 +- samples/advanced/cmcd.html | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/webpack.dev.cjs b/build/webpack.dev.cjs index e9c605ac29..322ec5e695 100644 --- a/build/webpack.dev.cjs +++ b/build/webpack.dev.cjs @@ -19,7 +19,7 @@ const umdDevConfig = merge(umdConfig, { open: ['samples/index.html'], hot: true, compress: true, - port: 3000 + port: 3001 } }); diff --git a/samples/advanced/cmcd.html b/samples/advanced/cmcd.html index 9bb577c267..9aca5add56 100644 --- a/samples/advanced/cmcd.html +++ b/samples/advanced/cmcd.html @@ -51,21 +51,21 @@ reporting: { requestMode: { enabled: true, /* enable cmcdv2 request mode */ - mode: CMCD_MODE_HEADER, /* overrides global mode */ + mode: CMCD_MODE_QUERY, /* overrides global mode */ }, responseMode: { enabled: true, /* enable cmcdv2 response mode. FALSE by default */ - mode: CMCD_MODE_HEADER, /* overrides global mode. JSON not supported */ + mode: CMCD_MODE_QUERY, /* overrides global mode. JSON not supported */ enabledKeys: ['sid', 'cid', 'ts', 'url', 'br', 'd', 'ot', 'tb', 'v', 'sta', 'ttfb', 'ttlb','rc'], /* optional, overrides global keys */ - requestUrl: 'http://localhost:3000/cmcd_response_server', /*mandatory, URL to send report */ + requestUrl: 'http://localhost:3000/cmcd/response-mode', /*mandatory, URL to send report */ requestMethod: 'POST', /*optional, get by default */ }, stateIntervalMode: { enabled: true, /* enable cmcdv2 state-interval mode. FALSE by default */ - mode: CMCD_MODE_HEADER, /*overrides global mode. JSON not supported */ + mode: CMCD_MODE_QUERY, /*overrides global mode. JSON not supported */ interval: 10000, /* time in ms between regular requests. Default 30s or 0 to omit */ enabledKeys: ['sta', 'sid', 'cid', 'sf', 'v', 'ltc', 'msd', 'sta'], /* Currently only supports: 'sta', 'ts', 'sid', 'cid', 'sf', 'v', 'lb', 'pr' */ - requestUrl: 'http://localhost:3000/cmcd_state_server', /* mandatory, URL to send report */ + requestUrl: 'http://localhost:3000/cmcd/state-interval-mode', /* mandatory, URL to send report */ requestMethod: 'POST', /* optional, get by default */ } }, From 6cd75b9ee3bdb0200698eee8a2889c8a01113935 Mon Sep 17 00:00:00 2001 From: Nicolas Levy Date: Thu, 26 Sep 2024 15:26:17 -0300 Subject: [PATCH 38/82] fix: send msd once per CMCD mode --- src/streaming/models/CmcdModel.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index e92179a87e..514e22e904 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -61,6 +61,7 @@ function CmcdModel() { serviceDescriptionController, throughputController, streamProcessors, + _msdSent, _lastMediaTypeRequest, _isStartup, _bufferLevelStarved, @@ -138,6 +139,7 @@ function CmcdModel() { cid: null, sta: null }; + _msdSent = [false, false, false]; _bufferLevelStarved = {}; _isStartup = {}; _initialMediaRequestsDone = {}; @@ -261,12 +263,22 @@ function CmcdModel() { return null; } } - + function _applyWhitelist(cmcdData, cmcdReportingMode) { try { const cmcdParametersFromManifest = getCmcdParametersFromManifest(); var enabledCMCDKeys = cmcdParametersFromManifest.version ? cmcdParametersFromManifest.keys : settings.get().streaming.cmcd.enabledKeys; + // MSD key must be send once per mode. + if (cmcdReportingMode > 0) { + const msdSentIndex = cmcdReportingMode - 1; + if (_msdSent[msdSentIndex]) { + enabledCMCDKeys = enabledCMCDKeys.filter(item => item !== 'msd'); + } + if (!_msdSent[msdSentIndex] && cmcdData.msd) { + _msdSent[msdSentIndex] = true + } + } // For CMCD v2 use the reporting mode keys or global ones as default // TODO: Try to improve this block by using _checkAvailableKeys(cmcdParametersFromManifest) if (cmcdReportingMode === 1){ @@ -650,7 +662,7 @@ function CmcdModel() { if (!isNaN(ltc)) { data.ltc = ltc; } - // TODO: Send this key only once for each reporting mode + if (!isNaN(internalData.msd)) { data.msd = internalData.msd; } From a3bf91fa65bb6bdec73edfee716d7f3088bc5e32 Mon Sep 17 00:00:00 2001 From: Nicolas Levy Date: Thu, 26 Sep 2024 16:25:00 -0300 Subject: [PATCH 39/82] fix send msd only one time per mode --- src/streaming/models/CmcdModel.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index 514e22e904..e4581515d6 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -270,13 +270,12 @@ function CmcdModel() { var enabledCMCDKeys = cmcdParametersFromManifest.version ? cmcdParametersFromManifest.keys : settings.get().streaming.cmcd.enabledKeys; // MSD key must be send once per mode. - if (cmcdReportingMode > 0) { + if (cmcdData.msd) { const msdSentIndex = cmcdReportingMode - 1; if (_msdSent[msdSentIndex]) { - enabledCMCDKeys = enabledCMCDKeys.filter(item => item !== 'msd'); - } - if (!_msdSent[msdSentIndex] && cmcdData.msd) { - _msdSent[msdSentIndex] = true + delete cmcdData.msd; + } else { + _msdSent[msdSentIndex] = true; } } // For CMCD v2 use the reporting mode keys or global ones as default @@ -680,15 +679,11 @@ function CmcdModel() { } // Add v2 mandatory keys - const cmcdResponseMode = settings.get().streaming.cmcd.reporting.responseMode; - const cmcdStateIntervalMode = settings.get().streaming.cmcd.reporting.stateIntervalMode; - if (request && internalData.v === 2 && cmcdResponseMode.enabled) { + if (request && internalData.v === 2) { data.url = request.url.split('?')[0]; // remove potential cmcd query params } - if (internalData.v === 2 && (cmcdResponseMode.enabled || cmcdStateIntervalMode.enabled)) { - // TODO: This key needs to be generated when loading the media request in _loadRequest for response Mode - // or in the onStateChange() for the state-interval mode - data.ts = Date.now(); + if (internalData.v === 2) { + data.ts = new Date(); } return data; From 681ba1aeec2f5d9ead3624763193a736c510b6e9 Mon Sep 17 00:00:00 2001 From: Nicolas Levy Date: Thu, 26 Sep 2024 16:47:55 -0300 Subject: [PATCH 40/82] fix date --- src/streaming/models/CmcdModel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/streaming/models/CmcdModel.js b/src/streaming/models/CmcdModel.js index e4581515d6..75bd0eb198 100644 --- a/src/streaming/models/CmcdModel.js +++ b/src/streaming/models/CmcdModel.js @@ -683,7 +683,7 @@ function CmcdModel() { data.url = request.url.split('?')[0]; // remove potential cmcd query params } if (internalData.v === 2) { - data.ts = new Date(); + data.ts = Date.now(); } return data; From 03bc302e4a9dcd95ee67ff8a766c6c6ecbedd9c3 Mon Sep 17 00:00:00 2001 From: Sebastian Piquerez Date: Tue, 5 Nov 2024 14:56:11 -0300 Subject: [PATCH 41/82] Alternative palyer POC --- samples/develop.html | 79 ++++++++++++++++++++++++++++++++++++ src/streaming/MediaPlayer.js | 57 +++++++++++++++++++++++--- 2 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 samples/develop.html diff --git a/samples/develop.html b/samples/develop.html new file mode 100644 index 0000000000..c4a2ce9e75 --- /dev/null +++ b/samples/develop.html @@ -0,0 +1,79 @@ + + + + + Buffer target + + + + + + + + + + +
+
+
+ +
+
+
+ © DASH-IF +
+ +
+ + + + \ No newline at end of file diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js index e212ce0cc6..d9df13b065 100644 --- a/src/streaming/MediaPlayer.js +++ b/src/streaming/MediaPlayer.js @@ -170,6 +170,7 @@ function MediaPlayer() { uriFragmentModel, domStorage, segmentBaseController, + alternativePlayer, clientDataReportingController; /* @@ -286,7 +287,7 @@ function MediaPlayer() { * @memberof module:MediaPlayer * @instance */ - function initialize(view, source, autoPlay, startTime = NaN) { + function initialize(view, source, autoPlay,startTime = NaN) { if (!capabilities) { capabilities = Capabilities(context).getInstance(); capabilities.setConfig({ @@ -457,8 +458,12 @@ function MediaPlayer() { * @memberof module:MediaPlayer * @instance */ - function reset() { - attachSource(null); + function reset(onlyControllers) { + + if (!onlyControllers) { + attachSource(null); + } + attachView(null); protectionData = null; if (protectionController) { @@ -585,12 +590,14 @@ function MediaPlayer() { * @throws {@link module:MediaPlayer~SOURCE_NOT_ATTACHED_ERROR SOURCE_NOT_ATTACHED_ERROR} if called before attachSource function * @instance */ - function preload() { - if (videoModel.getElement() || streamingInitialized) { + function preload(time) { + if (videoModel.getElement() || (streamingInitialized && !time)) { return; } if (source) { - _initializePlayback(providedStartTime); + const playbackTime = time ? time : providedStartTime; + console.log(playbackTime) + _initializePlayback(playbackTime); } else { throw SOURCE_NOT_ATTACHED_ERROR; } @@ -2732,6 +2739,42 @@ function MediaPlayer() { } } + let videoNew + function setAlternativePlayer() { + videoNew = videoModel.getElement().cloneNode(true); + const mediaPlayerFactory = FactoryMaker.getClassFactory(MediaPlayer); + alternativePlayer = mediaPlayerFactory().create() + // const alternativeUrl = 'https://livesim2.dashif.org/livesim2/scte35_2/testpic_2s/Manifest.mpd'; + // const alternativeUrl = 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd' + const alternativeUrl = 'http://localhost:3000/stream.mpd' + const parent = videoModel.getElement().parentNode + parent.append(videoNew) + alternativePlayer.initialize(null, alternativeUrl, false); + alternativePlayer.updateSettings({ + // debug: {logLevel: 5}, + streaming: {cacheInitSegments: true} + }); + alternativePlayer.preload(10) + } + + async function switchView() { + const video = videoModel.getElement(); + // const alternativeVideo = alternativePlayer.getVideoElement() + pause() + const currentTime = time() + preload(currentTime) + alternativePlayer.attachView(video) + alternativePlayer.play() + setTimeout(async () => { + await alternativePlayer.attachView(null) + attachView(video); + alternativePlayer.destroy(); + alternativePlayer = null; + play(); + }, 7000) + } + + instance = { addABRCustomRule, addRequestInterceptor, @@ -2815,6 +2858,7 @@ function MediaPlayer() { seek, seekToOriginalLive, seekToPresentationTime, + setAlternativePlayer, setAutoPlay, setConfig, setCurrentTrack, @@ -2828,6 +2872,7 @@ function MediaPlayer() { setTextTrack, setVolume, setXHRWithCredentialsForType, + switchView, time, timeAsUtc, timeInDvrWindow, From 0c9aed2816e290a5ac7f3bf404647610c1d4f385 Mon Sep 17 00:00:00 2001 From: Joaquin Bartaburu Date: Wed, 6 Nov 2024 16:54:58 -0300 Subject: [PATCH 42/82] try to keep buffers on alternative change --- src/streaming/MediaPlayer.js | 56 +++++++++++++++---- src/streaming/Stream.js | 30 ++++++++++ .../controllers/PlaybackController.js | 4 +- src/streaming/controllers/StreamController.js | 52 +++++++++++++++++ 4 files changed, 131 insertions(+), 11 deletions(-) diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js index d9df13b065..e408622bdb 100644 --- a/src/streaming/MediaPlayer.js +++ b/src/streaming/MediaPlayer.js @@ -1461,7 +1461,35 @@ function MediaPlayer() { if (playbackInitialized) { //Reset if we have been playing before, so this is a new element. _resetPlaybackControllers(); } + console.log(providedStartTime); + + _initializePlayback(providedStartTime); + } + + function _attachViewAlt(element, config) { + if (!mediaPlayerInitialized) { + throw MEDIA_PLAYER_NOT_INITIALIZED_ERROR; + } + + videoModel.setElement(element); + + if (element) { + _detectProtection(); + _detectMetricsReporting(); + _detectMss(); + if (streamController) { + streamController.switchToVideoElement(10); + } + } + + streamController.setConfig(config); + + if (playbackInitialized) { //Reset if we have been playing before, so this is a new element. + _resetPlaybackControllers(); + } + console.log(providedStartTime); + _initializePlayback(providedStartTime); } @@ -2337,6 +2365,11 @@ function MediaPlayer() { // PRIVATE METHODS //*********************************** + function _resetControllers() { + const streams = streamController.resetAlt(); + return streams; + } + function _resetPlaybackControllers() { playbackInitialized = false; streamingInitialized = false; @@ -2739,35 +2772,38 @@ function MediaPlayer() { } } - let videoNew function setAlternativePlayer() { - videoNew = videoModel.getElement().cloneNode(true); const mediaPlayerFactory = FactoryMaker.getClassFactory(MediaPlayer); alternativePlayer = mediaPlayerFactory().create() // const alternativeUrl = 'https://livesim2.dashif.org/livesim2/scte35_2/testpic_2s/Manifest.mpd'; - // const alternativeUrl = 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd' - const alternativeUrl = 'http://localhost:3000/stream.mpd' - const parent = videoModel.getElement().parentNode - parent.append(videoNew) + const alternativeUrl = 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd' + // const alternativeUrl = 'http://localhost:3000/stream.mpd' alternativePlayer.initialize(null, alternativeUrl, false); alternativePlayer.updateSettings({ // debug: {logLevel: 5}, streaming: {cacheInitSegments: true} }); - alternativePlayer.preload(10) + alternativePlayer.preload() } async function switchView() { const video = videoModel.getElement(); // const alternativeVideo = alternativePlayer.getVideoElement() pause() - const currentTime = time() - preload(currentTime) + // attachView(null); + updateSettings({ + // debug: {logLevel: 5}, + streaming: {cacheInitSegments: true} + }); + // const currentTime = time() + // providedStartTime = 5; + // preload() + const streams = _resetControllers(); alternativePlayer.attachView(video) alternativePlayer.play() setTimeout(async () => { await alternativePlayer.attachView(null) - attachView(video); + _attachViewAlt(video, streams); alternativePlayer.destroy(); alternativePlayer = null; play(); diff --git a/src/streaming/Stream.js b/src/streaming/Stream.js index dad44879c5..8a529106b0 100644 --- a/src/streaming/Stream.js +++ b/src/streaming/Stream.js @@ -608,6 +608,35 @@ function Stream(config) { trackChangedEvents = []; } + function resetAlt(keepBuffers) { + + if (fragmentController) { + fragmentController.reset(); + fragmentController = null; + } + + if (abrController && streamInfo) { + abrController.clearDataForStream(streamInfo.id); + } + + if (segmentBlacklistController) { + segmentBlacklistController.reset(); + segmentBlacklistController = null; + } + + console.log('resetAlt, keepBuffers', keepBuffers); + + + resetInitialSettings(keepBuffers); + + streamInfo = null; + + unRegisterEvents(); + + unRegisterProtectionEvents(); + + } + function reset(keepBuffers) { if (fragmentController) { @@ -1083,6 +1112,7 @@ function Stream(config) { prepareQualityChange, prepareTrackChange, reset, + resetAlt, setIsEndedEventSignaled, setMediaSource, startPreloading, diff --git a/src/streaming/controllers/PlaybackController.js b/src/streaming/controllers/PlaybackController.js index cc5503aa77..6e1bf5ce7f 100644 --- a/src/streaming/controllers/PlaybackController.js +++ b/src/streaming/controllers/PlaybackController.js @@ -703,7 +703,9 @@ function PlaybackController() { } function _onPlaybackProgress() { - eventBus.trigger(Events.PLAYBACK_PROGRESS, { streamId: streamInfo.id }); + if (streamInfo){ + eventBus.trigger(Events.PLAYBACK_PROGRESS, { streamId: streamInfo.id }); + } } function _onPlaybackRateChanged() { diff --git a/src/streaming/controllers/StreamController.js b/src/streaming/controllers/StreamController.js index c2c6cd5303..cbcfe6ddae 100644 --- a/src/streaming/controllers/StreamController.js +++ b/src/streaming/controllers/StreamController.js @@ -650,6 +650,8 @@ function StreamController() { // If the track was changed in the active stream we need to stop preloading and remove the already prebuffered stuff. Since we do not support preloading specific handling of specific AdaptationSets yet. _deactivateAllPreloadingStreams(); + console.log('_onCurrentTrackChanged'); + if (settings.get().streaming.buffer.resetSourceBuffersForTrackSwitch && e.oldMediaInfo && e.oldMediaInfo.codec !== e.newMediaInfo.codec) { const seekTime = playbackController.getTime(); activeStream.deactivate(false); @@ -1309,6 +1311,8 @@ function StreamController() { function switchToVideoElement(seekTime) { + console.log('switchToVideoElement'); + if (activeStream) { playbackController.initialize(getActiveStreamInfo()); _openMediaSource({ seekTime, keepBuffers: false, streamActivated: true }); @@ -1502,6 +1506,9 @@ function StreamController() { return; } + if (config.streams) { + streams = config.streams; + } if (config.capabilities) { capabilities = config.capabilities; } @@ -1606,6 +1613,50 @@ function StreamController() { } } + function resetAlt() { + // _checkConfig(); + + // timeSyncController.reset(); + + // _flushPlaylistMetrics(hasMediaError || hasInitialisationError ? PlayListTrace.FAILURE_STOP_REASON : PlayListTrace.USER_REQUEST_STOP_REASON); + const config = {}; + config.streamsCpy = [...streams]; + + for (let i = 0, ln = streams ? streams.length : 0; i < ln; i++) { + const stream = streams[i]; + stream.resetAlt(true); + } + + unRegisterEvents(); + + // baseURLController.reset(); + // manifestUpdater.reset(); + // eventController.reset(); + // dashMetrics.clearAllCurrentMetrics(); + // manifestModel.setValue(null); + // manifestLoader.reset(); + // timelineConverter.reset(); + // initCache.reset(); + + // if (mediaSource) { + // mediaSourceController.detachMediaSource(videoModel); + // mediaSource = null; + // } + // videoModel = null; + // if (protectionController) { + // protectionController = null; + // protectionData = null; + // if (manifestModel.getValue()) { + // eventBus.trigger(Events.PROTECTION_DESTROYED, { data: manifestModel.getValue().url }); + // } + // } + + _stopPlaybackEndedTimerInterval(); + eventBus.trigger(Events.STREAM_TEARDOWN_COMPLETE); + resetInitialSettings(); + return config; + } + function reset() { _checkConfig(); @@ -1688,6 +1739,7 @@ function StreamController() { loadWithManifest, refreshManifest, reset, + resetAlt, setConfig, setProtectionData, switchToVideoElement, From 5f340c17e14b225b55132e5d21c95a50c55110a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Caballero?= Date: Tue, 12 Nov 2024 19:11:20 -0300 Subject: [PATCH 43/82] restore bytes to original mediasource --- samples/develop.html | 5 +- src/streaming/MediaPlayer.js | 38 ++++++------ src/streaming/SourceBufferSink.js | 21 +++++++ src/streaming/StreamProcessor.js | 15 +++++ src/streaming/controllers/BufferController.js | 10 +++ .../controllers/MediaSourceController.js | 10 +++ src/streaming/controllers/StreamController.js | 61 +++++++++++++++++++ 7 files changed, 136 insertions(+), 24 deletions(-) diff --git a/samples/develop.html b/samples/develop.html index c4a2ce9e75..5f8a6b7201 100644 --- a/samples/develop.html +++ b/samples/develop.html @@ -21,7 +21,7 @@
- +