Skip to content

Commit

Permalink
Fix a bug in the calculation of the start period which lead to a stal…
Browse files Browse the repository at this point in the history
…l of playback (#3211)
  • Loading branch information
dsilhavy authored Apr 3, 2020
1 parent 8dbbd78 commit 0974b2d
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 19 deletions.
11 changes: 6 additions & 5 deletions src/streaming/controllers/PlaybackController.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ function PlaybackController() {
t -= timeOffset;
}
}

return t;
}

Expand Down Expand Up @@ -222,15 +221,18 @@ function PlaybackController() {
* Computes the desirable delay for the live edge to avoid a risk of getting 404 when playing at the bleeding edge
* @param {number} fragmentDuration - seconds?
* @param {number} dvrWindowSize - seconds?
* @param {number} minBufferTime - seconds?
* @returns {number} object
* @memberof PlaybackController#
*/
function computeLiveDelay(fragmentDuration, dvrWindowSize) {
function computeLiveDelay(fragmentDuration, dvrWindowSize, minBufferTime = NaN) {
let delay,
ret,
r,
startTime;
const END_OF_PLAYLIST_PADDING = 10;
const MIN_BUFFER_TIME_FACTOR = 4;
const FRAGMENT_DURATION_FACTOR = 4;

let uriParameters = uriFragmentModel.getURIFragmentData();

Expand All @@ -251,9 +253,9 @@ function PlaybackController() {
} else if (settings.get().streaming.useSuggestedPresentationDelay === true && suggestedPresentationDelay !== null && !isNaN(suggestedPresentationDelay) && suggestedPresentationDelay > 0) {
delay = suggestedPresentationDelay;
} else if (!isNaN(fragmentDuration)) {
delay = fragmentDuration * 4;
delay = fragmentDuration * FRAGMENT_DURATION_FACTOR;
} else {
delay = streamInfo.manifestInfo.minBufferTime * 4;
delay = !isNaN(minBufferTime) ? minBufferTime * MIN_BUFFER_TIME_FACTOR : streamInfo.manifestInfo.minBufferTime * MIN_BUFFER_TIME_FACTOR;
}

startTime = adapter.getAvailabilityStartTime();
Expand Down Expand Up @@ -453,7 +455,6 @@ function PlaybackController() {
if (isPaused() || !isDynamic || videoModel.getReadyState() === 0) return;
const currentTime = getNormalizedTime();
const actualTime = getActualPresentationTime(currentTime);

const timeChanged = (!isNaN(actualTime) && actualTime !== currentTime);
if (timeChanged) {
seek(actualTime);
Expand Down
32 changes: 18 additions & 14 deletions src/streaming/controllers/StreamController.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ function StreamController() {

// Find out what is the right time position to jump to taking
// into account state of buffer
for (let i = 0; i < streamProcessors.length; i ++) {
for (let i = 0; i < streamProcessors.length; i++) {
const mediaBuffer = streamProcessors[i].getBuffer();
const ranges = mediaBuffer.getAllBufferRanges();
let nextRangeStartTime;
Expand Down Expand Up @@ -262,7 +262,7 @@ function StreamController() {
stopPreloadTimer();
}

if ( seekingStream === activeStream && preloading ) {
if (seekingStream === activeStream && preloading) {
// Seeking to the current period was requested while preloading the next one, deactivate preloading one
preloading.deactivate(true);
}
Expand All @@ -278,7 +278,7 @@ function StreamController() {
createPlaylistMetrics(PlayList.SEEK_START_REASON);
}

function onPlaybackStarted( /*e*/ ) {
function onPlaybackStarted( /*e*/) {
logger.debug('[onPlaybackStarted]');
if (initialPlayback) {
initialPlayback = false;
Expand Down Expand Up @@ -324,9 +324,11 @@ function StreamController() {
const delayPlaybackEnded = timeToEnd > 0 ? timeToEnd * 1000 : 0;
const prefetchDelay = delayPlaybackEnded < PERIOD_PREFETCH_TIME ? delayPlaybackEnded / 4 : delayPlaybackEnded - PERIOD_PREFETCH_TIME;
logger.debug('[toggleEndPeriodTimer] Going to fire preload in', prefetchDelay, 'milliseconds');
prefetchTimerId = setTimeout(onStreamCanLoadNext, prefetchDelay);
logger.debug('[toggleEndPeriodTimer] start-up of timer to notify PLAYBACK_ENDED event. It will be triggered in',delayPlaybackEnded, 'milliseconds');
playbackEndedTimerId = setTimeout(function () {eventBus.trigger(Events.PLAYBACK_ENDED, {'isLast': getActiveStreamInfo().isLast});}, delayPlaybackEnded);
prefetchTimerId = setTimeout(onStreamCanLoadNext, prefetchDelay);
logger.debug('[toggleEndPeriodTimer] start-up of timer to notify PLAYBACK_ENDED event. It will be triggered in', delayPlaybackEnded, 'milliseconds');
playbackEndedTimerId = setTimeout(function () {
eventBus.trigger(Events.PLAYBACK_ENDED, {'isLast': getActiveStreamInfo().isLast});
}, delayPlaybackEnded);
}
}
}
Expand Down Expand Up @@ -452,8 +454,7 @@ function StreamController() {
audioTrackDetected = undefined;
videoTrackDetected = undefined;
switchStream(activeStream, nextStream, NaN);
}
else {
} else {
logger.debug('StreamController no next stream found');
}
flushPlaylistMetrics(nextStream ? PlayListTrace.END_OF_PERIOD_STOP_REASON : PlayListTrace.END_OF_CONTENT_STOP_REASON);
Expand Down Expand Up @@ -667,10 +668,13 @@ function StreamController() {
const initialTime = !isNaN(startTimeFormUriParameters.fragS) ? startTimeFormUriParameters.fragS : startTimeFormUriParameters.fragT;
initialStream = getStreamForTime(initialTime);
}
// For multiperiod streams we should avoid a switch of streams after the seek to the live edge. So we do a rough calculation of the expected seek time to find the right stream object.
// For multiperiod streams we should avoid a switch of streams after the seek to the live edge. So we do a calculation of the expected seek time to find the right stream object.
if (!initialStream && adapter.getIsDynamic() && streams.length) {
logger.debug('Dynamic multi-period stream: Trying to find the correct starting period');
const targetTime = timelineConverter.calcPresentationTimeFromWallTime(new Date(), adapter.getRegularPeriods()[0]);
const manifestInfo = adapter.getStreamsInfo(undefined, 1)[0].manifestInfo;
const liveEdge = timelineConverter.calcPresentationTimeFromWallTime(new Date(), adapter.getRegularPeriods()[0]);
const targetDelay = playbackController.computeLiveDelay(NaN, manifestInfo.DVRWindowSize, manifestInfo.minBufferTime);
const targetTime = liveEdge - targetDelay;
initialStream = getStreamForTime(targetTime);
}
switchStream(null, initialStream !== null ? initialStream : streams[0], NaN);
Expand All @@ -685,7 +689,7 @@ function StreamController() {
}
}

function onTimeSyncCompleted( /*e*/ ) {
function onTimeSyncCompleted( /*e*/) {
const manifest = manifestModel.getValue();
//TODO check if we can move this to initialize??
if (protectionController) {
Expand Down Expand Up @@ -719,7 +723,7 @@ function StreamController() {
useCalculatedLiveEdgeTime = adapter.getUseCalculatedLiveEdgeTimeForMediaInfo(mediaInfo);
if (useCalculatedLiveEdgeTime) {
logger.debug('SegmentTimeline detected using calculated Live Edge Time');
const s = { streaming: { useManifestDateHeaderTimeSource: false } };
const s = {streaming: {useManifestDateHeaderTimeSource: false}};
settings.update(s);
}
}
Expand Down Expand Up @@ -956,8 +960,8 @@ function StreamController() {

flushPlaylistMetrics(
hasMediaError || hasInitialisationError ?
PlayListTrace.FAILURE_STOP_REASON :
PlayListTrace.USER_REQUEST_STOP_REASON
PlayListTrace.FAILURE_STOP_REASON :
PlayListTrace.USER_REQUEST_STOP_REASON
);

for (let i = 0, ln = streams ? streams.length : 0; i < ln; i++) {
Expand Down

0 comments on commit 0974b2d

Please sign in to comment.