Skip to content

Commit

Permalink
Fix a bug that lead to starting the the schedule timer twice. When th…
Browse files Browse the repository at this point in the history
…e quality is changed as part of the scheduling logic the ScheduleController.js should not call _getNextFragment (#3654)
  • Loading branch information
dsilhavy authored Jun 1, 2021
1 parent 0379181 commit c3906de
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 51 deletions.
1 change: 1 addition & 0 deletions src/streaming/StreamProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ function StreamProcessor(config) {
}

if (e.error && e.request.serviceLocation) {
logger.info(`Fragment loading completed with an error`);
setExplicitBufferingTime(e.request.startTime + (e.request.duration / 2));
scheduleController.startScheduleTimer(0);
}
Expand Down
84 changes: 43 additions & 41 deletions src/streaming/controllers/AbrController.js
Original file line number Diff line number Diff line change
Expand Up @@ -577,55 +577,57 @@ function AbrController() {
* @param {string} streamId
*/
function checkPlaybackQuality(type, streamId) {
if (!type || !streamProcessorDict || !streamProcessorDict[streamId] || !streamProcessorDict[streamId][type]) {
return;
}

if (droppedFramesHistory) {
const playbackQuality = videoModel.getPlaybackQuality();
if (playbackQuality) {
droppedFramesHistory.push(streamId, playbackIndex, playbackQuality);
try {
if (!type || !streamProcessorDict || !streamProcessorDict[streamId] || !streamProcessorDict[streamId][type]) {
return false;
}
}

// ABR is turned off, do nothing
if (!settings.get().streaming.abr.autoSwitchBitrate[type]) {
return;
}
if (droppedFramesHistory) {
const playbackQuality = videoModel.getPlaybackQuality();
if (playbackQuality) {
droppedFramesHistory.push(streamId, playbackIndex, playbackQuality);
}
}

const oldQuality = getQualityFor(type, streamId);
const rulesContext = RulesContext(context).create({
abrController: instance,
switchHistory: switchHistoryDict[streamId][type],
droppedFramesHistory: droppedFramesHistory,
streamProcessor: streamProcessorDict[streamId][type],
currentValue: oldQuality,
useBufferOccupancyABR: isUsingBufferOccupancyAbrDict[type],
useL2AABR: isUsingL2AAbrDict[type],
useLoLPABR: isUsingLoLPAbrDict[type],
videoModel
});
const minIdx = getMinAllowedIndexFor(type, streamId);
const maxIdx = getMaxAllowedIndexFor(type, streamId);
const switchRequest = abrRulesCollection.getMaxQuality(rulesContext);
let newQuality = switchRequest.quality;
// ABR is turned off, do nothing
if (!settings.get().streaming.abr.autoSwitchBitrate[type]) {
return false;
}

if (minIdx !== undefined && ((newQuality > SwitchRequest.NO_CHANGE) ? newQuality : oldQuality) < minIdx) {
newQuality = minIdx;
}
if (newQuality > maxIdx) {
newQuality = maxIdx;
}
const oldQuality = getQualityFor(type, streamId);
const rulesContext = RulesContext(context).create({
abrController: instance,
switchHistory: switchHistoryDict[streamId][type],
droppedFramesHistory: droppedFramesHistory,
streamProcessor: streamProcessorDict[streamId][type],
currentValue: oldQuality,
useBufferOccupancyABR: isUsingBufferOccupancyAbrDict[type],
useL2AABR: isUsingL2AAbrDict[type],
useLoLPABR: isUsingLoLPAbrDict[type],
videoModel
});
const minIdx = getMinAllowedIndexFor(type, streamId);
const maxIdx = getMaxAllowedIndexFor(type, streamId);
const switchRequest = abrRulesCollection.getMaxQuality(rulesContext);
let newQuality = switchRequest.quality;

if (minIdx !== undefined && ((newQuality > SwitchRequest.NO_CHANGE) ? newQuality : oldQuality) < minIdx) {
newQuality = minIdx;
}
if (newQuality > maxIdx) {
newQuality = maxIdx;
}

switchHistoryDict[streamId][type].push({ oldValue: oldQuality, newValue: newQuality });
switchHistoryDict[streamId][type].push({ oldValue: oldQuality, newValue: newQuality });

if (newQuality > SwitchRequest.NO_CHANGE && newQuality !== oldQuality) {
if (abandonmentStateDict[streamId][type].state === MetricsConstants.ALLOW_LOAD || newQuality > oldQuality) {
if (newQuality > SwitchRequest.NO_CHANGE && newQuality !== oldQuality && (abandonmentStateDict[streamId][type].state === MetricsConstants.ALLOW_LOAD || newQuality > oldQuality)) {
_changeQuality(type, oldQuality, newQuality, maxIdx, switchRequest.reason, streamId);
return true;
}
} else if (settings.get().debug.logLevel === Debug.LOG_LEVEL_DEBUG) {
const bufferLevel = dashMetrics.getCurrentBufferLevel(type, true);
logger.debug('[' + type + '] stay on ' + oldQuality + '/' + maxIdx + ' (buffer: ' + bufferLevel + ')');

return false;
} catch (e) {
return false;
}

}
Expand Down
3 changes: 1 addition & 2 deletions src/streaming/controllers/BufferController.js
Original file line number Diff line number Diff line change
Expand Up @@ -942,8 +942,7 @@ function BufferController(config) {
}

return totalBufferedTime;
}
catch(e) {
} catch (e) {
return 0;
}
}
Expand Down
19 changes: 11 additions & 8 deletions src/streaming/controllers/ScheduleController.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,15 @@ function ScheduleController(config) {
}

if (_shouldScheduleNextRequest()) {
let qualityChange = false;
if (checkPlaybackQuality) {
// in case the playback quality is supposed to be changed, the corresponding StreamProcessor will update the currentRepresentation
abrController.checkPlaybackQuality(type, streamInfo.id);
// in case the playback quality is supposed to be changed, the corresponding StreamProcessor will update the currentRepresentation.
// The StreamProcessor will also start the schedule timer again once the quality switch has beeen prepared. Consequently, we only call _getNextFragment if the quality is not changed.
qualityChange = abrController.checkPlaybackQuality(type, streamInfo.id);
}
if (!qualityChange) {
_getNextFragment();
}
_getNextFragment();

} else {
startScheduleTimer(settings.get().streaming.lowLatencyEnabled ? settings.get().streaming.scheduling.lowLatencyTimeout : settings.get().streaming.scheduling.defaultTimeout);
Expand Down Expand Up @@ -298,12 +302,11 @@ function ScheduleController(config) {
const streamInfo = currentRepresentationInfo.mediaInfo.streamInfo;
if (abrController.isPlayingAtTopQuality(streamInfo)) {
const isLongFormContent = streamInfo.manifestInfo.duration >= settings.get().streaming.buffer.longFormContentDurationThreshold;
return isLongFormContent ? settings.get().streaming.buffer.bufferTimeAtTopQualityLongForm : settings.get().streaming.buffer.bufferTimeAtTopQuality;
return isLongFormContent ? settings.get().streaming.buffer.bufferTimeAtTopQualityLongForm : settings.get().streaming.buffer.bufferTimeAtTopQuality;
} else {
return mediaPlayerModel.getStableBufferTime();
return mediaPlayerModel.getStableBufferTime();
}
}
catch(e) {
} catch (e) {
return mediaPlayerModel.getStableBufferTime();
}
}
Expand Down Expand Up @@ -356,7 +359,7 @@ function ScheduleController(config) {
}

function _onBytesAppended(e) {
logger.debug(`Appended bytes for ${e.mediaType}`);
logger.debug(`Appended bytes for ${e.mediaType} and stream id ${streamInfo.id}`);

// we save the last initialized quality. That way we make sure that the media fragments we are about to append match the init segment
if (isNaN(e.index)) {
Expand Down

0 comments on commit c3906de

Please sign in to comment.