Skip to content

Commit

Permalink
Add feature support for Common Media Server Data (#4095)
Browse files Browse the repository at this point in the history
* Add support for Common Media Server Data (WIP)

* Add support for Common Media Server Data (WIP)

* fix unit tests

* index.d.ts: add settings.cmsd

* Use constants for CMSD parameters keys

* CMSD: max suggested bitrate shall be treated as combination of audio and video bitrate

* CMSS: If multiple CMSD-Dynamic headers are present, only ingest the last one defined

* fix index.d.ts

* fix,cmsd: crash on undefined staticParams

* CMSD: use request media type if object type not provided in CMSD-Static response header

* CMSD: add method CmsdModel.getEstimatedThroughput()

* CMSD: use etp from CMSD for ABR throughput rule

* CMSD: clear non persistent values, that applies only for the current request

* CMSD: use rd and rtt from CMSD for ABR throughput rule

* CMSD: complete jsdoc

* CMSD

- add CMSD response header in HTTPRequest metrics
- apply a weight ratio on etp for throughput calculation
- add setting parameter for ABR using CMSD response headers
- update index.d.ts

* CMSD: remove comment

* CMSD

- rename setting parameter streaming.cmsd.abr.useMb to streaming.cmsd.abr.applyMb
- apply (if enabled) CMSD maximum bitrate in ABR logic

* CMSD - reference sample

- complete CMSD settings
- add metrics ans charts for measured bandwidth and etp

* CMSD: add some Broadpeak reference streams

* CMSD: etpWeightRatio as a float number

* CMSD: fix parameters order for DashMetrics.addHTTPRequest()

* CMSD

- check if CMSD is enabled before applying mb
- check etpWeightRatio value

* CMSD: remove commented code

* CMSD: fix parameters order for MetricsModel.addHTTPRequest()
  • Loading branch information
bbert committed Feb 6, 2023
1 parent 7e25ead commit 63ec15d
Show file tree
Hide file tree
Showing 15 changed files with 547 additions and 12 deletions.
7 changes: 7 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,13 @@ declare namespace dashjs {
rtpSafetyFactor?: number,
mode?: 'query' | 'header',
enabledKeys?: Array<string>
},
cmsd?: {
enabled?: boolean,
abr?: {
applyMb: boolean,
etpWeightRatio?: number
}
}
};
errors?: {
Expand Down
78 changes: 73 additions & 5 deletions samples/dash-if-reference-player/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
download: { data: [], selected: false, color: '#44c248', label: 'Audio Download Time (sec)' },
latency: { data: [], selected: false, color: '#326e88', label: 'Audio Latency (ms)' },
droppedFPS: { data: [], selected: false, color: '#004E64', label: 'Audio Dropped FPS' },
mtp: { data: [], selected: false, color: '#FFC400', label: 'Measured throughput (kpbs)' },
etp: { data: [], selected: false, color: '#1712B3', label: 'Estimated throughput (kpbs)' },
liveLatency: { data: [], selected: false, color: '#65080c', label: 'Live Latency' },
playbackRate: { data: [], selected: false, color: '#65080c', label: 'Playback Rate' }
},
Expand All @@ -155,6 +157,8 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
download: { data: [], selected: false, color: '#FF6700', label: 'Video Download Time (sec)' },
latency: { data: [], selected: false, color: '#329d61', label: 'Video Latency (ms)' },
droppedFPS: { data: [], selected: false, color: '#65080c', label: 'Video Dropped FPS' },
mtp: { data: [], selected: false, color: '#FFC400', label: 'Measured throughput (kpbs)' },
etp: { data: [], selected: false, color: '#1712B3', label: 'Estimated throughput (kpbs)' },
liveLatency: { data: [], selected: false, color: '#65080c', label: 'Live Latency' },
playbackRate: { data: [], selected: false, color: '#65080c', label: 'Playback Rate' }
}
Expand Down Expand Up @@ -261,6 +265,8 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
$scope.videoDownload = '';
$scope.videoRatioCount = 0;
$scope.videoRatio = '';
$scope.videoMtp = 0;
$scope.videoEtp = 0;
$scope.videoLiveLatency = 0;
$scope.videoPlaybackRate = 1.00;

Expand All @@ -277,12 +283,17 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
$scope.audioDownload = '';
$scope.audioRatioCount = 0;
$scope.audioRatio = '';
$scope.audioMtp = 0;
$scope.audioEtp = 0;
$scope.audioLiveLatency = 0;
$scope.audioPlaybackRate = 1.00;

