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

Learn2Adapt Low Latency ABR rule #3340

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
271 changes: 271 additions & 0 deletions samples/low-latency/L2ALL_index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Low latency live stream instantiation example with local execution</title>
<script src="../../dist/dash.all.debug.js"></script>
<script src="../../dist/dash.all.debug.js"></script>
<script src="http://code.jquery.com/jquery-3.4.1.min.js"></script>


<script class="code">
var player, targetLatency, minDrift, catchupPlaybackRate;

function init() {
var video,
url = 'https://livesim.dashif.org/livesim/chunkdur_1/ato_7/testpic4_8s/Manifest300.mpd';//url = 'http://localhost:9001/live/live.mpd';//See instructions at https://github.com/unifiedstreaming/Learn2Adapt-LowLatency

video = document.querySelector("video");
player = dashjs.MediaPlayer().create();
player.initialize(video, url, true);
player.updateSettings({ 'streaming': { 'lowLatencyEnabled': true }});
player.updateSettings({'debug': {'logLevel': dashjs.Debug.LOG_LEVEL_WARNING }});

player.updateSettings({
'streaming': {
'abr': {
'useDefaultABRRules': false, 'ABRStrategy': 'abrL2A'//in case of own abr algo development
}
}
});
applyParameters();

return player;
}

function applyParameters() {
targetLatency = parseFloat(document.getElementById("target-latency").value, 10);
minDrift = parseFloat(document.getElementById("min-drift").value, 10);
catchupPlaybackRate = parseFloat(document.getElementById("catchup-playback-rate").value, 10);

player.updateSettings({
'streaming': {
'liveDelay': targetLatency,
'liveCatchUpMinDrift': minDrift,
'liveCatchUpPlaybackRate': catchupPlaybackRate
}
});
}
</script>

<style>
.video-wrapper {
display: flex;
flex-flow: row wrap;
}

.video-wrapper > div:nth-child(2) {
margin-left: 25px;
}

video {
width: 640px;
height: 360px;
}

ul {
margin: 0;
}

input {
width: 5em;
border: 1px solid gray;
padding: 0 4px 0 8px;
}

.help-container {
display: flex;
flex-flow: row wrap;
margin-top: 1em;
align-content: center;
background: white;
border: solid 1px #ddd;
padding: 0.5em;
}

.help-container > div {
width: 33.3%;
padding: 1em;
box-sizing: border-box;
}

.help-container h3 {
margin-top: 0;
}
</style>
</head>
<body>
<div>
<div class="video-wrapper">
<video controls="true" autoplay muted></video>
<div>
<div>
<form action="javascript:applyParameters()">
<fieldset>
<legend>Configurable parameters</legend>
<p>Target Latency (secs): <input type="number" id="target-latency" value="2" min="0" step="0.1"></p>
<p>Min. drift (secs): <input type="number" id="min-drift" value="0.3" min="0.0" max="0.5" step="0.01"></p>
<p>Catch-up playback rate (%): <input type="number" id="catchup-playback-rate" value="0.3" min="0.0" max="0.5" step="0.01"></p>
<button type="submit">Apply</button>
</fieldset>
</form>
</div>
<br>
<fieldset>
<legend>Current values</legend>
<ul>
<li>Latency: <span id="latency-tag"></span></li>
<li>Min. drift: <span id="mindrift-tag"></span></li>
<li>Playback rate: <span id="playbackrate-tag"></span></li>
<li>Buffer: <b><span id="buffer-tag"></span></b></li>
<li>Quality: <b><span id="quality-tag"></span></b></li>
</ul>
<div id="stats"></div>
</fieldset>
</div>
</div>

<p style="font-family:Arial,sans-serif; font-weight: bold; font-size: 1.1em">Concepts definition</p>
<div class="help-container">
<div id="latency-help">
<h3>Latency</h3>
<p>Lowering this value will lower latency but may decrease the player's ability to build a stable buffer.</p>
<p><a href="http://cdn.dashjs.org/latest/jsdoc/module-MediaPlayer.html#setLiveDelay__anchor" target="_blank">setLiveDelay() doc</a></p>
</div>

<div id="min-drift-help">
<h3>Min. drift</h3>
<p>Minimum latency deviation allowed before activating catch-up mechanism.</p>
<p><a href="http://cdn.dashjs.org/latest/jsdoc/module-MediaPlayer.html#setLowLatencyMinDrift__anchor" target="_blank">setLowLatencyMinDrift() doc</a></p>
</div>

<div id="catch-up-playback-rate-help">
<h3>Catch-up playback rate</h3>
<p>Maximum catch-up rate, as a percentage, for low latency live streams.</p>
<p><a href="http://cdn.dashjs.org/latest/jsdoc/module-MediaPlayer.html#setCatchUpPlaybackRate__anchor" target="_blank">setCatchUpPlaybackRate() doc</a></p>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const player = init();
const video = document.querySelector("video")
let stallingAt = null;
const CMA = () => {
let average = 0;
let count = 0;

return {
average(val) {
if (isNaN(val)) {
return 0;
}
average = average + ((val - average) / ++count);
return average;
},
}
}

