diff --git a/index.d.ts b/index.d.ts index ccd090d8ff..c91f2841d9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1755,6 +1755,7 @@ declare namespace dashjs { type: string } + export interface AstInFutureEvent extends MediaPlayerEvent { type: MediaPlayerEvents['AST_IN_FUTURE']; delay: number; @@ -2789,41 +2790,44 @@ declare namespace dashjs { } export interface MediaPlayerModel { - getABRCustomRules(): object[]; addABRCustomRule(type: string, rulename: string, rule: any): void; - removeABRCustomRule(rulename: string): void; + addUTCTimingSource(schemeIdUri: string, value: string): void; - getInitialBufferLevel(): number; + clearDefaultUTCTimingSources(): void; + + getABRCustomRules(): object[]; getBufferTimeDefault(): number; - getRetryAttemptsForType(type: string): number; + getDefaultUtcTimingSource(): UTCTiming; - getRetryIntervalsForType(type: string): any; + getFastSwitchEnabled(): boolean; - getLiveDelay(): number; + getInitialBufferLevel(): number; getLiveCatchupLatencyThreshold(): number; - addUTCTimingSource(schemeIdUri: string, value: string): void; + getLiveDelay(): number; - removeUTCTimingSource(schemeIdUri: string, value: string): void; + getRetryAttemptsForType(type: string): number; + + getRetryIntervalsForType(type: string): any; getUTCTimingSources(): UTCTiming[]; - clearDefaultUTCTimingSources(): void; + getXHRWithCredentialsForType(type: string): object; - restoreDefaultUTCTimingSources(): void; + removeABRCustomRule(rulename: string): void; - setXHRWithCredentialsForType(type: string, value: any): void; + removeUTCTimingSource(schemeIdUri: string, value: string): void; - getXHRWithCredentialsForType(type: string): object; + reset(): void; - getDefaultUtcTimingSource(): UTCTiming; + restoreDefaultUTCTimingSources(): void; - reset(): void; + setXHRWithCredentialsForType(type: string, value: any): void; } export interface MetricsModel { diff --git a/src/core/Settings.js b/src/core/Settings.js index 93007303a5..422a032e0d 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -77,7 +77,7 @@ import Events from './events/Events.js'; * { schemeIdUri: Constants.FONT_DOWNLOAD_DVB_SCHEME }, * { schemeIdUri: Constants.COLOUR_PRIMARIES_SCHEME_ID_URI, value: /1|5|6|7/ }, * { schemeIdUri: Constants.URL_QUERY_INFO_SCHEME }, - * { schemeIdUri: Constants.EXT_URL_QUERY_INFO_SCHEME }, + * { schemeIdUri: Constants.EXT_URL_QUERY_INFO_SCHEME }, * { schemeIdUri: Constants.MATRIX_COEFFICIENTS_SCHEME_ID_URI, value: /0|1|5|6/ }, * { schemeIdUri: Constants.TRANSFER_CHARACTERISTICS_SCHEME_ID_URI, value: /1|6|13|14|15/ }, * ...Constants.THUMBNAILS_SCHEME_ID_URIS.map(ep => { return { 'schemeIdUri': ep }; }) @@ -1112,7 +1112,7 @@ function Settings() { }, buffer: { enableSeekDecorrelationFix: false, - fastSwitchEnabled: true, + fastSwitchEnabled: null, flushBufferAtTrackSwitch: false, reuseExistingSourceBuffers: true, bufferPruningInterval: 10, diff --git a/src/streaming/StreamProcessor.js b/src/streaming/StreamProcessor.js index d046d0cf22..a6ccd073de 100644 --- a/src/streaming/StreamProcessor.js +++ b/src/streaming/StreamProcessor.js @@ -807,7 +807,7 @@ function StreamProcessor(config) { } // If fast switch is enabled we check if we are supposed to replace existing stuff in the buffer - else if (settings.get().streaming.buffer.fastSwitchEnabled) { + else if (mediaPlayerModel.getFastSwitchEnabled()) { _prepareForFastQualitySwitch(newRepresentation, oldRepresentation); } diff --git a/src/streaming/controllers/PlaybackController.js b/src/streaming/controllers/PlaybackController.js index cc5503aa77..fc2269bc71 100644 --- a/src/streaming/controllers/PlaybackController.js +++ b/src/streaming/controllers/PlaybackController.js @@ -916,37 +916,37 @@ function PlaybackController() { } instance = { - initialize, - setConfig, - getTimeToStreamEnd, + computeAndSetLiveDelay, + getAvailabilityStartTime, getBufferLevel, - getPlaybackStalled, - getTime, - getLowLatencyModeEnabled, + getCurrentLiveLatency, + getEnded, getInitialCatchupModeActivated, + getIsDynamic, getIsManifestUpdateInProgress, + getLiveDelay, + getLowLatencyModeEnabled, + getOriginalLiveDelay, getPlaybackRate, + getPlaybackStalled, getPlayedRanges, - getEnded, - getIsDynamic, getStreamController, - computeAndSetLiveDelay, - getLiveDelay, - getOriginalLiveDelay, - getCurrentLiveLatency, - play, + getStreamEndTime, + getTime, + getTimeToStreamEnd, + initialize, isPaused, isProgressing, + isSeeking, isStalled, pause, - isSeeking, - getStreamEndTime, + play, + reset, seek, - seekToOriginalLive, seekToCurrentLive, - reset, + seekToOriginalLive, + setConfig, updateCurrentTime, - getAvailabilityStartTime }; setup(); diff --git a/src/streaming/controllers/ScheduleController.js b/src/streaming/controllers/ScheduleController.js index 3e320bb4d4..bdebc79752 100644 --- a/src/streaming/controllers/ScheduleController.js +++ b/src/streaming/controllers/ScheduleController.js @@ -228,7 +228,8 @@ function ScheduleController(config) { const bufferLevel = dashMetrics.getCurrentBufferLevel(type); const bufferTarget = getBufferTarget(); - if (bufferTarget <= segmentDurationToAddToBufferLevel) { + // 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. + if (bufferTarget <= segmentDurationToAddToBufferLevel || playbackController.getLowLatencyModeEnabled()) { segmentDurationToAddToBufferLevel = 0; } diff --git a/src/streaming/models/MediaPlayerModel.js b/src/streaming/models/MediaPlayerModel.js index 9a50afab97..f4966b5ef3 100644 --- a/src/streaming/models/MediaPlayerModel.js +++ b/src/streaming/models/MediaPlayerModel.js @@ -32,15 +32,16 @@ import Debug from '../../core/Debug.js'; import FactoryMaker from '../../core/FactoryMaker.js'; import Settings from '../../core/Settings.js'; +const CATCHUP_PLAYBACK_RATE_MAX_LIMIT = 1; +const CATCHUP_PLAYBACK_RATE_MIN_LIMIT = -0.5; +const DEFAULT_CATCHUP_MAX_DRIFT = 12; +const DEFAULT_CATCHUP_PLAYBACK_RATE_MAX = 0.5; +const DEFAULT_CATCHUP_PLAYBACK_RATE_MIN = -0.5; const DEFAULT_MIN_BUFFER_TIME = 12; const DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH = 20; -const LOW_LATENCY_REDUCTION_FACTOR = 10; const LOW_LATENCY_MULTIPLY_FACTOR = 5; -const DEFAULT_CATCHUP_MAX_DRIFT = 12; -const DEFAULT_CATCHUP_PLAYBACK_RATE_MIN = -0.5; -const DEFAULT_CATCHUP_PLAYBACK_RATE_MAX = 0.5; -const CATCHUP_PLAYBACK_RATE_MIN_LIMIT = -0.5; -const CATCHUP_PLAYBACK_RATE_MAX_LIMIT = 1; +const LOW_LATENCY_REDUCTION_FACTOR = 10; + /** * 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() { * @return {number} */ function getBufferTimeDefault() { - 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; + let bufferTimeDefault = settings.get().streaming.buffer.bufferTimeDefault > 0 ? settings.get().streaming.buffer.bufferTimeDefault : getFastSwitchEnabled() ? DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH : DEFAULT_MIN_BUFFER_TIME; const liveDelay = playbackController.getLiveDelay(); return !isNaN(liveDelay) && liveDelay > 0 ? Math.min(bufferTimeDefault, liveDelay) : bufferTimeDefault; @@ -250,6 +251,18 @@ function MediaPlayerModel() { return playbackController.getLowLatencyModeEnabled() ? settings.get().streaming.retryIntervals[type] / lowLatencyReductionFactor : settings.get().streaming.retryIntervals[type]; } + /** + * 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. + * @return {boolean} + */ + function getFastSwitchEnabled() { + if (settings.get().streaming.buffer.fastSwitchEnabled !== null) { + return settings.get().streaming.buffer.fastSwitchEnabled; + } + + return !playbackController.getLowLatencyModeEnabled(); + } + function reset() { } @@ -257,6 +270,7 @@ function MediaPlayerModel() { getCatchupMaxDrift, getCatchupModeEnabled, getBufferTimeDefault, + getFastSwitchEnabled, getInitialBufferLevel, getRetryAttemptsForType, getRetryIntervalsForType, diff --git a/test/unit/test/streaming/streaming.MediaPlayer.js b/test/unit/test/streaming/streaming.MediaPlayer.js index 12dacb5b64..5b41c084ae 100644 --- a/test/unit/test/streaming/streaming.MediaPlayer.js +++ b/test/unit/test/streaming/streaming.MediaPlayer.js @@ -658,6 +658,10 @@ describe('MediaPlayer', function () { it('should configure fastSwitchEnabled', function () { let fastSwitchEnabled = player.getSettings().streaming.buffer.fastSwitchEnabled; + expect(fastSwitchEnabled).to.be.null; + + player.updateSettings({ 'streaming': { 'buffer': { 'fastSwitchEnabled': true } } }); + fastSwitchEnabled = player.getSettings().streaming.buffer.fastSwitchEnabled; expect(fastSwitchEnabled).to.be.true; player.updateSettings({ 'streaming': { 'buffer': { 'fastSwitchEnabled': false } } }); diff --git a/test/unit/test/streaming/streaming.models.MediaPlayerModel.js b/test/unit/test/streaming/streaming.models.MediaPlayerModel.js index 2382d95479..f3520b5a3d 100644 --- a/test/unit/test/streaming/streaming.models.MediaPlayerModel.js +++ b/test/unit/test/streaming/streaming.models.MediaPlayerModel.js @@ -323,4 +323,33 @@ describe('MediaPlayerModel', function () { expect(value).to.equal(bufferTimeDefault); }); + it('should return the value provided via the settings for fastSwitchEnabled', function () { + const s = { streaming: { buffer: { fastSwitchEnabled: true } } }; + playbackController.getLowLatencyModeEnabled = () => { + return true; + } + settings.update(s); + + let value = mediaPlayerModel.getFastSwitchEnabled(); + expect(value).to.equal(true); + }); + + it('should return true for fastSwitchEnabled in case the player is not operating in low latency mode', function () { + playbackController.getLowLatencyModeEnabled = () => { + return false; + } + + let value = mediaPlayerModel.getFastSwitchEnabled(); + expect(value).to.equal(true); + }); + + it('should return false for fastSwitchEnabled in case the player is operating in low latency mode', function () { + playbackController.getLowLatencyModeEnabled = () => { + return true; + } + + let value = mediaPlayerModel.getFastSwitchEnabled(); + expect(value).to.equal(false); + }); + });