Skip to content

Commit f747060

Browse files
authored
Fix/low latency (Dash-Industry-Forum#4618)
* Fix scheduling for low latency streams * Adjust webpack/babel config for maximum compatibility * Proxy the fastSwitchEnabled setting through MediaPlayerModel to deactivate it by default for low latency streaming * Use Chrome 51 as the babel target
1 parent ffe08a7 commit f747060

File tree

8 files changed

+95
-43
lines changed

8 files changed

+95
-43
lines changed

index.d.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,6 +1755,7 @@ declare namespace dashjs {
17551755
type: string
17561756

17571757
}
1758+
17581759
export interface AstInFutureEvent extends MediaPlayerEvent {
17591760
type: MediaPlayerEvents['AST_IN_FUTURE'];
17601761
delay: number;
@@ -2789,41 +2790,44 @@ declare namespace dashjs {
27892790
}
27902791

27912792
export interface MediaPlayerModel {
2792-
getABRCustomRules(): object[];
27932793

27942794
addABRCustomRule(type: string, rulename: string, rule: any): void;
27952795

2796-
removeABRCustomRule(rulename: string): void;
2796+
addUTCTimingSource(schemeIdUri: string, value: string): void;
27972797

2798-
getInitialBufferLevel(): number;
2798+
clearDefaultUTCTimingSources(): void;
2799+
2800+
getABRCustomRules(): object[];
27992801

28002802
getBufferTimeDefault(): number;
28012803

2802-
getRetryAttemptsForType(type: string): number;
2804+
getDefaultUtcTimingSource(): UTCTiming;
28032805

2804-
getRetryIntervalsForType(type: string): any;
2806+
getFastSwitchEnabled(): boolean;
28052807

2806-
getLiveDelay(): number;
2808+
getInitialBufferLevel(): number;
28072809

28082810
getLiveCatchupLatencyThreshold(): number;
28092811

2810-
addUTCTimingSource(schemeIdUri: string, value: string): void;
2812+
getLiveDelay(): number;
28112813

2812-
removeUTCTimingSource(schemeIdUri: string, value: string): void;
2814+
getRetryAttemptsForType(type: string): number;
2815+
2816+
getRetryIntervalsForType(type: string): any;
28132817

28142818
getUTCTimingSources(): UTCTiming[];
28152819

2816-
clearDefaultUTCTimingSources(): void;
2820+
getXHRWithCredentialsForType(type: string): object;
28172821

2818-
restoreDefaultUTCTimingSources(): void;
2822+
removeABRCustomRule(rulename: string): void;
28192823

2820-
setXHRWithCredentialsForType(type: string, value: any): void;
2824+
removeUTCTimingSource(schemeIdUri: string, value: string): void;
28212825

2822-
getXHRWithCredentialsForType(type: string): object;
2826+
reset(): void;
28232827

2824-
getDefaultUtcTimingSource(): UTCTiming;
2828+
restoreDefaultUTCTimingSources(): void;
28252829

2826-
reset(): void;
2830+
setXHRWithCredentialsForType(type: string, value: any): void;
28272831
}
28282832

28292833
export interface MetricsModel {

src/core/Settings.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ import Events from './events/Events.js';
7777
* { schemeIdUri: Constants.FONT_DOWNLOAD_DVB_SCHEME },
7878
* { schemeIdUri: Constants.COLOUR_PRIMARIES_SCHEME_ID_URI, value: /1|5|6|7/ },
7979
* { schemeIdUri: Constants.URL_QUERY_INFO_SCHEME },
80-
* { schemeIdUri: Constants.EXT_URL_QUERY_INFO_SCHEME },
80+
* { schemeIdUri: Constants.EXT_URL_QUERY_INFO_SCHEME },
8181
* { schemeIdUri: Constants.MATRIX_COEFFICIENTS_SCHEME_ID_URI, value: /0|1|5|6/ },
8282
* { schemeIdUri: Constants.TRANSFER_CHARACTERISTICS_SCHEME_ID_URI, value: /1|6|13|14|15/ },
8383
* ...Constants.THUMBNAILS_SCHEME_ID_URIS.map(ep => { return { 'schemeIdUri': ep }; })
@@ -1112,7 +1112,7 @@ function Settings() {
11121112
},
11131113
buffer: {
11141114
enableSeekDecorrelationFix: false,
1115-
fastSwitchEnabled: true,
1115+
fastSwitchEnabled: null,
11161116
flushBufferAtTrackSwitch: false,
11171117
reuseExistingSourceBuffers: true,
11181118
bufferPruningInterval: 10,

src/streaming/StreamProcessor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@ function StreamProcessor(config) {
807807
}
808808

809809
// If fast switch is enabled we check if we are supposed to replace existing stuff in the buffer
810-
else if (settings.get().streaming.buffer.fastSwitchEnabled) {
810+
else if (mediaPlayerModel.getFastSwitchEnabled()) {
811811
_prepareForFastQualitySwitch(newRepresentation, oldRepresentation);
812812
}
813813

src/streaming/controllers/PlaybackController.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -916,37 +916,37 @@ function PlaybackController() {
916916
}
917917

918918
instance = {
919-
initialize,
920-
setConfig,
921-
getTimeToStreamEnd,
919+
computeAndSetLiveDelay,
920+
getAvailabilityStartTime,
922921
getBufferLevel,
923-
getPlaybackStalled,
924-
getTime,
925-
getLowLatencyModeEnabled,
922+
getCurrentLiveLatency,
923+
getEnded,
926924
getInitialCatchupModeActivated,
925+
getIsDynamic,
927926
getIsManifestUpdateInProgress,
927+
getLiveDelay,
928+
getLowLatencyModeEnabled,
929+
getOriginalLiveDelay,
928930
getPlaybackRate,
931+
getPlaybackStalled,
929932
getPlayedRanges,
930-
getEnded,
931-
getIsDynamic,
932933
getStreamController,
933-
computeAndSetLiveDelay,
934-
getLiveDelay,
935-
getOriginalLiveDelay,
936-
getCurrentLiveLatency,
937-
play,
934+
getStreamEndTime,
935+
getTime,
936+
getTimeToStreamEnd,
937+
initialize,
938938
isPaused,
939939
isProgressing,
940+
isSeeking,
940941
isStalled,
941942
pause,
942-
isSeeking,
943-
getStreamEndTime,
943+
play,
944+
reset,
944945
seek,
945-
seekToOriginalLive,
946946
seekToCurrentLive,
947-
reset,
947+
seekToOriginalLive,
948+
setConfig,
948949
updateCurrentTime,
949-
getAvailabilityStartTime
950950
};
951951

952952
setup();

src/streaming/controllers/ScheduleController.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ function ScheduleController(config) {
228228
const bufferLevel = dashMetrics.getCurrentBufferLevel(type);
229229
const bufferTarget = getBufferTarget();
230230

231-
if (bufferTarget <= segmentDurationToAddToBufferLevel) {
231+
// If the buffer target is smaller than the segment duration we do not take it into account. For low latency playback do not delay the buffering.
232+
if (bufferTarget <= segmentDurationToAddToBufferLevel || playbackController.getLowLatencyModeEnabled()) {
232233
segmentDurationToAddToBufferLevel = 0;
233234
}
234235

src/streaming/models/MediaPlayerModel.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,16 @@ import Debug from '../../core/Debug.js';
3232
import FactoryMaker from '../../core/FactoryMaker.js';
3333
import Settings from '../../core/Settings.js';
3434

35+
const CATCHUP_PLAYBACK_RATE_MAX_LIMIT = 1;
36+
const CATCHUP_PLAYBACK_RATE_MIN_LIMIT = -0.5;
37+
const DEFAULT_CATCHUP_MAX_DRIFT = 12;
38+
const DEFAULT_CATCHUP_PLAYBACK_RATE_MAX = 0.5;
39+
const DEFAULT_CATCHUP_PLAYBACK_RATE_MIN = -0.5;
3540
const DEFAULT_MIN_BUFFER_TIME = 12;
3641
const DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH = 20;
37-
const LOW_LATENCY_REDUCTION_FACTOR = 10;
3842
const LOW_LATENCY_MULTIPLY_FACTOR = 5;
39-
const DEFAULT_CATCHUP_MAX_DRIFT = 12;
40-
const DEFAULT_CATCHUP_PLAYBACK_RATE_MIN = -0.5;
41-
const DEFAULT_CATCHUP_PLAYBACK_RATE_MAX = 0.5;
42-
const CATCHUP_PLAYBACK_RATE_MIN_LIMIT = -0.5;
43-
const CATCHUP_PLAYBACK_RATE_MAX_LIMIT = 1;
43+
const LOW_LATENCY_REDUCTION_FACTOR = 10;
44+
4445

4546
/**
4647
* We use this model as a wrapper/proxy between Settings.js and classes that are using parameters from Settings.js.
@@ -222,7 +223,7 @@ function MediaPlayerModel() {
222223
* @return {number}
223224
*/
224225
function getBufferTimeDefault() {
225-
let bufferTimeDefault = settings.get().streaming.buffer.bufferTimeDefault > 0 ? settings.get().streaming.buffer.bufferTimeDefault : settings.get().streaming.buffer.fastSwitchEnabled ? DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH : DEFAULT_MIN_BUFFER_TIME;
226+
let bufferTimeDefault = settings.get().streaming.buffer.bufferTimeDefault > 0 ? settings.get().streaming.buffer.bufferTimeDefault : getFastSwitchEnabled() ? DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH : DEFAULT_MIN_BUFFER_TIME;
226227
const liveDelay = playbackController.getLiveDelay();
227228

228229
return !isNaN(liveDelay) && liveDelay > 0 ? Math.min(bufferTimeDefault, liveDelay) : bufferTimeDefault;
@@ -250,13 +251,26 @@ function MediaPlayerModel() {
250251
return playbackController.getLowLatencyModeEnabled() ? settings.get().streaming.retryIntervals[type] / lowLatencyReductionFactor : settings.get().streaming.retryIntervals[type];
251252
}
252253

254+
/**
255+
* Returns whether the fast switch mode is defined in the settings options. If not we enable it by default but only for non low-latency playback.
256+
* @return {boolean}
257+
*/
258+
function getFastSwitchEnabled() {
259+
if (settings.get().streaming.buffer.fastSwitchEnabled !== null) {
260+
return settings.get().streaming.buffer.fastSwitchEnabled;
261+
}
262+
263+
return !playbackController.getLowLatencyModeEnabled();
264+
}
265+
253266
function reset() {
254267
}
255268

256269
instance = {
257270
getCatchupMaxDrift,
258271
getCatchupModeEnabled,
259272
getBufferTimeDefault,
273+
getFastSwitchEnabled,
260274
getInitialBufferLevel,
261275
getRetryAttemptsForType,
262276
getRetryIntervalsForType,

test/unit/test/streaming/streaming.MediaPlayer.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,10 @@ describe('MediaPlayer', function () {
658658

659659
it('should configure fastSwitchEnabled', function () {
660660
let fastSwitchEnabled = player.getSettings().streaming.buffer.fastSwitchEnabled;
661+
expect(fastSwitchEnabled).to.be.null;
662+
663+
player.updateSettings({ 'streaming': { 'buffer': { 'fastSwitchEnabled': true } } });
664+
fastSwitchEnabled = player.getSettings().streaming.buffer.fastSwitchEnabled;
661665
expect(fastSwitchEnabled).to.be.true;
662666

663667
player.updateSettings({ 'streaming': { 'buffer': { 'fastSwitchEnabled': false } } });

test/unit/test/streaming/streaming.models.MediaPlayerModel.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,4 +323,33 @@ describe('MediaPlayerModel', function () {
323323
expect(value).to.equal(bufferTimeDefault);
324324
});
325325

326+
it('should return the value provided via the settings for fastSwitchEnabled', function () {
327+
const s = { streaming: { buffer: { fastSwitchEnabled: true } } };
328+
playbackController.getLowLatencyModeEnabled = () => {
329+
return true;
330+
}
331+
settings.update(s);
332+
333+
let value = mediaPlayerModel.getFastSwitchEnabled();
334+
expect(value).to.equal(true);
335+
});
336+
337+
it('should return true for fastSwitchEnabled in case the player is not operating in low latency mode', function () {
338+
playbackController.getLowLatencyModeEnabled = () => {
339+
return false;
340+
}
341+
342+
let value = mediaPlayerModel.getFastSwitchEnabled();
343+
expect(value).to.equal(true);
344+
});
345+
346+
it('should return false for fastSwitchEnabled in case the player is operating in low latency mode', function () {
347+
playbackController.getLowLatencyModeEnabled = () => {
348+
return true;
349+
}
350+
351+
let value = mediaPlayerModel.getFastSwitchEnabled();
352+
expect(value).to.equal(false);
353+
});
354+
326355
});

0 commit comments

Comments
 (0)