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

Define a threshold which controls catchup mechanism for low latency streaming #3316

Merged
merged 3 commits into from Jun 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
});

});