Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/content steering #4031

Merged
merged 23 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c1ed3f3
Move ServiceDescriptionController.js to dash controller folder
dsilhavy Aug 9, 2022
4caaa3f
WiP: Parse content steering information
dsilhavy Aug 9, 2022
8bf70f7
Merge branch 'development' of https://github.com/Dash-Industry-Forum/…
dsilhavy Aug 22, 2022
9e6ec96
Add initial request to content steering server
dsilhavy Aug 22, 2022
5eb60fd
Load steering data according to ttl and queryBeforeStart
dsilhavy Aug 23, 2022
5009f17
Fix unit tests
dsilhavy Aug 23, 2022
d5e965e
Fix linting errors
dsilhavy Aug 23, 2022
2d21c26
First working steering implementation
dsilhavy Aug 23, 2022
98763f0
Fix JSDoc
dsilhavy Aug 23, 2022
5291c3b
Move content steering selection to dedicated class ContentSteeringSel…
dsilhavy Aug 24, 2022
03611b8
Fix linting error
dsilhavy Aug 24, 2022
309765e
Add settings flag to enable/disable content steering
dsilhavy Aug 24, 2022
879e6ae
Add support for RELOAD-URI specified absolute and relative
dsilhavy Aug 24, 2022
9b8bee4
Add API endpoint to trigger steering request
dsilhavy Aug 26, 2022
26c4290
Content steering demo page for local steering server
dsilhavy Aug 26, 2022
8177d90
Minor changes to the steering demo page
dsilhavy Aug 29, 2022
542254b
Add support for proxy server url
dsilhavy Aug 29, 2022
a7a4b2f
Add unit tests for Content Steering in DASHManifestModel
dsilhavy Aug 29, 2022
fa8102f
Add flag to enable/disable content steering to reference UI
dsilhavy Aug 29, 2022
bd1f5fe
Revert change to getLocation
dsilhavy Aug 29, 2022
cbb036b
Revert change to getPatchLocation
dsilhavy Aug 29, 2022
99e1d89
Remove unnecessary parameter in call to contentSteeringController.loa…
dsilhavy Aug 29, 2022
d242132
Formatting changes
dsilhavy Aug 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ declare namespace dashjs {
wallclockTimeUpdateInterval?: number,
manifestUpdateRetryInterval?: number,
applyServiceDescription?: boolean,
applyProducerReferenceTime?: boolean,
applyContentSteering?: boolean,
cacheInitSegments?: boolean,
eventControllerRefreshDelay?: number,
enableManifestDurationMismatchFix?: boolean,
Expand Down Expand Up @@ -606,6 +608,10 @@ declare namespace dashjs {

getOfflineController(): OfflineController;

triggerSteeringRequest(): Promise<any>;

getCurrentSteeringResponseData(): object;

getSettings(): MediaPlayerSettingClass;

updateSettings(settings: MediaPlayerSettingClass): void;
Expand Down
233 changes: 233 additions & 0 deletions samples/advanced/content-steering.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Content Steering</title>

<script src="../../dist/dash.all.debug.js"></script>

<!-- Bootstrap core CSS -->
<link href="../lib/bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="../lib/main.css" rel="stylesheet">

<style>
video {
width: 640px;
height: 360px;
}

#start, #receive {
height: 500px;
margin-top: 20px;
font-size: 10px;
}

.border {
border: 2px solid #dee2e6 !important;
}
</style>

<script class="code">
let player;
let mpd = "";

function init() {
player = dashjs.MediaPlayer().create();
document.getElementById('load-button').addEventListener('click', function () {
_load();
})
player.initialize(document.querySelector("video"), null, true);

player.on(dashjs.MediaPlayer.events.FRAGMENT_LOADING_STARTED, _onFragmentLoadingStarted, null);
player.on(dashjs.MediaPlayer.events.CONTENT_STEERING_REQUEST_COMPLETED, _onContentSteeringRequestCompleted, null);
}

function _load() {
mpd = document.getElementById('manifest').value;
player.attachSource(mpd);
}

function _onFragmentLoadingStarted(e) {
try {
if (e && e.mediaType && (e.mediaType === 'video' || e.mediaType === 'audio') && e.request) {
if (e.request.serviceLocation) {
const element = document.getElementById(`${e.mediaType}-service-location`);
element.innerText = e.request.serviceLocation;

if (e.request.serviceLocation === 'alpha') {
document.getElementById('img-alpha-cdn').classList.remove('d-none');
document.getElementById('img-beta-cdn').classList.add('d-none');
} else {
document.getElementById('img-alpha-cdn').classList.add('d-none');
document.getElementById('img-beta-cdn').classList.remove('d-none');
}
}
if (e.request.url) {
const element = document.getElementById(`${e.mediaType}-request-url`);
element.innerText = e.request.url;
}
}
} catch (e) {
console.error(e);
}
}

function _onContentSteeringRequestCompleted(e) {
try {
if (e) {
document.getElementById(`steering-request-timestamp`).innerText = new Date().toISOString();
if (e.url) {
const element = document.getElementById(`steering-request-url`);
element.innerText = e.url;
}
if (e.currentSteeringResponseData) {
document.getElementById(`steering-version`).innerText = e.currentSteeringResponseData.version;
document.getElementById(`steering-ttl`).innerText = e.currentSteeringResponseData.ttl;
document.getElementById(`steering-reload-uri`).innerText = e.currentSteeringResponseData.reloadUri;
document.getElementById(`steering-service-location-priority`).innerText = e.currentSteeringResponseData.serviceLocationPriority.toString();
}
}
} catch (e) {
console.error(e);
}
}


</script>
</head>
<body>

<main>
<div class="container py-4">
<header class="pb-3 mb-4 border-bottom">
<img class=""
src="../lib/img/dashjs-logo.png"
width="200">
</header>
<div class="row">
<h1>Content Steering</h1>
<div class="col-md-5">
<div class="h-100 p-5 border rounded-3">
<h4>Description</h4>
<p>Content distributors often use multiple Content Delivery Networks (CDNs) to
distribute their content to the end-users. They may upload a copy of their catalogue
to each CDN, or more commonly have all CDNs pull the content from a common
origin. Alternate URLs are generated, one for each CDN, that point at identical
content. DASH players may access alternate URLs in the event of delivery
problems. </p>
<p><b>Content steering</b> describes a deterministic capability for a content
distributor to switch the content source that a player uses either at start-up or
midstream, by means of a remote steering service. The DASH implementation of
Content Steering also supports the notion of a proxy steering server which can
switch a mobile client between broadcast and unicast sources. </p>
</div>
</div>
<div class="col-md-7">
<div class="h-100 p-5 border rounded-3">
<h4>Architecture</h4>
<img src="img/steering.png" class="img-fluid" alt="Steering architecture illustration">
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<div class="input-group mb-3">
<input type="text" id="manifest" class="form-control" placeholder="MPD URL"
value="http://localhost:3333/steering-content/bbb/dash.mpd">
<button type="button" id="load-button" class="btn btn-success">Load MPD
</button>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-md-6">
<video controls="true" muted></video>
</div>
<div class="col-md-6">
<div class="h-100 p-5 border rounded-3">
<h4>CDN Selection</h4>
<img src="img/content-steering-simulation-alpha-cdn.drawio.png" id="img-alpha-cdn"
class="img-fluid d-none" alt="Steering alpha CDN">
<img src="img/content-steering-simulation-beta-cdn.drawio.png" id="img-beta-cdn"
class="img-fluid d-none" alt="Steering alpha beta">
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-md-12">
<div class="h-100 p-5 border rounded-3">
<h4> Fragment Requests</h4>
<table class="table">
<thead>
<tr>
<th scope="col">Type</th>
<th scope="col">Service Location</th>
<th scope="col">Request URL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Audio</td>
<td id="audio-service-location"></td>
<td id="audio-request-url"></td>
</tr>
<tr>
<td>Video</td>
<td id="video-service-location"></td>
<td id="video-request-url"></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col-md-12 mt-2">
<div class="h-100 p-5 border rounded-3">
<h4> Steering Data</h4>
<table class="table table-condensed">
<thead>
<tr>
<th scope="col">Timestamp</th>
<th scope="col">Request URL</th>
<th scope="col">Response</th>
</tr>
</thead>
<tbody>
<tr>
<tr>
<td id="steering-request-timestamp"></td>
<td id="steering-request-url"></td>
<td id="steering-response">
<ul>
<li>Version: <span id="steering-version"></span></li>
<li>Reload URI: <span id="steering-reload-uri"></span></li>
<li>Service Location Priority: <span id="steering-service-location-priority"></span>
</li>
<li>TTL: <span id="steering-ttl"></span></li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="code-output"></div>
</div>
</div>
<footer class="pt-3 mt-4 text-muted border-top">
&copy; DASH-IF
</footer>
</div>
</main>


<script>
document.addEventListener('DOMContentLoaded', function () {
init();
});
</script>
<script src="../highlighter.js"></script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/advanced/img/steering.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions samples/advanced/listening-to-SCTE-EMSG-events.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<script class="code">
var player;
const URL = "https://livesim.dashif.org/livesim/scte35_2/testpic_2s/Manifest.mpd";
const mpd = "https://livesim.dashif.org/livesim/scte35_2/testpic_2s/Manifest.mpd";
const SCHEMEIDURI = "urn:scte:scte35:2013:xml";
const EVENT_MODE_ON_START = dashjs.MediaPlayer.events.EVENT_MODE_ON_START;
const EVENT_MODE_ON_RECEIVE = dashjs.MediaPlayer.events.EVENT_MODE_ON_RECEIVE;
Expand All @@ -35,7 +35,7 @@
player.updateSettings({ 'debug': { 'logLevel': dashjs.Debug.LOG_LEVEL_DEBUG }});
player.on(SCHEMEIDURI, showStartEvent, null); /* Default mode is onStart, no need to specify a mode */
player.on(SCHEMEIDURI, showReceiveEvent, null, { mode: EVENT_MODE_ON_RECEIVE });
player.initialize(document.querySelector("video"), URL, true);
player.initialize(document.querySelector("video"), mpd, true);
}

