Skip to content

Commit

Permalink
Merge pull request #259 from jwplayer/bugfix/live-with-audio-track
Browse files Browse the repository at this point in the history
[JW8-10716] Fix live stream with audio tracks start
  • Loading branch information
robwalch committed Jan 16, 2020
2 parents b3f654b + 2d35308 commit 16769c8
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 19 deletions.
15 changes: 12 additions & 3 deletions src/controller/audio-stream-controller.js
Expand Up @@ -397,8 +397,6 @@ class AudioStreamController extends BaseStreamController {
// we already have details for that level, merge them
LevelHelper.mergeDetails(curDetails, newDetails);
sliding = newDetails.fragments[0].start;
// TODO
// this.liveSyncPosition = this.computeLivePosition(sliding, curDetails);
if (newDetails.PTSKnown) {
logger.log(`live audio playlist sliding:${sliding.toFixed(3)}`);
} else {
Expand All @@ -423,7 +421,13 @@ class AudioStreamController extends BaseStreamController {
logger.log(`start time offset found in playlist, adjust startPosition to ${startTimeOffset}`);
this.startPosition = startTimeOffset;
} else {
this.startPosition = 0;
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
if (newDetails.live) {
this.startPosition = this.computeLivePosition(sliding, newDetails);
logger.log(`[audio track] configure startPosition to ${this.startPosition}`);
} else {
this.startPosition = 0;
}
}
}
this.nextLoadPosition = this.startPosition;
Expand All @@ -437,6 +441,11 @@ class AudioStreamController extends BaseStreamController {
this.tick();
}

computeLivePosition (sliding, levelDetails) {
let targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * levelDetails.targetduration;
return sliding + Math.max(0, levelDetails.totalduration - targetLatency);
}

onKeyLoaded () {
if (this.state === State.KEY_LOADING) {
this.state = State.IDLE;
Expand Down
34 changes: 18 additions & 16 deletions src/controller/stream-controller.js
Expand Up @@ -248,7 +248,7 @@ class StreamController extends BaseStreamController {
return;
}

frag = this._ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments, fragLen);
frag = this._ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments);
// if it explicitely returns null don't load any fragment and exit function now
if (frag === null) {
return;
Expand All @@ -275,14 +275,19 @@ class StreamController extends BaseStreamController {
}
}

_ensureFragmentAtLivePoint (levelDetails, bufferEnd, start, end, fragPrevious, fragments, fragLen) {
_ensureFragmentAtLivePoint (levelDetails, bufferEnd, start, end, fragPrevious, fragments) {
const config = this.hls.config, media = this.media;

let frag;

// check if requested position is within seekable boundaries :
// logger.log(`start/pos/bufEnd/seeking:${start.toFixed(3)}/${pos.toFixed(3)}/${bufferEnd.toFixed(3)}/${this.media.seeking}`);
let maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration;
let maxLatency = Infinity;
if (config.liveMaxLatencyDuration !== undefined) {
maxLatency = config.liveMaxLatencyDuration;
} else if (Number.isFinite(config.liveMaxLatencyDurationCount)) {
maxLatency = config.liveMaxLatencyDurationCount * levelDetails.targetduration;
}

if (bufferEnd < Math.max(start - config.maxFragLookUpTolerance, end - maxLatency)) {
let liveSyncPosition = this.liveSyncPosition = this.computeLivePosition(start, levelDetails);
Expand Down Expand Up @@ -341,13 +346,6 @@ class StreamController extends BaseStreamController {
}
}
}
if (!frag) {
/* we have no idea about which fragment should be loaded.
so let's load mid fragment. it will help computing playlist sliding and find the right one
*/
frag = fragments[Math.min(fragLen - 1, Math.round(fragLen / 2))];
logger.log(`live playlist, switching playlist, unknown, load middle frag : ${frag.sn}`);
}
}

return frag;
Expand Down Expand Up @@ -1153,7 +1151,10 @@ class StreamController extends BaseStreamController {
this.hls.trigger(Event.FRAG_BUFFERED, { stats: stats, frag: frag, id: 'main' });
this.state = State.IDLE;
}
this.tick();
// Do not tick when _seekToStartPos needs to be called as seeking to the start can fail on live streams at this point
if (this.loadedmetadata || this.startPosition <= 0) {
this.tick();
}
}
}

Expand Down Expand Up @@ -1313,15 +1314,16 @@ class StreamController extends BaseStreamController {
* @private
*/
_seekToStartPos () {
const { media } = this;
const { media, startPosition } = this;
const currentTime = media.currentTime;
// only adjust currentTime if different from startPosition or if startPosition not buffered
// at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered
const startPosition = media.seeking ? currentTime : this.startPosition;
// if currentTime not matching with expected startPosition or startPosition not buffered but close to first buffered
if (currentTime !== startPosition) {
// if startPosition not buffered, let's seek to buffered.start(0)
logger.log(`target start position not buffered, seek to buffered.start(0) ${startPosition} from current time ${currentTime} `);
if (media.seeking) {
logger.log(`could not seek to ${startPosition}, already seeking at ${currentTime}`);
return;
}
logger.log(`seek to target start position ${startPosition} from current time ${currentTime}. ready state ${media.readyState}`);
media.currentTime = startPosition;
}
}
Expand Down

0 comments on commit 16769c8

Please sign in to comment.