Skip to content

Commit

Permalink
Fix/low latency (#4618)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
dsilhavy authored Nov 14, 2024
1 parent ffe08a7 commit f747060
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 43 deletions.
32 changes: 18 additions & 14 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1755,6 +1755,7 @@ declare namespace dashjs {
type: string

}

export interface AstInFutureEvent extends MediaPlayerEvent {
type: MediaPlayerEvents['AST_IN_FUTURE'];
delay: number;
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }; })
Expand Down Expand Up @@ -1112,7 +1112,7 @@ function Settings() {
},
buffer: {
enableSeekDecorrelationFix: false,
fastSwitchEnabled: true,
fastSwitchEnabled: null,
flushBufferAtTrackSwitch: false,
reuseExistingSourceBuffers: true,
bufferPruningInterval: 10,
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/StreamProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
36 changes: 18 additions & 18 deletions src/streaming/controllers/PlaybackController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 2 additions & 1 deletion src/streaming/controllers/ScheduleController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
28 changes: 21 additions & 7 deletions src/streaming/models/MediaPlayerModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -250,13 +251,26 @@ 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() {
}

instance = {
getCatchupMaxDrift,
getCatchupModeEnabled,
getBufferTimeDefault,
getFastSwitchEnabled,
getInitialBufferLevel,
getRetryAttemptsForType,
getRetryIntervalsForType,
Expand Down
4 changes: 4 additions & 0 deletions test/unit/test/streaming/streaming.MediaPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 } } });
Expand Down
29 changes: 29 additions & 0 deletions test/unit/test/streaming/streaming.models.MediaPlayerModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});

});

0 comments on commit f747060

Please sign in to comment.