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

Split stableBufferTime into hybridSwitchBufferTime and stableBufferTime #76

Merged
merged 8 commits into from
Aug 7, 2023
3 changes: 3 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,7 @@ declare namespace dashjs {
bufferTimeAtTopQualityLongForm?: number,
initialBufferLevel?: number,
stableBufferTime?: number,
hybridSwitchBufferTime?: number,
longFormContentDurationThreshold?: number,
stallThreshold?: number,
useAppendWindow?: boolean,
Expand Down Expand Up @@ -2582,6 +2583,8 @@ declare namespace dashjs {
getInitialBufferLevel(): number;

getStableBufferTime(): number;

getHybridSwitchBufferTime(): number;

getRetryAttemptsForType(type: string): number;

Expand Down
4 changes: 4 additions & 0 deletions src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ import Events from './events/Events';
* bufferTimeAtTopQualityLongForm: 60,
* initialBufferLevel: NaN,
* stableBufferTime: 12,
* hybridSwitchBufferTime: NaN,
* longFormContentDurationThreshold: 600,
* stallThreshold: 0.3,
* useAppendWindow: true,
Expand Down Expand Up @@ -313,6 +314,8 @@ import Events from './events/Events';
* Initial buffer level before playback starts
* @property {number} [stableBufferTime=12]
* The time that the internal buffer target will be set to post startup/seeks (NOT top quality).
* @property {number} [hybridSwitchBufferTime=NaN]
* The buffer time that the hybrid rule will switch between throughput and BOLA at. Defaults to the value of `stableBufferTime`.
*
* When the time is set higher than the default you will have to wait longer to see automatic bitrate switches but will have a larger buffer which will increase stability.
* @property {number} [stallThreshold=0.3]
Expand Down Expand Up @@ -863,6 +866,7 @@ function Settings() {
bufferTimeAtTopQualityLongForm: 60,
initialBufferLevel: NaN,
stableBufferTime: 12,
hybridSwitchBufferTime: NaN,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this default to 12 given the comment above?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot! Forgot to change the default value to be stableBufferTime in documentation. Is it clearer with the change?

longFormContentDurationThreshold: 600,
stallThreshold: 0.3,
useAppendWindow: true,
Expand Down
8 changes: 4 additions & 4 deletions src/streaming/controllers/AbrController.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ function AbrController() {
idx = _checkMaxBitrate(type, streamId);
idx = _checkMaxRepresentationRatio(idx, type, streamId);
idx = _checkPortalSize(idx, type, streamId);
// Apply maximum suggested bitrate from CMSD headers if enabled
// Apply maximum suggested bitrate from CMSD headers if enabled
if (settings.get().streaming.cmsd.enabled && settings.get().streaming.cmsd.abr.applyMb) {
idx = _checkCmsdMaxBitrate(idx, type, streamId);
}
Expand Down Expand Up @@ -853,9 +853,9 @@ function AbrController() {

function _updateDynamicAbrStrategy(mediaType, bufferLevel) {
try {
const stableBufferTime = mediaPlayerModel.getStableBufferTime();
const switchOnThreshold = stableBufferTime;
const switchOffThreshold = 0.5 * stableBufferTime;
const hybridSwitchBufferTime = mediaPlayerModel.getHybridSwitchBufferTime();
const switchOnThreshold = hybridSwitchBufferTime;
const switchOffThreshold = 0.5 * hybridSwitchBufferTime;

const useBufferABR = isUsingBufferOccupancyAbrDict[mediaType];
const newUseBufferABR = bufferLevel > (useBufferABR ? switchOffThreshold : switchOnThreshold); // use hysteresis to avoid oscillating rules
Expand Down
23 changes: 16 additions & 7 deletions src/streaming/models/MediaPlayerModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ function MediaPlayerModel() {

/**
* Checks the supplied min playback rate is a valid vlaue and within supported limits
* @param {number} rate - Supplied min playback rate
* @param {boolean} log - wether to shown warning or not
* @param {number} rate - Supplied min playback rate
* @param {boolean} log - wether to shown warning or not
* @returns {number} corrected min playback rate
*/
function _checkMinPlaybackRate (rate, log) {
Expand All @@ -84,7 +84,7 @@ function MediaPlayerModel() {
logger.warn(`Supplied minimum playback rate is a positive value when it should be negative or 0. The supplied rate will not be applied and set to 0: 100% playback speed.`)
}
return 0;
}
}
if (rate < CATCHUP_PLAYBACK_RATE_MIN_LIMIT) {
if (log) {
logger.warn(`Supplied minimum playback rate is out of range and will be limited to ${CATCHUP_PLAYBACK_RATE_MIN_LIMIT}: ${CATCHUP_PLAYBACK_RATE_MIN_LIMIT * 100}% playback speed.`);
Expand All @@ -96,8 +96,8 @@ function MediaPlayerModel() {

/**
* Checks the supplied max playback rate is a valid vlaue and within supported limits
* @param {number} rate - Supplied max playback rate
* @param {boolean} log - wether to shown warning or not
* @param {number} rate - Supplied max playback rate
* @param {boolean} log - wether to shown warning or not
* @returns {number} corrected max playback rate
*/
function _checkMaxPlaybackRate (rate, log) {
Expand All @@ -107,7 +107,7 @@ function MediaPlayerModel() {
logger.warn(`Supplied maximum playback rate is a negative value when it should be negative or 0. The supplied rate will not be applied and set to 0: 100% playback speed.`)
}
return 0;
}
}
if (rate > CATCHUP_PLAYBACK_RATE_MAX_LIMIT) {
if (log) {
logger.warn(`Supplied maximum playback rate is out of range and will be limited to ${CATCHUP_PLAYBACK_RATE_MAX_LIMIT}: ${(1 + CATCHUP_PLAYBACK_RATE_MAX_LIMIT) * 100}% playback speed.`);
Expand Down Expand Up @@ -141,7 +141,7 @@ function MediaPlayerModel() {
*/
function getCatchupPlaybackRates(log) {
const settingsPlaybackRate = settings.get().streaming.liveCatchup.playbackRate;

if(!isNaN(settingsPlaybackRate.min) || !isNaN(settingsPlaybackRate.max)) {
return {
min: _checkMinPlaybackRate(settingsPlaybackRate.min, log),
Expand Down Expand Up @@ -225,6 +225,14 @@ function MediaPlayerModel() {
return !isNaN(liveDelay) && liveDelay > 0 ? Math.min(stableBufferTime, liveDelay) : stableBufferTime;
}

/**
* Returns the buffer time that the hybrid rule will switch between throughput and BOLA at.
* @returns {number}
*/
function getHybridSwitchBufferTime() {
return settings.get().streaming.buffer.hybridSwitchBufferTime || getStableBufferTime();
}

/**
* Returns the number of retry attempts for a specific media type
* @param type
Expand Down Expand Up @@ -254,6 +262,7 @@ function MediaPlayerModel() {
getCatchupMaxDrift,
getCatchupModeEnabled,
getStableBufferTime,
getHybridSwitchBufferTime,
getInitialBufferLevel,
getRetryAttemptsForType,
getRetryIntervalsForType,
Expand Down
4 changes: 4 additions & 0 deletions test/unit/mocks/MediaPlayerModelMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ class MediaPlayerModelMock {
return this.stableBufferTime > -1 ? this.stableBufferTime : this.fastSwitchEnabled ? DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH : DEFAULT_MIN_BUFFER_TIME;
}

getHybridSwitchBufferTime() {
return this.hybridSwitchBufferTime > -1 ? this.hybridSwitchBufferTime : this.getStableBufferTime()
}

setRetryAttemptsForType(type, value) {
this.retryAttempts[type] = value;
}
Expand Down
16 changes: 16 additions & 0 deletions test/unit/streaming.models.MediaPlayerModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,22 @@ describe('MediaPlayerModel', function () {
expect(stableBufferTime).to.equal(10);
});

it('should configure HybridSwitchBufferTime', function() {
eirikbjornr marked this conversation as resolved.
Show resolved Hide resolved
const s = { streaming: { buffer: { hybridSwitchBufferTime: 12.34, stableBufferTime: 14.32 } } };
settings.update(s);

const hybridSwitchBufferTime = mediaPlayerModel.getHybridSwitchBufferTime();
expect(hybridSwitchBufferTime).to.equal(12.34);
});

it('should default to StableBufferTime for HybridSwitchBufferTime', function () {
const s = { streaming: { buffer: { stableBufferTime: 14.32 } } };
settings.update(s);

const hybridSwitchBufferTime = mediaPlayerModel.getHybridSwitchBufferTime();
expect(hybridSwitchBufferTime).to.equal(14.32);
});

it('should configure initial buffer level', function () {
const s = { streaming: { buffer: { initialBufferLevel: 8 } } };
settings.update(s);
Expand Down