setInterval(function() {
var dashMetrics = player.getDashMetrics();
var settings = player.getSettings();

var currentLatency = parseFloat(player.getCurrentLiveLatency(), 10);
document.getElementById("latency-tag").innerHTML = currentLatency + " secs";

document.getElementById("mindrift-tag").innerHTML = settings.streaming.liveCatchUpMinDrift + " secs";

var currentPlaybackRate = player.getPlaybackRate();
document.getElementById("playbackrate-tag").innerHTML = Math.round(currentPlaybackRate * 100) / 100;

var currentBuffer = dashMetrics.getCurrentBufferLevel("video");
document.getElementById("buffer-tag").innerHTML = currentBuffer + " secs";
}, 200);

player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_REQUESTED, (e) => {
console.warn('Quality changed requested', e);
});

player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_RENDERED, (e) => {
console.warn('Quality changed', e);
const quality = player.getBitrateInfoListFor('video')[e.newQuality];
if (!quality) {
return;
}
document.querySelector('#quality-tag').innerText = `${quality.width}x${quality.height}, ${quality.bitrate / 1000}Kbps`;
});

window.startRecording = () => {
console.info('Begin recording');

const latencyCMA = CMA();
const bufferCMA = CMA();
const history = window.abrHistory = {
switchHistory: [],
stallDuration: 0,
averageLatency: 0,
averageBufferLength: 0,
};

// Record the initial quality
recordSwitch(player.getBitrateInfoListFor('video')[player.getQualityFor('video')]);

let pollInterval = -1;
window.stopRecording = () => {
clearInterval(pollInterval);
checkStallResolution();
const lastQuality = history.switchHistory[history.switchHistory.length - 1];
if (lastQuality.end === null) {
lastQuality.end = video.currentTime;
}
console.warn('Run ended. Please navigate back to node for results.');
}

pollInterval = setInterval(function() {
const currentLatency = parseFloat(player.getCurrentLiveLatency(), 10);
const currentBuffer = player.getDashMetrics().getCurrentBufferLevel("video");
history.averageLatency = latencyCMA.average(currentLatency);
history.averageBufferLength = bufferCMA.average(currentBuffer);
console.log(history);
}, 200);


player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_RENDERED, (e) => {
recordSwitch(player.getBitrateInfoListFor('video')[e.newQuality]);
});

video.addEventListener('waiting', (e) => {
stallingAt = performance.now();
});

video.addEventListener('timeupdate', () => {
checkStallResolution();
});

function recordSwitch(quality) {
if (!quality) {
return;
}
const switchHistory = history.switchHistory;
const prev = switchHistory[switchHistory.length - 1];
const videoNow = video.currentTime;
if (prev) {
prev.end = videoNow;
}
switchHistory.push({ start: videoNow, end: null, quality });
}

function checkStallResolution() {
if (stallingAt !== null) {
history.stallDuration += (performance.now() - stallingAt);
stallingAt = null;
}
}
}
});
</script>
<script src="../highlighter.js"></script>
</body>
</html>


18 changes: 16 additions & 2 deletions src/streaming/constants/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,20 @@ class Constants {
*/
this.ABR_STRATEGY_BOLA = 'abrBola';

/**
* @constant {string} ABR_STRATEGY_L2A Adaptive bitrate algorithm based on Bola (buffer level)
* @memberof Constants#
* @static
*/
this.ABR_STRATEGY_L2A= 'abrL2A';

/**
* @constant {string} ABR_STRATEGY_L2A Adaptive bitrate algorithm based on L2A (online learning)
* @memberof Constants#
* @static
*/
this.ABR_STRATEGY_L2A = 'abrL2A';

/**
* @constant {string} ABR_STRATEGY_THROUGHPUT Adaptive bitrate algorithm based on throughput
* @memberof Constants#
Expand Down Expand Up @@ -170,6 +184,7 @@ class Constants {
* @static
*/
this.MISSING_CONFIG_ERROR = 'Missing config parameter(s)';

this.LOCATION = 'Location';
this.INITIALIZE = 'initialize';
this.TEXT_SHOWING = 'showing';
Expand All @@ -179,10 +194,9 @@ class Constants {
this.UTF8 = 'utf-8';
this.SCHEME_ID_URI = 'schemeIdUri';
this.START_TIME = 'starttime';

this.SERVICE_DESCRIPTION_LL_SCHEME = 'urn:dvb:dash:lowlatency:scope:2019';
this.SUPPLEMENTAL_PROPERTY_LL_SCHEME = 'urn:dvb:dash:lowlatency:critical:2019';
this.XML = 'XML';
this.ARRAY_BUFFER = 'ArrayBuffer';
}

constructor () {
Expand Down
Loading