// Starting Options
$scope.autoPlaySelected = true;
$scope.cmcdEnabled = false;
$scope.cmsdEnabled = false;
$scope.cmsdApplyMb = false;
$scope.cmsdEtpWeightRatio = 0;
$scope.loopSelected = true;
$scope.scheduleWhilePausedSelected = true;
$scope.calcSegmentAvailabilityRangeFromTimelineSelected = false;
Expand Down Expand Up @@ -875,9 +886,43 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'

$scope.toggleCmcdEnabled = function () {
$scope.player.updateSettings({
'streaming': {
'cmcd': {
'enabled': $scope.cmcdEnabled
streaming: {
cmcd: {
enabled: $scope.cmcdEnabled
}
}
});
};

$scope.toggleCmsdEnabled = function () {
$scope.player.updateSettings({
streaming: {
cmsd: {
enabled: $scope.cmsdEnabled
}
}
});
};

$scope.toggleCmsdApplyMb = function () {
$scope.player.updateSettings({
streaming: {
cmsd: {
abr: {
applyMb: $scope.cmsdApplyMb
}
}
}
});
};

$scope.updateCmsdEtpWeightRatio = function () {
$scope.player.updateSettings({
streaming: {
cmsd: {
abr: {
etpWeightRatio: parseFloat($scope.cmsdEtpWeightRatio)
}
}
}
});
Expand Down Expand Up @@ -1078,6 +1123,10 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
}
};

$scope.isCMSDEnabled = function () {
return $scope.player.getSettings().streaming.cmsd.enabled;
};

$scope.hasLogo = function (item) {
return (item.hasOwnProperty('logo') && item.logo);
};
Expand Down Expand Up @@ -1812,7 +1861,9 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
function calculateHTTPMetrics(type, requests) {
var latency = {},
download = {},
ratio = {};
ratio = {},
mtp = {},
etp = {};

var requestWindow = requests.slice(-20).filter(function (req) {
return req.responsecode >= 200 && req.responsecode < 300 && req.type === 'MediaSegment' && req._stream === type && !!req._mediaduration;
Expand Down Expand Up @@ -1870,10 +1921,14 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
count: durationTimes.length
};

const request = requestWindow[requestWindow.length - 1];
etp[type] = request.cmsd && request.cmsd.dynamic && request.cmsd.dynamic.etp ? request.cmsd.dynamic.etp : 0;

return {
latency: latency,
download: download,
ratio: ratio
ratio: ratio,
etp: etp
};

}
Expand Down Expand Up @@ -1961,6 +2016,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
var droppedFPS = droppedFramesMetrics ? droppedFramesMetrics.droppedFrames : 0;
var liveLatency = 0;
var playbackRate = 1.00
var mtp = $scope.player.getAverageThroughput(type);
if ($scope.isDynamic) {
liveLatency = $scope.player.getCurrentLiveLatency();
playbackRate = parseFloat($scope.player.getPlaybackRate().toFixed(2));
Expand All @@ -1977,6 +2033,8 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
$scope[type + 'Download'] = httpMetrics.download[type].low.toFixed(2) + ' | ' + httpMetrics.download[type].average.toFixed(2) + ' | ' + httpMetrics.download[type].high.toFixed(2);
$scope[type + 'Latency'] = httpMetrics.latency[type].low.toFixed(2) + ' | ' + httpMetrics.latency[type].average.toFixed(2) + ' | ' + httpMetrics.latency[type].high.toFixed(2);
$scope[type + 'Ratio'] = httpMetrics.ratio[type].low.toFixed(2) + ' | ' + httpMetrics.ratio[type].average.toFixed(2) + ' | ' + httpMetrics.ratio[type].high.toFixed(2);
$scope[type + 'Etp'] = (httpMetrics.etp[type] / 1000).toFixed(3);
$scope[type + 'Mtp'] = (mtp / 1000).toFixed(3);
}

if ($scope.chartCount % 2 === 0) {
Expand All @@ -1992,6 +2050,8 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
$scope.plotPoint('download', type, httpMetrics.download[type].average.toFixed(2), time);
$scope.plotPoint('latency', type, httpMetrics.latency[type].average.toFixed(2), time);
$scope.plotPoint('ratio', type, httpMetrics.ratio[type].average.toFixed(2), time);
$scope.plotPoint('etp', type, (httpMetrics.etp[type] / 1000).toFixed(3), time);
$scope.plotPoint('mtp', type, (mtp / 1000).toFixed(3), time);
}
$scope.safeApply();
}
Expand Down Expand Up @@ -2158,6 +2218,13 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
}
}

