Skip to content

Commit

Permalink
Schedule live reload time base on start of last update request
Browse files Browse the repository at this point in the history
Use last fragment duration rather than target duration when playhead is less than two target durations from the live edge
  • Loading branch information
robwalch committed Oct 12, 2022
1 parent 7497e49 commit c4263ac
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 10 deletions.
10 changes: 8 additions & 2 deletions src/controller/base-playlist-controller.ts
Expand Up @@ -204,9 +204,15 @@ 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;
reloadInterval -= details.partTarget || 1;
}
this.log(
`reload live playlist ${index} in ${Math.round(reloadInterval)} ms`
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);
});
});
});

0 comments on commit c4263ac

Please sign in to comment.