Skip to content
This repository has been archived by the owner on Jan 12, 2019. It is now read-only.

Commit

Permalink
A few updates and fixes to make the gap-skipper more robusto
Browse files Browse the repository at this point in the history
  • Loading branch information
jrivera committed Jun 8, 2016
1 parent 774374c commit 9cdc027
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 47 deletions.
146 changes: 101 additions & 45 deletions src/gap-skipper.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,66 +26,114 @@ export default class GapSkipper {

this.player = videojs(options.tech.options_.playerId);
this.tech_ = options.tech;
this.seeking = false;
this.consecutiveUpdates = 0;
this.timer = null;
this.playerState = null;
this.lastRecordedTime = null;

this.player.on('timeupdate', () => {
if (this.player.paused()) {
return;
}
let currentTime = this.player.currentTime();
if (options.debug) {
this.logger_ = console.log.bind(console, '<gap-skipper>');
}

this.player.one('canplaythrough', () => {
this.player.on('waiting', () => {
this.setTimer_();
});

// The purpose of this function is to emulate the "waiting" event on
// browsers that do not emit it when they are stalled waiting for
// more data
this.player.on('timeupdate', () => {
if (this.player.paused()) {
return;
}

if (this.consecutiveUpdates === 5 &&
currentTime === this.lastRecordedTime) {
// trigger waiting
if (this.playerState !== 'waiting') {
let currentTime = this.player.currentTime();

if (this.consecutiveUpdates === 5 &&
currentTime === this.lastRecordedTime) {

// trigger waiting
this.player.trigger('waiting');
this.consecutiveUpdates++;
} else if (currentTime === this.lastRecordedTime) {
this.consecutiveUpdates++;
} else {
this.consecutiveUpdates = 0;
this.playerState = 'waiting';
this.skipTheGap_();
this.lastRecordedTime = currentTime;
}
} else if (currentTime === this.lastRecordedTime) {
this.consecutiveUpdates++;
} else {
this.consecutiveUpdates = 0;
this.lastRecordedTime = currentTime;
}
});
});

// Don't listen for waiting while seeking
this.player.on('seeking', () => {
this.seeking = true;
// Set of conditions that reset the gap-skipper logic
[
'seeking',
'seeked',
'pause',
'playing',
'error'
].forEach((event) => {
this.player.on(event, () => {
this.cancelTimer_();
});
});
});
}

// Listen for waiting when finished seeking
this.player.on('seeked', () => {
this.seeking = false;
});
/**
* Cancels any pending timers and resets the 'timeupdate' mechanism
* designed to detect that we are stalled
*
* @private
*/
cancelTimer_() {
this.consecutiveUpdates = 0;

this.player.on('playing', () => {
this.player.on('waiting', this.skipTheGap_);
});
if (this.timer) {
clearTimeout(this.timer);
}

this.player.on('error', () => {
if (this.timer) {
clearTimeout(this.timer);
}
});
this.timer = null;
}

/**
* Set a timer to skip the unbuffered region.
* Timer callback. If playback still has not proceeded, then we seek
* to the start of the next buffered region.
*
* @private
*/
skipTheGap_() {
skipTheGap_ (scheduledCurrentTime) {
let buffered = this.player.buffered();
let currentTime = this.player.currentTime();
let nextRange = Ranges.findNextRange(buffered, currentTime);

this.consecutiveUpdates = 0;
this.timer = null;

if (this.seeking) {
this.logger_('timer triggered');

if (nextRange.length === 0) {
return;
}

this.logger_('currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0));

if (currentTime !== scheduledCurrentTime) {
return;
}

this.logger_('seeking to', nextRange.start(0) + Ranges.TIME_FUDGE_FACTOR);

// only seek if we still have not played
this.player.currentTime(nextRange.start(0) + Ranges.TIME_FUDGE_FACTOR);
}

/**
* Set a timer to skip the unbuffered region.
*
* @private
*/
setTimer_() {
this.logger_('triggered. currentTime:', this.player.currentTime());

let buffered = this.player.buffered();
let currentTime = this.player.currentTime();
let nextRange = Ranges.findNextRange(buffered, currentTime);
Expand All @@ -94,14 +142,22 @@ export default class GapSkipper {
return;
}

this.logger_('nextRange start:', nextRange.start(0));

if (this.timer !== null) {
return;
}

let difference = nextRange.start(0) - currentTime;

this.timer = setTimeout(() => {
if (this.player.currentTime() === currentTime) {
// only seek if we still have not played
this.player.currentTime(nextRange.start(0) + Ranges.TIME_FUDGE_FACTOR);
this.playerState = 'playing';
}
}, difference * 1000);
this.logger_('setting timer for', difference, 'seconds');
this.timer = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
}

/**
* A logger_ noop that is set to console.log if debugging is enabled globally.
*
* @private
*/
logger_() {}
}
2 changes: 0 additions & 2 deletions src/videojs-contrib-hls.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,6 @@ class HlsHandler extends Component {
this.tech_ = tech;
this.source_ = source;
this.stats = {};
// register gapSKipper plugin
gapSkipper(tech.options_.playerId, tech);

// handle global & Source Handler level options
this.options_ = videojs.mergeOptions(videojs.options.hls || {}, options.hls);
Expand Down

0 comments on commit 9cdc027

Please sign in to comment.