Skip to content

Commit

Permalink
Honor startPosition from config/startLoad with live streams
Browse files Browse the repository at this point in the history
Do not sync with live edge when inside sliding window and liveMaxLatencyDuration
Fixes #3736
  • Loading branch information
Rob Walch committed Apr 7, 2021
1 parent e8cbd53 commit 8df2c6e
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 12 deletions.
18 changes: 15 additions & 3 deletions demo/chart/timeline-chart.ts
Expand Up @@ -283,12 +283,18 @@ export class TimelineChart {
updateLevelOrTrack(details: LevelDetails) {
const { targetduration, totalduration, url } = details;
const { datasets } = this.chart.data;
const levelDataSet = arrayFind(
let levelDataSet = arrayFind(
datasets,
(dataset) =>
stripDeliveryDirectives(url) ===
stripDeliveryDirectives(dataset.url || '')
);
if (!levelDataSet) {
levelDataSet = arrayFind(
datasets,
(dataset) => details.fragments[0]?.level === dataset.level
);
}
if (!levelDataSet) {
return;
}
Expand Down Expand Up @@ -381,10 +387,16 @@ export class TimelineChart {
updateFragment(data: FragLoadedData | FragParsedData | FragChangedData) {
const { datasets } = this.chart.data;
const frag: Fragment = data.frag;
const levelDataSet = arrayFind(
let levelDataSet = arrayFind(
datasets,
(dataset) => dataset.url === frag.baseurl
(dataset) => frag.baseurl === dataset.url
);
if (!levelDataSet) {
levelDataSet = arrayFind(
datasets,
(dataset) => frag.level === dataset.level
);
}
if (!levelDataSet) {
return;
}
Expand Down
25 changes: 18 additions & 7 deletions src/controller/base-stream-controller.ts
Expand Up @@ -766,7 +766,11 @@ export default class BaseStreamController
// In order to discover the range, we load the best matching fragment for that level and demux it.
// Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that
// we get the fragment matching that start time
if (!levelDetails.PTSKnown && !this.startFragRequested) {
if (
!levelDetails.PTSKnown &&
!this.startFragRequested &&
this.startPosition === -1
) {
frag = this.getInitialLiveFragment(levelDetails, fragments);
}
} else if (pos <= start) {
Expand Down Expand Up @@ -981,20 +985,24 @@ export default class BaseStreamController
const currentTime = media.currentTime;
const start = levelDetails.fragments[0].start;
const end = levelDetails.edge;
const withinSlidingWindow =
currentTime >= start - config.maxFragLookUpTolerance &&
currentTime <= end;
// Continue if we can seek forward to sync position or if current time is outside of sliding window
if (
liveSyncPosition !== null &&
media.duration > liveSyncPosition &&
(currentTime < liveSyncPosition ||
currentTime < start - config.maxFragLookUpTolerance ||
currentTime > end)
(currentTime < liveSyncPosition || !withinSlidingWindow)
) {
// Continue if buffer is starving or if current time is behind max latency
const maxLatency =
config.liveMaxLatencyDuration !== undefined
? config.liveMaxLatencyDuration
: config.liveMaxLatencyDurationCount * levelDetails.targetduration;
if (media.readyState < 4 || currentTime < end - maxLatency) {
if (
(!withinSlidingWindow && media.readyState < 4) ||
currentTime < end - maxLatency
) {
if (!this.loadedmetadata) {
this.nextLoadPosition = liveSyncPosition;
}
Expand Down Expand Up @@ -1071,8 +1079,11 @@ export default class BaseStreamController
this.startPosition = startTimeOffset;
} else {
if (details.live) {
this.startPosition = this.hls.liveSyncPosition || sliding;
this.log(`Configure startPosition to ${this.startPosition}`);
// Leave this.startPosition at -1, so that we can use `getInitialLiveFragment` logic when startPosition has
// not been specified via the config or an as an argument to startLoad (#3736).
this.nextLoadPosition = this.lastCurrentTime =
this.hls.liveSyncPosition || sliding;
return;
} else {
this.startPosition = 0;
}
Expand Down
1 change: 0 additions & 1 deletion src/controller/stream-controller.ts
Expand Up @@ -8,7 +8,6 @@ import { FragmentState } from './fragment-tracker';
import type { Level } from '../types/level';
import { PlaylistLevelType } from '../types/loader';
import { Fragment, ElementaryStreamTypes } from '../loader/fragment';
import FragmentLoader from '../loader/fragment-loader';
import TransmuxerInterface from '../demux/transmuxer-interface';
import type { TransmuxerResult } from '../types/transmuxer';
import { ChunkMetadata } from '../types/transmuxer';
Expand Down
19 changes: 18 additions & 1 deletion tests/unit/controller/stream-controller.ts
Expand Up @@ -114,6 +114,7 @@ describe('StreamController', function () {

beforeEach(function () {
streamController['fragPrevious'] = fragPrevious;
levelDetails.live = false;
levelDetails.startSN = mockFragments[0].sn;
levelDetails.endSN = mockFragments[mockFragments.length - 1].sn;
levelDetails.fragments = mockFragments;
Expand Down Expand Up @@ -434,7 +435,7 @@ describe('StreamController', function () {
expect(streamController['lastCurrentTime']).to.equal(5);
});

it('should set startPosition to lastCurrentTime if unset', function () {
it('should set startPosition to lastCurrentTime if unset and lastCurrentTime > 0', function () {
streamController['lastCurrentTime'] = 5;
streamController.startLoad(-1);
assertStreamControllerStarted(streamController);
Expand All @@ -443,6 +444,22 @@ describe('StreamController', function () {
expect(streamController['lastCurrentTime']).to.equal(5);
});

it('should set startPosition when passed as an argument', function () {
streamController.startLoad(123);
assertStreamControllerStarted(streamController);
expect(streamController['nextLoadPosition']).to.equal(123);
expect(streamController['startPosition']).to.equal(123);
expect(streamController['lastCurrentTime']).to.equal(123);
});

it('should set startPosition to -1 when passed as an argument', function () {
streamController.startLoad(-1);
assertStreamControllerStarted(streamController);
expect(streamController['nextLoadPosition']).to.equal(-1);
expect(streamController['startPosition']).to.equal(-1);
expect(streamController['lastCurrentTime']).to.equal(-1);
});

it('sets up for a bandwidth test if starting at auto', function () {
streamController['startFragRequested'] = false;
hls.startLevel = -1;
Expand Down

0 comments on commit 8df2c6e

Please sign in to comment.