Skip to content

Commit

Permalink
Run sync to live edge following level update
Browse files Browse the repository at this point in the history
  • Loading branch information
Rob Walch committed Nov 5, 2020
1 parent 5653dd8 commit 0e24ba3
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 48 deletions.
29 changes: 13 additions & 16 deletions src/controller/base-stream-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,7 @@ export default class BaseStreamController extends TaskLoop implements NetworkCom

// find fragment index, contiguous with end of buffer position
const start = fragments[0].start;
const end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration;
let loadPosition = pos;
const end = levelDetails.edge;
let frag;

// If an initSegment is present, it must be buffered first
Expand All @@ -388,26 +387,21 @@ export default class BaseStreamController extends TaskLoop implements NetworkCom
this.warn(`Not enough fragments to start playback (have: ${fragLen}, need: ${initialLiveManifestSize})`);
return null;
}
// Check to see if we're within the live range; if not, this method will seek to the live edge and return the new position
const syncPos = this.synchronizeToLiveEdge(start, end, loadPosition, levelDetails);
if (syncPos !== null) {
loadPosition = syncPos;
}
// The real fragment start times for a live stream are only known after the PTS range for that level is known.
// 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 && !startFragRequested) {
frag = this.getInitialLiveFragment(levelDetails, fragments);
}
} else if (loadPosition < start) {
} else if (pos < start) {
// VoD playlist: if loadPosition before start of playlist, load first fragment
frag = fragments[0];
}

// If we haven't run into any special cases already, just load the fragment most closely matching the requested position
if (!frag) {
frag = this.getFragmentAtPosition(loadPosition, end, levelDetails);
frag = this.getFragmentAtPosition(pos, end, levelDetails);
}

return frag;
Expand Down Expand Up @@ -510,19 +504,22 @@ export default class BaseStreamController extends TaskLoop implements NetworkCom
return frag;
}

protected synchronizeToLiveEdge (start: number, end: number, bufferEnd: number, levelDetails: LevelDetails): number | null {
protected synchronizeToLiveEdge (levelDetails: LevelDetails): number | null {
const { config, media } = this;
const liveSyncPosition = this.hls.liveSyncPosition;
if (liveSyncPosition !== null) {
const currentTime = media.currentTime;
if (liveSyncPosition !== null && media?.readyState && media.duration > liveSyncPosition && liveSyncPosition > currentTime) {
const maxLatency = config.liveMaxLatencyDuration !== undefined
? config.liveMaxLatencyDuration
: config.liveMaxLatencyDurationCount * levelDetails.targetduration;
if (bufferEnd < Math.max(start - config.maxFragLookUpTolerance, end - maxLatency)) {
this.warn(`Buffer end: ${bufferEnd.toFixed(3)} is located too far from the end of live sliding playlist, reset currentTime to : ${liveSyncPosition.toFixed(3)}`);
this.nextLoadPosition = liveSyncPosition;
if (media?.readyState && media.duration > liveSyncPosition && liveSyncPosition > media.currentTime) {
media.currentTime = liveSyncPosition;
const start = levelDetails.fragments[0].start;
const end = levelDetails.edge;
if (currentTime < Math.max(start - config.maxFragLookUpTolerance, end - maxLatency)) {
this.warn(`Playback: ${currentTime.toFixed(3)} is located too far from the end of live sliding playlist: ${end}, reset currentTime to : ${liveSyncPosition.toFixed(3)}`);
if (!this.loadedmetadata) {
this.nextLoadPosition = liveSyncPosition;
}
media.currentTime = liveSyncPosition;
return liveSyncPosition;
}
}
Expand Down
18 changes: 2 additions & 16 deletions src/controller/stream-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type {
MediaAttachedData,
AudioTrackSwitchingData,
LevelsUpdatedData,
LevelUpdatedData,
AudioTrackSwitchedData,
BufferCreatedData,
ErrorData,
Expand Down Expand Up @@ -82,7 +81,6 @@ export default class StreamController extends BaseStreamController implements Ne
hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
}

Expand All @@ -101,7 +99,6 @@ export default class StreamController extends BaseStreamController implements Ne
hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
}

Expand Down Expand Up @@ -614,6 +611,8 @@ export default class StreamController extends BaseStreamController implements Ne

if (!this.startFragRequested) {
this.setStartPosition(newDetails, sliding);
} else if (newDetails.live) {
this.synchronizeToLiveEdge(newDetails);
}

// trigger handler right now
Expand Down Expand Up @@ -923,19 +922,6 @@ export default class StreamController extends BaseStreamController implements Ne
this.levels = data.levels;
}

onLevelUpdated (event: Events.LEVEL_UPDATED, data: LevelUpdatedData) {
const { media, mediaBuffer } = this;
const currentTime = media ? media.currentTime : null;
const levelDetails = data.details;
const fragments = levelDetails.fragments;
const fragLen = fragments.length;
const start = fragments[0].start;
const end = fragments[fragments.length - 1].start + fragments[fragLen - 1].duration;
const bufferInfo = BufferHelper.bufferInfo(mediaBuffer || media, currentTime, this.config.maxBufferHole);
const pos = bufferInfo.end;
this.synchronizeToLiveEdge(start, end, pos, levelDetails);
}

swapAudioCodec () {
this.audioCodecSwap = !this.audioCodecSwap;
}
Expand Down
34 changes: 19 additions & 15 deletions tests/mocks/data.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,52 @@
import Fragment from '../../src/loader/fragment';
import { PlaylistLevelType } from '../../src/types/loader';

function fragment (options) {
const frag = new Fragment(PlaylistLevelType.MAIN, '');
Object.assign(frag, options);
return frag;
}

export const mockFragments = [
{
fragment({
programDateTime: 1505502661523,
endProgramDateTime: 1505502666523,
level: 2,
duration: 5.000,
start: 0,
sn: 0,
cc: 0
},
}),
// Discontinuity with PDT 1505502671523 which does not exist in level 1 as per fragPrevious
{
fragment({
programDateTime: 1505502671523,
endProgramDateTime: 1505502676523,
level: 2,
duration: 5.000,
start: 5.000,
sn: 1,
cc: 1
},
{
}),
fragment({
programDateTime: 1505502676523,
endProgramDateTime: 1505502681523,
level: 2,
duration: 5.000,
start: 10.000,
sn: 2,
cc: 1
},
{
}),
fragment({
programDateTime: 1505502681523,
endProgramDateTime: 1505502686523,
level: 2,
duration: 5.000,
start: 15.000,
sn: 3,
cc: 1
},
{
}),
fragment({
programDateTime: 1505502686523,
endProgramDateTime: 1505502691523,
level: 2,
duration: 5.000,
start: 20.000,
sn: 4,
cc: 1
}
})
];
2 changes: 1 addition & 1 deletion tests/unit/controller/stream-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ describe('StreamController', function () {
expect(foundFragment).to.equal(mockFragments[3], 'Expected sn 3, found sn segment ' + resultSN);
});

// TODO: This test fails if using a real instance of Hls
it('PTS search choosing the right segment if fragPrevious is not available', function () {
streamController['fragPrevious'] = null;
const foundFragment = streamController['getNextFragment'](bufferEnd, levelDetails);
const resultSN = foundFragment ? foundFragment.sn : -1;
expect(foundFragment).to.equal(mockFragments[3], 'Expected sn 3, found sn segment ' + resultSN);
Expand Down

0 comments on commit 0e24ba3

Please sign in to comment.