Skip to content

Commit

Permalink
Feature enable low latency (#3684)
Browse files Browse the repository at this point in the history
Enable low latency streaming automatically if availabilityTimeComplete is set to false
  • Loading branch information
dsilhavy authored Sep 8, 2021
1 parent dcccdd3 commit ef00170
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 52 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ declare namespace dashjs {
abandonLoadTimeout?: number,
wallclockTimeUpdateInterval?: number,
lowLatencyEnabled?: boolean,
lowLatencyEnabledByManifest?: boolean,
manifestUpdateRetryInterval?: number,
cacheInitSegments?: boolean,
eventControllerRefreshDelay?: number,
Expand Down
40 changes: 25 additions & 15 deletions samples/dash-if-reference-player/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -682,8 +682,16 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'

$scope.togglelowLatencyMode = function () {
$scope.player.updateSettings({
'streaming': {
'lowLatencyEnabled': $scope.lowLatencyModeSelected
streaming: {
lowLatencyEnabled: $scope.lowLatencyModeSelected
}
});
};

$scope.toggleLowLatencyByManifestMode = function () {
$scope.player.updateSettings({
streaming: {
lowLatencyEnabledByManifest: $scope.lowLatencyEnabledByManifest
}
});
};
Expand Down Expand Up @@ -776,16 +784,17 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
}

var config = {
'streaming': {
'buffer': {
'stableBufferTime': $scope.defaultStableBufferDelay,
'bufferTimeAtTopQuality': $scope.defaultBufferTimeAtTopQuality,
'bufferTimeAtTopQualityLongForm': $scope.defaultBufferTimeAtTopQualityLongForm,
streaming: {
buffer: {
stableBufferTime: $scope.defaultStableBufferDelay,
bufferTimeAtTopQuality: $scope.defaultBufferTimeAtTopQuality,
bufferTimeAtTopQualityLongForm: $scope.defaultBufferTimeAtTopQualityLongForm,
},
'delay': {
'liveDelay': $scope.defaultLiveDelay
delay: {
liveDelay: $scope.defaultLiveDelay
},
'lowLatencyEnabled': $scope.lowLatencyModeSelected,
lowLatencyEnabled: $scope.lowLatencyModeSelected,
lowLatencyEnabledByManifest: $scope.lowLatencyEnabledByManifest,
abr: {},
cmcd: {}
}
Expand Down Expand Up @@ -1008,7 +1017,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
protectionData[input.drmKeySystem]['httpRequestHeaders'] = input.httpRequestHeaders;
}
} else {
alert("Kid and Key must be specified!");
alert('Kid and Key must be specified!');
}

} else {
Expand All @@ -1018,15 +1027,15 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
// Check if DRM-Priorisation is enabled
if (this.prioritiesEnabled) {
protectionData[input.drmKeySystem] = {
"serverURL": input.licenseServerUrl,
"priority": parseInt(input.priority)
'serverURL': input.licenseServerUrl,
'priority': parseInt(input.priority)
}
if (!angular.equals(input.httpRequestHeaders, {}))
protectionData[input.drmKeySystem]['httpRequestHeaders'] = input.httpRequestHeaders;

} else {
protectionData[input.drmKeySystem] = {
"serverURL": input.licenseServerUrl,
'serverURL': input.licenseServerUrl,
}
}

Expand All @@ -1052,7 +1061,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
}

} else {
console.log(input.licenseServerUrl, "is not a valid url!")
console.log(input.licenseServerUrl, 'is not a valid url!')
}

}
Expand Down Expand Up @@ -1535,6 +1544,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
$scope.defaultBufferTimeAtTopQuality = currentConfig.streaming.buffer.bufferTimeAtTopQuality;
$scope.defaultBufferTimeAtTopQualityLongForm = currentConfig.streaming.buffer.bufferTimeAtTopQualityLongForm;
$scope.lowLatencyModeSelected = currentConfig.streaming.lowLatencyEnabled;
$scope.lowLatencyEnabledByManifest = currentConfig.streaming.lowLatencyEnabledByManifest;
$scope.liveCatchupEnabled = currentConfig.streaming.liveCatchup.enabled;
}

