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

Schedule live reload based on start of last update request #4961

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/controller/base-playlist-controller.ts
Expand Up @@ -204,7 +204,13 @@ export default class BasePlaylistController implements NetworkComponentAPI {
part
);
}
let reloadInterval = computeReloadInterval(details, stats);
const position = this.hls.mainForwardBufferInfo?.start || 0;
const distanceToLiveEdgeMs = (details.edge - position) * 1000;
let reloadInterval = computeReloadInterval(
details,
stats,
distanceToLiveEdgeMs
);
if (msn !== undefined && details.canBlockReload) {
reloadInterval -= details.partTarget || 1;
}
Expand Down
25 changes: 21 additions & 4 deletions src/controller/level-helper.ts
Expand Up @@ -434,10 +434,20 @@ export function addSliding(details: LevelDetails, start: number) {

export function computeReloadInterval(
newDetails: LevelDetails,
stats: LoaderStats
stats: LoaderStats,
distanceToLiveEdgeMs: number = Infinity
): number {
const reloadInterval = 1000 * newDetails.targetduration;
const roundTrip = stats.loading.end - stats.loading.start;
let reloadInterval = 1000 * newDetails.targetduration;

// Use last segment duration when shorter than target duration and near live edge
const fragments = newDetails.fragments;
if (fragments.length && reloadInterval * 2 > distanceToLiveEdgeMs) {
const lastSegmentDuration = fragments[fragments.length - 1].duration * 1000;
if (lastSegmentDuration < reloadInterval) {
const now = performance.now();
reloadInterval = stats.loading.start + lastSegmentDuration - now;
}
}

let estimatedTimeUntilUpdate;
if (!newDetails.updated) {
Expand All @@ -447,7 +457,14 @@ export function computeReloadInterval(
// duration before retrying.
estimatedTimeUntilUpdate = reloadInterval / 2;
} else {
estimatedTimeUntilUpdate = reloadInterval - roundTrip;
const roundTrip = stats.loading.end - stats.loading.start;
const now = performance.now();
const estimatedRefreshFromLastRequest =
stats.loading.start + reloadInterval - now;
estimatedTimeUntilUpdate = Math.min(
reloadInterval - roundTrip,
estimatedRefreshFromLastRequest
);
}

// console.log(`[computeReloadInterval] live reload ${newDetails.updated ? 'REFRESHED' : 'MISSED'}`,
Expand Down
32 changes: 28 additions & 4 deletions tests/unit/controller/level-helper.ts
Expand Up @@ -17,15 +17,15 @@ import { AttrList } from '../../../src/utils/attr-list';
chai.use(sinonChai);
const expect = chai.expect;

const generatePlaylist = (sequenceNumbers, offset = 0) => {
const generatePlaylist = (sequenceNumbers, offset = 0, duration = 5) => {
const playlist = new LevelDetails('');
playlist.startSN = sequenceNumbers[0];
playlist.endSN = sequenceNumbers[sequenceNumbers.length - 1];
playlist.fragments = sequenceNumbers.map((n, i) => {
const frag = new Fragment(PlaylistLevelType.MAIN, '');
frag.sn = n;
frag.start = i * 5 + offset;
frag.duration = 5;
frag.duration = duration;
return frag;
});
return playlist;
Expand Down Expand Up @@ -265,8 +265,18 @@ expect: ${JSON.stringify(merged.fragments[i])}`
});

describe('computeReloadInterval', function () {
let sandbox;
beforeEach(function () {
sandbox = sinon.createSandbox();
sandbox.stub(performance, 'now').returns(0);
});

afterEach(function () {
sandbox.restore();
});

it('returns the targetduration of the new level if available', function () {
const newPlaylist = generatePlaylist([3, 4]);
const newPlaylist = generatePlaylist([3, 4], 0, 6);
newPlaylist.targetduration = 5;
newPlaylist.updated = true;
const actual = computeReloadInterval(newPlaylist, new LoadStats());
Expand All @@ -282,7 +292,7 @@ expect: ${JSON.stringify(merged.fragments[i])}`
});

it('rounds the reload interval', function () {
const newPlaylist = generatePlaylist([3, 4]);
const newPlaylist = generatePlaylist([3, 4], 0, 10);
newPlaylist.targetduration = 5.9999;
newPlaylist.updated = true;
const actual = computeReloadInterval(newPlaylist, new LoadStats());
Expand Down Expand Up @@ -310,5 +320,19 @@ expect: ${JSON.stringify(merged.fragments[i])}`
const actual = computeReloadInterval(newPlaylist, stats);
expect(actual).to.equal(2500);
});

it('returns the last fragment duration when distance to live edge is less than two target durations', function () {
const newPlaylist = generatePlaylist([3, 4], 0, 2);
newPlaylist.targetduration = 5;
newPlaylist.updated = true;
const actual = computeReloadInterval(newPlaylist, new LoadStats(), 11000);
expect(actual).to.equal(5000);
const actualLow = computeReloadInterval(
newPlaylist,
new LoadStats(),
9000
);
expect(actualLow).to.equal(2000);
});
});
});