Skip to content

Commit

Permalink
Define a threshold which controls catchup mechanism for low latency s…
Browse files Browse the repository at this point in the history
…treaming (#3316)

* Define a threshold which controls catchup mechanism for low latency streaming

* Consider maximum latency (liveCatchupMinDrift) for threshold calculation

* Update JsDoc
  • Loading branch information
dsilhavy committed Jun 29, 2020
1 parent 05b7d3c commit 641cc48
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 23 deletions.
1 change: 1 addition & 0 deletions samples/dash-if-reference-player/dashjs_config.json
Expand Up @@ -27,6 +27,7 @@
"liveCatchUpMinDrift": 0.02,
"liveCatchUpMaxDrift": 0,
"liveCatchUpPlaybackRate": 0.5,
"liveCatchupLatencyThreshold": null,
"lastBitrateCachingInfo": { "enabled": true, "ttl": 360000},
"lastMediaSettingsCachingInfo": { "enabled": true, "ttl": 360000},
"cacheLoadThresholds": {"video": 50, "audio": 5},
Expand Down
12 changes: 11 additions & 1 deletion src/core/Settings.js
Expand Up @@ -296,13 +296,22 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
*
* Note: Catch-up mechanism is only applied when playing low latency live streams.
* @property {number} [liveCatchUpPlaybackRate=0.5]
* Use this method to set the maximum catch up rate, as a percentage, for low latency live streams. In low latency mode,
* Use this parameter to set the maximum catch up rate, as a percentage, for low latency live streams. In low latency mode,
* when measured latency is higher/lower than the target one,
* dash.js increases/decreases playback rate respectively up to (+/-) the percentage defined with this method until target is reached.
*
* Valid values for catch up rate are in range 0-0.5 (0-50%). Set it to 0 to turn off live catch up feature.
*
* Note: Catch-up mechanism is only applied when playing low latency live streams.
* @property {number} [liveCatchupLatencyThreshold=NaN]
* Use this parameter to set the maximum threshold for which live catch up is applied. For instance, if this value is set to 8 seconds,
* then live catchup is only applied if the current live latency is equal or below 8 seconds. The reason behind this parameter is to avoid an increase
* of the playback rate if the user seeks within the DVR window.
*
* If no value is specified this will be twice the maximum live delay. The maximum live delay is either specified in the manifest as part of a ServiceDescriptor or calculated the following:
* maximumLiveDelay = targetDelay + liveCatchupMinDrift
*
* Note: Catch-up mechanism is only applied when playing low latency live streams.
* @property {module:Settings~CachingInfoSettings} [lastBitrateCachingInfo={enabled: true, ttl: 360000}]
* Set to false if you would like to disable the last known bit rate from being stored during playback and used
* to set the initial bit rate for subsequent playback within the expiration window.
Expand Down Expand Up @@ -398,6 +407,7 @@ function Settings() {
liveCatchUpMinDrift: 0.02,
liveCatchUpMaxDrift: 0,
liveCatchUpPlaybackRate: 0.5,
liveCatchupLatencyThreshold: NaN,
lastBitrateCachingInfo: {enabled: true, ttl: 360000},
lastMediaSettingsCachingInfo: {enabled: true, ttl: 360000},
cacheLoadThresholds: {video: 50, audio: 5},
Expand Down
8 changes: 6 additions & 2 deletions src/streaming/controllers/PlaybackController.js
Expand Up @@ -644,8 +644,12 @@ function PlaybackController() {
}

function needToCatchUp() {
return settings.get().streaming.liveCatchUpPlaybackRate > 0 && getTime() > 0 &&
Math.abs(getCurrentLiveLatency() - mediaPlayerModel.getLiveDelay()) > settings.get().streaming.liveCatchUpMinDrift;
const currentLiveLatency = getCurrentLiveLatency();
const latencyDrift = Math.abs(currentLiveLatency - mediaPlayerModel.getLiveDelay());
const liveCatchupLatencyThreshold = mediaPlayerModel.getLiveCatchupLatencyThreshold();

return settings.get().streaming.lowLatencyEnabled && settings.get().streaming.liveCatchUpPlaybackRate > 0 && getTime() > 0 &&
latencyDrift > settings.get().streaming.liveCatchUpMinDrift && (isNaN(liveCatchupLatencyThreshold) || currentLiveLatency <= liveCatchupLatencyThreshold ) ;
}

function startPlaybackCatchUp() {
Expand Down
65 changes: 45 additions & 20 deletions src/streaming/models/MediaPlayerModel.js
Expand Up @@ -33,7 +33,7 @@ import FactoryMaker from '../../core/FactoryMaker';
import Constants from '../constants/Constants';
import ABRRulesCollection from '../rules/abr/ABRRulesCollection';
import Settings from '../../core/Settings';
import { checkParameterType} from '../utils/SupervisorTools';
import {checkParameterType} from '../utils/SupervisorTools';


const DEFAULT_MIN_BUFFER_TIME = 12;
Expand All @@ -42,6 +42,7 @@ const DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH = 20;
const DEFAULT_LOW_LATENCY_LIVE_DELAY = 3.0;
const LOW_LATENCY_REDUCTION_FACTOR = 10;
const LOW_LATENCY_MULTIPLY_FACTOR = 5;
const DEFAULT_LIVE_LATENCY_CATCHUP_THRESHOLD_FACTOR = 2;

const DEFAULT_XHR_WITH_CREDENTIALS = false;

Expand All @@ -53,9 +54,9 @@ function MediaPlayerModel() {
customABRRule;

const DEFAULT_UTC_TIMING_SOURCE = {
scheme: 'urn:mpeg:dash:utc:http-xsdate:2014',
value: 'http://time.akamai.com/?iso&ms'
};
scheme: 'urn:mpeg:dash:utc:http-xsdate:2014',
value: 'http://time.akamai.com/?iso&ms'
};
const context = this.context;
const settings = Settings(context).getInstance();

Expand Down Expand Up @@ -144,6 +145,29 @@ function MediaPlayerModel() {
return settings.get().streaming.liveDelay;
}

function getLiveCatchupLatencyThreshold() {
try {
const liveCatchupLatencyThreshold = settings.get().streaming.liveCatchupLatencyThreshold;

if (liveCatchupLatencyThreshold !== null && !isNaN(liveCatchupLatencyThreshold)) {
return liveCatchupLatencyThreshold;
}

const liveDelay = getLiveDelay();
const liveCatchupMinDrift = settings.get().streaming.liveCatchUpMinDrift;
const maximumLiveDelay = !isNaN(liveDelay) && liveDelay ? !isNaN(liveCatchupMinDrift) ? settings.get().streaming.liveCatchUpMinDrift + getLiveDelay() : getLiveDelay() : NaN;

if (maximumLiveDelay && !isNaN(maximumLiveDelay)) {
return maximumLiveDelay * DEFAULT_LIVE_LATENCY_CATCHUP_THRESHOLD_FACTOR;
}

return NaN;

} catch (e) {
return NaN;
}
}

function addUTCTimingSource(schemeIdUri, value) {
removeUTCTimingSource(schemeIdUri, value); //check if it already exists and remove if so.
let vo = new UTCTiming();
Expand Down Expand Up @@ -200,22 +224,23 @@ function MediaPlayerModel() {
}

instance = {
getABRCustomRules: getABRCustomRules,
addABRCustomRule: addABRCustomRule,
removeABRCustomRule: removeABRCustomRule,
getStableBufferTime: getStableBufferTime,
getRetryAttemptsForType: getRetryAttemptsForType,
getRetryIntervalsForType: getRetryIntervalsForType,
getLiveDelay: getLiveDelay,
addUTCTimingSource: addUTCTimingSource,
removeUTCTimingSource: removeUTCTimingSource,
getUTCTimingSources: getUTCTimingSources,
clearDefaultUTCTimingSources: clearDefaultUTCTimingSources,
restoreDefaultUTCTimingSources: restoreDefaultUTCTimingSources,
setXHRWithCredentialsForType: setXHRWithCredentialsForType,
getXHRWithCredentialsForType: getXHRWithCredentialsForType,
getDefaultUtcTimingSource: getDefaultUtcTimingSource,
reset: reset
getABRCustomRules,
addABRCustomRule,
removeABRCustomRule,
getStableBufferTime,
getRetryAttemptsForType,
getRetryIntervalsForType,
getLiveDelay,
getLiveCatchupLatencyThreshold,
addUTCTimingSource,
removeUTCTimingSource,
getUTCTimingSources,
clearDefaultUTCTimingSources,
restoreDefaultUTCTimingSources,
setXHRWithCredentialsForType,
getXHRWithCredentialsForType,
getDefaultUtcTimingSource,
reset
};

setup();
Expand Down
20 changes: 20 additions & 0 deletions test/unit/streaming.models.MediaPlayerModel.js
Expand Up @@ -138,4 +138,24 @@ describe('MediaPlayerModel', function () {
expect(StableBufferTime).to.equal(50);
});

it('should configure liveCatchupLatencyThreshold', function () {
let liveCatchupLatencyThreshold = mediaPlayerModel.getLiveCatchupLatencyThreshold();
expect(liveCatchupLatencyThreshold).to.be.NaN; // jshint ignore:line

settings.update({streaming: {lowLatencyEnabled: true, liveDelay: 3, liveCatchUpMinDrift: 3}});

liveCatchupLatencyThreshold = mediaPlayerModel.getLiveCatchupLatencyThreshold();
expect(liveCatchupLatencyThreshold).to.equal(12);

settings.update({streaming: {liveCatchUpMinDrift: NaN}});

liveCatchupLatencyThreshold = mediaPlayerModel.getLiveCatchupLatencyThreshold();
expect(liveCatchupLatencyThreshold).to.equal(6);

settings.update({streaming: {liveCatchupLatencyThreshold: 50}});

liveCatchupLatencyThreshold = mediaPlayerModel.getLiveCatchupLatencyThreshold();
expect(liveCatchupLatencyThreshold).to.equal(50);
});

});

0 comments on commit 641cc48

Please sign in to comment.