Expand Down
7 changes: 2 additions & 5 deletions samples/dash-if-reference-player/app/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,16 +285,15 @@
"url": "https://akamaibroadcasteruseast.akamaized.net/cmaf/live/657078/akasource/out.mpd",
"name": "Akamai Low Latency Stream (Single Rate)",
"bufferConfig": {
"lowLatencyMode": true,
"liveDelay": 3
"liveDelay": 3,
"lowLatencyMode": true
},
"provider": "akamai"
},
{
"url": "https://cmafref.akamaized.net/cmaf/live-ull/2006350/akambr/out.mpd",
"name": "Akamai Low Latency Stream (Multi Rate)",
"bufferConfig": {
"lowLatencyMode": true,
"liveDelay": 3
},
"provider": "akamai"
Expand All @@ -303,7 +302,6 @@
"url": "https://livesim.dashif.org/livesim/chunkdur_1/ato_7/testpic4_8s/Manifest300.mpd",
"name": "Low Latency (Single-Rate) (livesim-chunked)",
"bufferConfig": {
"lowLatencyMode": true,
"liveDelay": 4
},
"provider": "dashif"
Expand All @@ -312,7 +310,6 @@
"url": "https://livesim.dashif.org/livesim/chunkdur_1/ato_7/testpic4_8s/Manifest.mpd",
"name": "Low Latency (Multi-Rate) (livesim-chunked)",
"bufferConfig": {
"lowLatencyMode": true,
"liveDelay": 4
},
"provider": "dashif"
Expand Down
8 changes: 7 additions & 1 deletion samples/dash-if-reference-player/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,13 @@
title="Enable low latency mode">
<input type="checkbox" id="lowLatencyModeCB" ng-model="lowLatencyModeSelected"
ng-change="togglelowLatencyMode()" ng-checked="lowLatencyModeSelected">
Low Latency Mode
Low latency mode
</label>
<label class="topcoat-checkbox" data-toggle="tooltip" data-placement="right"
title="Enable low latency mode by MPD attribute 'availabilityTimeComplete' ">
<input type="checkbox" id="lowLatencyModeEnabledByManifestCB" ng-model="lowLatencyEnabledByManifest"
ng-change="toggleLowLatencyByManifestMode()" ng-checked="lowLatencyEnabledByManifest">
Enable low latency mode by MPD
</label>
<label class="topcoat-checkbox" data-toggle="tooltip" data-placement="right"
title="Enable catchup mode for non low latency streams">
Expand Down
15 changes: 10 additions & 5 deletions src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* abandonLoadTimeout: 10000,
* wallclockTimeUpdateInterval: 100,
* lowLatencyEnabled: false,
* lowLatencyEnabledByManifest: true,
* manifestUpdateRetryInterval: 100,
* cacheInitSegments: true,
* eventControllerRefreshDelay: 100,
Expand Down Expand Up @@ -129,7 +130,7 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* },
* liveCatchup: {
* minDrift: 0.02,
* maxDrift: 0,
* maxDrift: 12,
* playbackRate: 0.5,
* latencyThreshold: 60,
* playbackBufferMin: 0.5,
Expand Down Expand Up @@ -377,6 +378,7 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* @property {boolean} [useManifestDateHeaderTimeSource=true]
* Allows you to enable the use of the Date Header, if exposed with CORS, as a timing source for live edge detection.
*
* The use of the date header will happen only after the other timing source that take precedence fail or are omitted as described.
* @property {number} [backgroundAttempts=2]
* Number of synchronization attempts to perform in the background after an initial synchronization request has been done. This is used to verify that the derived client-server offset is correct.
*
Expand Down Expand Up @@ -434,7 +436,7 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* LowLatencyMinDrift should be provided in seconds, and it uses values between 0.0 and 0.5.
*
* Note: Catch-up mechanism is only applied when playing low latency live streams.
* @property {number} [maxDrift=0]
* @property {number} [maxDrift=12]
* Use this method to set the maximum latency deviation allowed before dash.js to do a seeking to live position.
*
* In low latency mode, when the difference between the measured latency and the target one, as an absolute number, is higher than the one sets with this method, then dash.js does a seek to live edge position minus the target live delay.
Expand Down Expand Up @@ -644,9 +646,11 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* @property {number} [wallclockTimeUpdateInterval=50]
* How frequently the wallclockTimeUpdated internal event is triggered (in milliseconds).
* @property {boolean} [lowLatencyEnabled=false]
* Enable or disable low latency mode.
* Manually enable or disable low latency mode.
*
* @property {boolean} [lowLatencyEnabledByManifest=true]
* If this value is set to true we enable the low latency mode based on MPD attributes: Specifically in case "availabilityTimeComplete" of the current representation is set to false.
*
* The use of the date header will happen only after the other timing source that take precedence fail or are omitted as described.
* @property {number} [manifestUpdateRetryInterval=100]
* For live streams, set the interval-frequency in milliseconds at which dash.js will check if the current manifest is still processed before downloading the next manifest once the minimumUpdatePeriod time has.
* @property {boolean} [cacheInitSegments=true]
Expand Down Expand Up @@ -743,6 +747,7 @@ function Settings() {
abandonLoadTimeout: 10000,
wallclockTimeUpdateInterval: 100,
lowLatencyEnabled: false,
lowLatencyEnabledByManifest: true,
manifestUpdateRetryInterval: 100,
cacheInitSegments: false,
eventControllerRefreshDelay: 150,
Expand Down Expand Up @@ -812,7 +817,7 @@ function Settings() {
},
liveCatchup: {
minDrift: 0.02,
maxDrift: 0,
maxDrift: 12,
playbackRate: 0.5,
latencyThreshold: 60,
playbackBufferMin: 0.5,
Expand Down
6 changes: 0 additions & 6 deletions src/dash/SegmentBaseLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ function SegmentBaseLoader() {
boxParser,
requestModifier,
dashMetrics,
settings,
mediaPlayerModel,
urlLoader,
errors,
Expand All @@ -62,7 +61,6 @@ function SegmentBaseLoader() {
dashMetrics: dashMetrics,
mediaPlayerModel: mediaPlayerModel,
requestModifier: requestModifier,
useFetch: settings ? settings.get().streaming.lowLatencyEnabled : null,
boxParser: boxParser,
errors: errors,
urlUtils: urlUtils,
Expand All @@ -88,10 +86,6 @@ function SegmentBaseLoader() {
errHandler = config.errHandler;
}

if (config.settings) {
settings = config.settings;
}

if (config.boxParser) {
boxParser = config.boxParser;
}
Expand Down
3 changes: 0 additions & 3 deletions src/dash/WebmSegmentBaseLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ function WebmSegmentBaseLoader() {
dashMetrics,
mediaPlayerModel,
urlLoader,
settings,
errors,
baseURLController;

Expand Down Expand Up @@ -94,7 +93,6 @@ function WebmSegmentBaseLoader() {
dashMetrics: dashMetrics,
mediaPlayerModel: mediaPlayerModel,
requestModifier: requestModifier,
useFetch: settings ? settings.get().streaming.lowLatencyEnabled : null,
errors: errors
});
}
Expand All @@ -107,7 +105,6 @@ function WebmSegmentBaseLoader() {
dashMetrics = config.dashMetrics;
mediaPlayerModel = config.mediaPlayerModel;
errHandler = config.errHandler;
settings = config.settings;
errors = config.errors;
logger = config.debug.getLogger(instance);
requestModifier = config.requestModifier;
Expand Down
19 changes: 13 additions & 6 deletions src/dash/controllers/RepresentationController.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ function RepresentationController(config) {

voAvailableRepresentations = availableRepresentations;

currentVoRepresentation = getRepresentationForQuality(quality);
const rep = getRepresentationForQuality(quality)
_setCurrentVoRepresentation(rep);
realAdaptation = newRealAdaptation;

if (type !== Constants.VIDEO && type !== Constants.AUDIO && (type !== Constants.TEXT || !isFragmented)) {
Expand Down Expand Up @@ -203,7 +204,7 @@ function RepresentationController(config) {
return representation;
}

function addRepresentationSwitch() {
function _addRepresentationSwitch() {
checkConfig();
const now = new Date();
const currentRepresentation = getCurrentRepresentation();
Expand All @@ -214,9 +215,10 @@ function RepresentationController(config) {

eventBus.trigger(MediaPlayerEvents.REPRESENTATION_SWITCH, {
mediaType: type,
streamId: streamInfo.id,
currentRepresentation,
numberOfRepresentations: voAvailableRepresentations.length
})
}, { streamId: streamInfo.id, mediaType: type })
}

function getRepresentationForQuality(quality) {
Expand Down Expand Up @@ -285,15 +287,20 @@ function RepresentationController(config) {
repSwitch = dashMetrics.getCurrentRepresentationSwitch(getCurrentRepresentation().adaptation.type);

if (!repSwitch) {
addRepresentationSwitch();
_addRepresentationSwitch();
}
endDataUpdate();
}
}

function prepareQualityChange(newQuality) {
currentVoRepresentation = getRepresentationForQuality(newQuality);
addRepresentationSwitch();
const newRep = getRepresentationForQuality(newQuality)
_setCurrentVoRepresentation(newRep);
_addRepresentationSwitch();
}

function _setCurrentVoRepresentation(value) {
currentVoRepresentation = value;
}

function onManifestValidityChanged(e) {
Expand Down
1 change: 0 additions & 1 deletion src/streaming/FragmentLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ function FragmentLoader(config) {
dashMetrics: config.dashMetrics,
mediaPlayerModel: config.mediaPlayerModel,
requestModifier: config.requestModifier,
useFetch: config.settings.get().streaming.lowLatencyEnabled,
urlUtils: urlUtils,
constants: Constants,
boxParser: config.boxParser,
Expand Down
1 change: 0 additions & 1 deletion src/streaming/ManifestLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ function ManifestLoader(config) {
dashMetrics: config.dashMetrics,
mediaPlayerModel: config.mediaPlayerModel,
requestModifier: config.requestModifier,
useFetch: config.settings.get().streaming.lowLatencyEnabled,
urlUtils: urlUtils,
constants: Constants,
dashConstants: DashConstants,
Expand Down
1 change: 0 additions & 1 deletion src/streaming/XlinkLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ function XlinkLoader(config) {
dashMetrics: config.dashMetrics,
mediaPlayerModel: config.mediaPlayerModel,
requestModifier: config.requestModifier,
useFetch: config.settings ? config.settings.get().streaming.lowLatencyEnabled : null,
errors: Errors
});

Expand Down
25 changes: 25 additions & 0 deletions src/streaming/controllers/PlaybackController.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ function PlaybackController() {
eventBus.on(MediaPlayerEvents.PLAYBACK_TIME_UPDATED, _onPlaybackProgression, this);
eventBus.on(MediaPlayerEvents.PLAYBACK_ENDED, _onPlaybackEnded, this, { priority: EventBus.EVENT_PRIORITY_HIGH });
eventBus.on(MediaPlayerEvents.STREAM_INITIALIZING, _onStreamInitializing, this);
eventBus.on(MediaPlayerEvents.REPRESENTATION_SWITCH, _onRepresentationSwitch, this);

if (playOnceInitialized) {
playOnceInitialized = false;
Expand Down Expand Up @@ -353,6 +354,7 @@ function PlaybackController() {
eventBus.off(MediaPlayerEvents.PLAYBACK_TIME_UPDATED, _onPlaybackProgression, this);
eventBus.off(MediaPlayerEvents.PLAYBACK_ENDED, _onPlaybackEnded, this);
eventBus.off(MediaPlayerEvents.STREAM_INITIALIZING, _onStreamInitializing, this);
eventBus.off(MediaPlayerEvents.REPRESENTATION_SWITCH, _onRepresentationSwitch, this);
videoModel.setPlaybackRate(1.0, true);
stopUpdatingWallclockTime();
removeAllListeners();
Expand Down Expand Up @@ -916,6 +918,29 @@ function PlaybackController() {
_checkEnableLowLatency(e.mediaInfo);
}

/**
* We enable low latency playback if for the current representation availabilityTimeComplete is set to false
* @param e
* @private
*/
function _onRepresentationSwitch(e) {
const activeStreamInfo = streamController.getActiveStreamInfo();
if (!settings.get().streaming.lowLatencyEnabledByManifest || !e || !activeStreamInfo || !e.currentRepresentation || !e.streamId || e.streamId !== activeStreamInfo.id || !e.mediaType || (e.mediaType !== Constants.VIDEO && e.mediaType !== Constants.AUDIO)) {
return;
}

const lowLatencyEnabled = !e.currentRepresentation.availabilityTimeComplete;

if (lowLatencyEnabled) {
settings.update({
streaming: {
lowLatencyEnabled: lowLatencyEnabled
}
});
}
}


function _checkEnableLowLatency(mediaInfo) {
if (mediaInfo && mediaInfo.supplementalProperties &&
mediaInfo.supplementalProperties[Constants.SUPPLEMENTAL_PROPERTY_LL_SCHEME] === 'true') {
Expand Down
Loading

0 comments on commit ef00170

Please sign in to comment.