Skip to content

Commit

Permalink
fix: Add exclude reason and skip duplicate playlist-unchanged (#1082)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonocasey authored Mar 5, 2021
1 parent 1cfdab6 commit 0dceb5b
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 28 deletions.
99 changes: 72 additions & 27 deletions src/dash-playlist-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from './manifest';
import containerRequest from './util/container-request.js';
import {toUint8} from '@videojs/vhs-utils/es/byte-helpers';
import logger from './util/logger';

const { EventTarget, mergeOptions } = videojs;

Expand Down Expand Up @@ -291,6 +292,7 @@ export default class DashPlaylistLoader extends EventTarget {

this.state = 'HAVE_NOTHING';
this.loadedPlaylists_ = {};
this.logger_ = logger('DashPlaylistLoader');

// initialize the loader state
// The masterPlaylistLoader will be created with a string
Expand Down Expand Up @@ -415,6 +417,14 @@ export default class DashPlaylistLoader extends EventTarget {
window.clearTimeout(this.minimumUpdatePeriodTimeout_);
window.clearTimeout(this.mediaRequest_);
window.clearTimeout(this.mediaUpdateTimeout);
this.mediaUpdateTimeout = null;
this.mediaRequest_ = null;
this.minimumUpdatePeriodTimeout_ = null;

if (this.masterPlaylistLoader_.createMupOnMedia_) {
this.off('loadedmetadata', this.masterPlaylistLoader_.createMupOnMedia_);
this.masterPlaylistLoader_.createMupOnMedia_ = null;
}

this.off();
}
Expand Down Expand Up @@ -505,9 +515,15 @@ export default class DashPlaylistLoader extends EventTarget {
}

pause() {
if (this.masterPlaylistLoader_.createMupOnMedia_) {
this.off('loadedmetadata', this.masterPlaylistLoader_.createMupOnMedia_);
this.masterPlaylistLoader_.createMupOnMedia_ = null;
}
this.stopRequest();
window.clearTimeout(this.mediaUpdateTimeout);
window.clearTimeout(this.minimumUpdatePeriodTimeout_);
window.clearTimeout(this.masterPlaylistLoader_.minimumUpdatePeriodTimeout_);
this.masterPlaylistLoader_.minimumUpdatePeriodTimeout_ = null;
this.mediaUpdateTimeout = null;
if (this.state === 'HAVE_NOTHING') {
// If we pause the loader before any data has been retrieved, its as if we never
// started, so reset to an unstarted state.
Expand All @@ -517,7 +533,7 @@ export default class DashPlaylistLoader extends EventTarget {

load(isFinalRendition) {
window.clearTimeout(this.mediaUpdateTimeout);
window.clearTimeout(this.minimumUpdatePeriodTimeout_);
this.mediaUpdateTimeout = null;

const media = this.media();

Expand Down Expand Up @@ -696,45 +712,65 @@ export default class DashPlaylistLoader extends EventTarget {
this.masterPlaylistLoader_.srcUrl = location;
}

// if the minimumUpdatePeriod was changed, update the minimumUpdatePeriodTimeout_
if (!oldMaster || (newMaster && oldMaster.minimumUpdatePeriod !== newMaster.minimumUpdatePeriod)) {
if (!oldMaster || (newMaster && newMaster.minimumUpdatePeriod !== oldMaster.minimumUpdatePeriod)) {
this.updateMinimumUpdatePeriodTimeout_();
}

return Boolean(newMaster);
}

updateMinimumUpdatePeriodTimeout_() {
// Clear existing timeout
window.clearTimeout(this.minimumUpdatePeriodTimeout_);
const mpl = this.masterPlaylistLoader_;

const createMUPTimeout = (mup) => {
this.minimumUpdatePeriodTimeout_ = window.setTimeout(() => {
this.trigger('minimumUpdatePeriod');
createMUPTimeout(mup);
}, mup);
};
// cancel any pending creation of mup on media
// a new one will be added if needed.
if (mpl.createMupOnMedia_) {
mpl.off('loadedmetadata', mpl.createMupOnMedia_);
mpl.createMupOnMedia_ = null;
}

const minimumUpdatePeriod = this.masterPlaylistLoader_.master && this.masterPlaylistLoader_.master.minimumUpdatePeriod;
// clear any pending timeouts
if (mpl.minimumUpdatePeriodTimeout_) {
window.clearTimeout(mpl.minimumUpdatePeriodTimeout_);
mpl.minimumUpdatePeriodTimeout_ = null;
}

if (minimumUpdatePeriod > 0) {
createMUPTimeout(minimumUpdatePeriod);
let mup = mpl.master && mpl.master.minimumUpdatePeriod;

// If the minimumUpdatePeriod has a value of 0, that indicates that the current
// MPD has no future validity, so a new one will need to be acquired when new
// media segments are to be made available. Thus, we use the target duration
// in this case
} else if (minimumUpdatePeriod === 0) {
// If we haven't yet selected a playlist, wait until then so we know the
// target duration
if (!this.media()) {
this.one('loadedplaylist', () => {
createMUPTimeout(this.media().targetDuration * 1000);
});
if (mup === 0) {
if (mpl.media()) {
mup = mpl.media().targetDuration * 1000;
} else {
createMUPTimeout(this.media().targetDuration * 1000);
mpl.createMupOnMedia_ = mpl.updateMinimumUpdatePeriodTimeout_;
mpl.one('loadedmetadata', mpl.createMupOnMedia_);
}
}

// if minimumUpdatePeriod is invalid or <= zero, which
// can happen when a live video becomes VOD. skip timeout
// creation.
if (typeof mup !== 'number' || mup <= 0) {
if (mup < 0) {
this.logger_(`found invalid minimumUpdatePeriod of ${mup}, not setting a timeout`);
}
return;
}

this.createMUPTimeout_(mup);
}

createMUPTimeout_(mup) {
const mpl = this.masterPlaylistLoader_;

mpl.minimumUpdatePeriodTimeout_ = window.setTimeout(() => {
mpl.minimumUpdatePeriodTimeout_ = null;
mpl.trigger('minimumUpdatePeriod');
mpl.createMUPTimeout_(mup);
}, mup);
}

/**
Expand Down Expand Up @@ -791,10 +827,19 @@ export default class DashPlaylistLoader extends EventTarget {
this.trigger('playlistunchanged');
}

if (!this.media().endList) {
this.mediaUpdateTimeout = window.setTimeout(() => {
this.trigger('mediaupdatetimeout');
}, refreshDelay(this.media(), Boolean(mediaChanged)));
if (!this.mediaUpdateTimeout) {
const createMediaUpdateTimeout = () => {
if (this.media().endList) {
return;
}

this.mediaUpdateTimeout = window.setTimeout(() => {
this.trigger('mediaupdatetimeout');
createMediaUpdateTimeout();
}, refreshDelay(this.media(), Boolean(mediaChanged)));
};

createMediaUpdateTimeout();
}

this.trigger('loadedplaylist');
Expand Down
14 changes: 13 additions & 1 deletion src/master-playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,14 @@ export class MasterPlaylistController extends videojs.EventTarget {

this.masterPlaylistLoader_.on('playlistunchanged', () => {
const updatedPlaylist = this.masterPlaylistLoader_.media();

// ignore unchanged playlists that have already been
// excluded for not-changing. We likely just have a really slowly updating
// playlist.
if (updatedPlaylist.lastExcludeReason_ === 'playlist-unchanged') {
return;
}

const playlistOutdated = this.stuckAtPlaylistEnd_(updatedPlaylist);

if (playlistOutdated) {
Expand All @@ -480,7 +488,8 @@ export class MasterPlaylistController extends videojs.EventTarget {
// one is updating (and give the player a chance to re-adjust to the
// safe live point).
this.blacklistCurrentPlaylist({
message: 'Playlist no longer updating.'
message: 'Playlist no longer updating.',
reason: 'playlist-unchanged'
});
// useful for monitoring QoS
this.tech_.trigger('playliststuck');
Expand Down Expand Up @@ -1066,6 +1075,9 @@ export class MasterPlaylistController extends videojs.EventTarget {

// Blacklist this playlist
currentPlaylist.excludeUntil = Date.now() + (blacklistDuration * 1000);
if (error.reason) {
currentPlaylist.lastExcludeReason_ = error.reason;
}
this.tech_.trigger('blacklistplaylist');
this.tech_.trigger({type: 'usage', name: 'vhs-rendition-blacklisted'});
this.tech_.trigger({type: 'usage', name: 'hls-rendition-blacklisted'});
Expand Down

0 comments on commit 0dceb5b

Please sign in to comment.