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

Fix/buffer prune range #4056

Merged
merged 5 commits into from
Sep 30, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 42 additions & 33 deletions src/streaming/controllers/BufferController.js
Original file line number Diff line number Diff line change
@@ -541,54 +541,63 @@ function BufferController(config) {

function _getRangeAheadForPruning(targetTime, ranges) {
// if we do a seek behind the current play position we do need to prune ahead of the new play position
// we keep everything that is within bufferToKeepAhead but only if the buffer is continuous.
// Otherwise we have gaps once the seek is done which might trigger an unintentional gap jump
const endOfBuffer = ranges.end(ranges.length - 1) + BUFFER_END_THRESHOLD;
const continuousBufferTime = getContinuousBufferTimeForTargetTime(targetTime);

// This is the maximum range we keep ahead
const isLongFormContent = streamInfo.manifestInfo.duration >= settings.get().streaming.buffer.longFormContentDurationThreshold;
const bufferToKeepAhead = isLongFormContent ? settings.get().streaming.buffer.bufferTimeAtTopQualityLongForm : settings.get().streaming.buffer.bufferTimeAtTopQuality;
const aheadDiff = endOfBuffer - targetTime;

if (aheadDiff > bufferToKeepAhead) {
// Define the start time from which we will prune. If there is no continuous range from the targettime we start immediately at the target time
// Otherwise we set the start point to the end of the continuous range taking the maximum buffer to keep ahead into account
let rangeStart = !isNaN(continuousBufferTime) ? Math.min(continuousBufferTime, targetTime + bufferToKeepAhead) : targetTime;

let rangeStart = targetTime + bufferToKeepAhead;
// Ensure we keep full range of current fragment
const currentTimeRequest = fragmentModel.getRequests({
state: FragmentModel.FRAGMENT_MODEL_EXECUTED,
time: targetTime,
threshold: BUFFER_RANGE_CALCULATION_THRESHOLD
})[0];
// Check if we are done buffering, no need to prune then
if (rangeStart >= ranges.end(ranges.length - 1)) {
return null
}

if (currentTimeRequest) {
rangeStart = Math.max(currentTimeRequest.startTime + currentTimeRequest.duration, rangeStart);
}
// Ensure we keep full range of current fragment
const currentTimeRequest = fragmentModel.getRequests({
state: FragmentModel.FRAGMENT_MODEL_EXECUTED,
time: targetTime,
threshold: BUFFER_RANGE_CALCULATION_THRESHOLD
})[0];

// Never remove the contiguous range of targetTime in order to avoid flushes & reenqueues when the user doesn't want it
const avoidCurrentTimeRangePruning = settings.get().streaming.buffer.avoidCurrentTimeRangePruning;
if (avoidCurrentTimeRangePruning) {
for (let i = 0; i < ranges.length; i++) {
if (ranges.start(i) <= targetTime && targetTime <= ranges.end(i)
&& ranges.start(i) <= rangeStart && rangeStart <= ranges.end(i)) {
let oldRangeStart = rangeStart;
if (i + 1 < ranges.length) {
rangeStart = ranges.start(i+1);
} else {
rangeStart = ranges.end(i) + 1;
}
logger.debug('Buffered range [' + ranges.start(i) + ', ' + ranges.end(i) + '] overlaps with targetTime ' + targetTime + ' and range to be pruned [' + oldRangeStart + ', ' + endOfBuffer + '], using [' + rangeStart + ', ' + endOfBuffer +'] instead' + ((rangeStart < endOfBuffer) ? '' : ' (no actual pruning)'));
break;
if (currentTimeRequest) {
rangeStart = Math.max(currentTimeRequest.startTime + currentTimeRequest.duration, rangeStart);
}

// Never remove the contiguous range of targetTime in order to avoid flushes & reenqueues when the user doesn't want it
const avoidCurrentTimeRangePruning = settings.get().streaming.buffer.avoidCurrentTimeRangePruning;
if (avoidCurrentTimeRangePruning) {
for (let i = 0; i < ranges.length; i++) {
if (ranges.start(i) <= targetTime && targetTime <= ranges.end(i)
&& ranges.start(i) <= rangeStart && rangeStart <= ranges.end(i)) {
let oldRangeStart = rangeStart;
if (i + 1 < ranges.length) {
rangeStart = ranges.start(i + 1);
} else {
rangeStart = ranges.end(i) + 1;
}
logger.debug('Buffered range [' + ranges.start(i) + ', ' + ranges.end(i) + '] overlaps with targetTime ' + targetTime + ' and range to be pruned [' + oldRangeStart + ', ' + endOfBuffer + '], using [' + rangeStart + ', ' + endOfBuffer + '] instead' + ((rangeStart < endOfBuffer) ? '' : ' (no actual pruning)'));
break;
}
}

if (rangeStart < endOfBuffer) {
return {
start: rangeStart,
end: endOfBuffer
};
}
}

if (rangeStart < ranges.end(ranges.length - 1)) {
return {
start: rangeStart,
end: endOfBuffer
};
}
return null;
}


function _onPlaybackProgression() {
if (!replacingBuffer || (type === Constants.TEXT && textController.isTextEnabled())) {
_updateBufferLevel();