function showStartEvent(e) {
Expand Down
10 changes: 10 additions & 0 deletions samples/dash-if-reference-player/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
$scope.jumpGapsSelected = true;
$scope.fastSwitchSelected = true;
$scope.applyServiceDescription = true;
$scope.applyContentSteering = true;
$scope.useSuggestedPresentationDelay = true;
$scope.videoAutoSwitchSelected = true;
$scope.forceQualitySwitchSelected = false;
Expand Down Expand Up @@ -570,6 +571,14 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
});
};

$scope.toggleApplyContentSteering = function () {
$scope.player.updateSettings({
streaming: {
applyContentSteering: $scope.applyContentSteering
}
});
};

$scope.toggleUseSuggestedPresentationDelay = function () {
$scope.player.updateSettings({
streaming: {
Expand Down Expand Up @@ -2037,6 +2046,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
function setAdditionalPlaybackOptions() {
var currentConfig = $scope.player.getSettings();
$scope.applyServiceDescription = currentConfig.streaming.applyServiceDescription;
$scope.applyContentSteering = currentConfig.streaming.applyContentSteering;
$scope.scheduleWhilePausedSelected = currentConfig.streaming.scheduling.scheduleWhilePaused;
$scope.calcSegmentAvailabilityRangeFromTimelineSelected = currentConfig.streaming.timeShiftBuffer.calcFromSegmentTimeline;
$scope.reuseExistingSourceBuffersSelected = currentConfig.streaming.buffer.reuseExistingSourceBuffers;
Expand Down
6 changes: 6 additions & 0 deletions samples/dash-if-reference-player/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@
ng-change="toggleLiveCatchupEnabled()" ng-checked="liveCatchupEnabled">
Live catchup
</label>
<label class="topcoat-checkbox" data-toggle="tooltip" data-placement="right"
title="Use the ContentSteering information from the MPD if enabled">
<input type="checkbox" id="applyContentSteering" ng-model="applyContentSteering"
ng-change="toggleApplyContentSteering()" ng-checked="applyContentSteering">
Apply ContentSteering
</label>
<div class="sub-options-item">
<div class="sub-options-item-title">Catchup mechanism</div>
<div class="sub-options-item-body">
Expand Down
4 changes: 4 additions & 0 deletions src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import Events from './events/Events';
* cacheInitSegments: true,
* applyServiceDescription: true,
* applyProducerReferenceTime: true,
* applyContentSteering: true,
* eventControllerRefreshDelay: 100,
* enableManifestDurationMismatchFix: true,
* capabilities: {
Expand Down Expand Up @@ -655,6 +656,8 @@ import Events from './events/Events';
* Set to true if dash.js should use the parameters defined in ServiceDescription elements
* @property {boolean} [applyProducerReferenceTime=true]
* Set to true if dash.js should use the parameters defined in ProducerReferenceTime elements in combination with ServiceDescription elements.
* @property {boolean} [applyContentSteering=true]
* Set to true if dash.js should apply content steering during playback.
* @property {number} [eventControllerRefreshDelay=100]
* For multi-period streams, overwrite the manifest mediaPresentationDuration attribute with the sum of period durations if the manifest mediaPresentationDuration is greater than the sum of period durations
* @property {boolean} [enableManifestDurationMismatchFix=true]
Expand Down Expand Up @@ -760,6 +763,7 @@ function Settings() {
cacheInitSegments: false,
applyServiceDescription: true,
applyProducerReferenceTime: true,
applyContentSteering: true,
eventControllerRefreshDelay: 100,
enableManifestDurationMismatchFix: true,
capabilities: {
Expand Down
12 changes: 12 additions & 0 deletions src/dash/DashAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,17 @@ function DashAdapter() {
return dashManifestModel.getMpd(manifest);
}

/**
* Returns the ContentSteering element of the MPD
* @param {object} manifest
* @returns {object} contentSteering
* @memberOf module:DashAdapter
* @instance
*/
function getContentSteering(manifest) {
return dashManifestModel.getContentSteering(manifest);
}

/**
* Returns the location element of the MPD
* @param {object} manifest
Expand Down Expand Up @@ -1211,6 +1222,7 @@ function DashAdapter() {
getIsDynamic,
getDuration,
getRegularPeriods,
getContentSteering,
getLocation,
getPatchLocation,
getManifestUpdatePeriod,
Expand Down
11 changes: 11 additions & 0 deletions src/dash/constants/DashConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ class DashConstants {
this.ORIGINAL_MPD_ID = 'mpdId';
this.WALL_CLOCK_TIME = 'wallClockTime';
this.PRESENTATION_TIME = 'presentationTime';
this.CONTENT_STEERING = 'ContentSteering';
this.CONTENT_STEERING_AS_ARRAY = 'ContentSteering_asArray';
this.DEFAULT_SERVICE_LOCATION = 'defaultServiceLocation';
this.QUERY_BEFORE_START = 'queryBeforeStart';
this.PROXY_SERVER_URL = 'proxyServerURL';
this.CONTENT_STEERING_RESPONSE = {
VERSION: 'VERSION',
TTL: 'TTL',
RELOAD_URI: 'RELOAD-URI',
SERVICE_LOCATION_PRIORITY : 'SERVICE-LOCATION-PRIORITY'
}
}

constructor () {
Expand Down
Loading