function setCMSDSettings() {
var currentConfig = $scope.player.getSettings();
$scope.cmsdEnabled = currentConfig.streaming.cmsd.enabled;
$scope.cmsdApplyMb = currentConfig.streaming.cmsd.abr.applyMb;
$scope.cmsdEtpWeightRatio = currentConfig.streaming.cmsd.abr.etpWeightRatio;
}

function getUrlVars() {
var vars = {};
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
Expand Down Expand Up @@ -2221,6 +2288,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
setTrackSwitchModeSettings();
setInitialLogLevel();
setCMCDSettings();
setCMSDSettings();

checkLocationProtocol();

Expand Down
15 changes: 15 additions & 0 deletions samples/dash-if-reference-player/app/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@
"acronym": "VDMS",
"name": "Verizon Media",
"url": "https://www.verizonmedia.com/"
},
"broadpeak": {
"acronym": "Broadpeak",
"name": "Broadpeak",
"url": "https://www.broadpeak.tv/"
}
},
"items": [
Expand Down Expand Up @@ -304,6 +309,16 @@
"url": "https://livesim.dashif.org/livesim/chunkdur_1/ato_7/testpic4_8s/Manifest.mpd",
"name": "Low Latency (Multi-Rate) (livesim-chunked)",
"provider": "dashif"
},
{
"url": "https://explo.broadpeak.tv:8343/bpk-tv/spring/lowlat/index.mpd",
"name": "Live low latency (SegmentTemplate, CMSD)",
"provider": "broadpeak"
},
{
"url": "https://explo.broadpeak.tv:8343/bpk-tv/spring/lowlat/index_timeline.mpd",
"name": "Live low latency (SegmentTimeline, CMSD)",
"provider": "broadpeak"
}
]
},
Expand Down
54 changes: 53 additions & 1 deletion samples/dash-if-reference-player/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,26 @@
ng-change="updateCmcdEnabledKeys()">
</div>
</div>
<div class="options-item">
<div class="options-item-title">CMSD</div>
<div class="options-item-body">
<label class="topcoat-checkbox" data-toggle="tooltip" data-placement="right"
title="Enables parsing of CMSD response header parameters">
<input type="checkbox" ng-model="cmsdEnabled" ng-change="toggleCmsdEnabled()"
ng-checked="cmsdEnabled">
Enable CMSD Parsing
</label>
<label class="topcoat-checkbox" data-toggle="tooltip" data-placement="right"
title="Apply maximum suggested bitrate from CMSD response headers">
<input type="checkbox" id="cmsdApplyMb" ng-model="cmsdApplyMb"
ng-change="toggleCmsdApplyMb()" ng-checked="cmsdApplyMb">
Apply maximum suggested bitrate
</label>
<label class="options-label">Etp weight ratio:</label>
<input type="text" class="form-control" placeholder="weight ratio" ng-model="cmsdEtpWeightRatio"
ng-change="updateCmsdEtpWeightRatio()">
</div>
</div>

</div>

Expand Down Expand Up @@ -995,6 +1015,22 @@ <h5><span class="label label-warning" style="margin-right:3px">Updated</span>Exp
title="The minimum, average and maximum ratio of the segment playback time to total download time over the last 4 segments">Ratio
(min|avg|max) :</label> {{videoRatio}}
</div>
<div class="text-success" ng-show="isCMSDEnabled()">
<input id="mtpCB" type="checkbox"
ng-model="chartState.video.mtp.selected"
ng-change="enableChartByName('mtp', 'video')">
<label class="text-primary" for="mtpCB" data-toggle="tooltip"
data-placement="top"
title="The measured (averaged) throughput computed in the ABR logic">Measured throughput:</label> {{videoMtp}} Mbps
</div>
<div class="text-success" ng-show="isCMSDEnabled()">
<input id="etpCB" type="checkbox"
ng-model="chartState.video.etp.selected"
ng-change="enableChartByName('etp', 'video')">
<label class="text-primary" for="etpCB" data-toggle="tooltip"
data-placement="top"
title="The estimated throughput return by CMSD response headers">Estimated throughput:</label> {{videoEtp}} Mbps
</div>
<div class="text-success" ng-show="isDynamic">
<input id="liveLatencyCB" type="checkbox"
ng-model="chartState.video.liveLatency.selected"
Expand Down Expand Up @@ -1084,7 +1120,23 @@ <h5><span class="label label-warning" style="margin-right:3px">Updated</span>Exp
title="The minimum, average and maximum ratio of the segment playback time to total download time over the last 4 segments">Ratio
(min|avg|max) :</label> {{audioRatio}}
</div>
<div class="text-success">
<div class="text-success" ng-show="isCMSDEnabled()">
<input id="mtpCB" type="checkbox"
ng-model="chartState.video.mtp.selected"
ng-change="enableChartByName('mtp', 'audio')">
<label class="text-primary" for="mtpCB" data-toggle="tooltip"
data-placement="top"
title="The measured (averaged) throughput computed in the ABR logic">Measured throughput:</label> {{videoMtp}} Mbps
</div>
<div class="text-success" ng-show="isCMSDEnabled()">
<input id="etpCB" type="checkbox"
ng-model="chartState.audio.etp.selected"
ng-change="enableChartByName('etp', 'audio')">
<label class="text-primary" for="etpCB" data-toggle="tooltip"
data-placement="top"
title="The estimated throughput return by CMSD response headers">Estimated throughput:</label> {{audioEtp}} Mbps
</div>
<div class="text-success" ng-show="isDynamic">
<input id="liveLatencyCB" type="checkbox"
ng-model="chartState.audio.liveLatency.selected"
ng-change="enableChartByName('liveLatency', 'audio')">
Expand Down
34 changes: 33 additions & 1 deletion src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,14 @@ import Events from './events/Events';
* rtpSafetyFactor: 5,
* mode: Constants.CMCD_MODE_QUERY,
* enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v']
* }
* },
* cmsd: {
* enabled: false,
* abr: {
* applyMb: false,
* etpWeightRatio: 0
* }
* }
* },
* errors: {
* recoverAttempts: {
Expand Down Expand Up @@ -658,6 +665,22 @@ import Events from './events/Events';
* This value is used to specify the desired CMCD parameters. Parameters not included in this list are not reported.
*/

/**
* @typedef {Object} module:Settings~CmsdSettings
* @property {boolean} [enabled=false]
* Enable or disable the CMSD response headers parsing.
* @property {module:Settings~CmsdAbrSettings} [abr]
* Sets additional ABR rules based on CMSD response headers.
*/

/**
* @typedef {Object} CmsdAbrSettings
* @property {boolean} [applyMb=false]
* Set to true if dash.js should apply CMSD maximum suggested bitrate in ABR logic.
* @property {number} [etpWeightRatio=0]
* Sets the weight ratio (between 0 and 1) that shall be applied on CMSD estimated throuhgput compared to measured throughput when calculating throughput.
*/

/**
* @typedef {Object} Metrics
* @property {number} [metricsMaxListDepth=100]
Expand Down Expand Up @@ -758,6 +781,8 @@ import Events from './events/Events';
* Adaptive Bitrate algorithm related settings.
* @property {module:Settings~CmcdSettings} cmcd
* Settings related to Common Media Client Data reporting.
* @property {module:Settings~CmsdSettings} cmsd
* Settings related to Common Media Server Data parsing.
*/


Expand Down Expand Up @@ -974,6 +999,13 @@ function Settings() {
rtpSafetyFactor: 5,
mode: Constants.CMCD_MODE_QUERY,
enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v']
},
cmsd: {
enabled: false,
abr: {
applyMb: false,
etpWeightRatio: 0
}
}
},
errors: {
Expand Down
1 change: 1 addition & 0 deletions src/core/events/CoreEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class CoreEvents extends EventsBase {
this.BYTES_APPENDED_END_FRAGMENT = 'bytesAppendedEndFragment';
this.BUFFER_REPLACEMENT_STARTED = 'bufferReplacementStarted';
this.CHECK_FOR_EXISTENCE_COMPLETED = 'checkForExistenceCompleted';
this.CMSD_STATIC_HEADER = 'cmsdStaticHeader';
this.CURRENT_TRACK_CHANGED = 'currentTrackChanged';
this.DATA_UPDATE_COMPLETED = 'dataUpdateCompleted';
this.INBAND_EVENTS = 'inbandEvents';
Expand Down
5 changes: 3 additions & 2 deletions src/dash/DashMetrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ function DashMetrics(config) {
* @instance
* @ignore
*/
function addHttpRequest(request, responseURL, responseStatus, responseHeaders, traces) {
function addHttpRequest(request, responseURL, responseStatus, responseHeaders, traces, cmsd) {
metricsModel.addHttpRequest(request.mediaType,
null,
request.type,
Expand All @@ -361,7 +361,8 @@ function DashMetrics(config) {
request.duration,
responseHeaders,
traces,
request.fileLoaderType);
request.fileLoaderType,
cmsd);
}

/**
Expand Down

0 comments on commit 63ec15d

Please sign in to comment.