Browse Source

Immediately reload the source when an error is emitted by the player (#902)

* Immediately reload the source when an error is emitted by the player

* Fixed linting opinions

* Updated pluging to use the Tech for currentSource
pull/6/head
Jon-Carlos Rivera 9 years ago
committed by David LaPalomento
parent
commit
dc201b9e44
  1. 34
      src/reload-source-on-error.js
  2. 2
      src/videojs-contrib-hls.js
  3. 105
      test/reload-source-on-error.test.js
  4. 2
      test/test-helpers.js

34
src/reload-source-on-error.js

@ -0,0 +1,34 @@
/**
* Reload the source when an error is detected as long as there
* wasn't an error previously within the last 30 seconds
*/
const reloadSourceOnError = function() {
const player = this;
let lastCalled = 0;
const reloadSource = function() {
let tech = player.tech({ IWillNotUseThisInPlugins: true });
let sourceObj = tech.currentSource_;
let seekTo = (player.duration() !== Infinity && player.currentTime()) || 0;
if (Date.now() - lastCalled < 30 * 1000) {
return;
}
lastCalled = Date.now();
if (seekTo) {
player.one('loadedmetadata', () => {
player.currentTime(seekTo);
});
}
player.src(sourceObj);
player.play();
};
player.on('error', reloadSource);
player.on('dispose', () => {
player.off('error', reloadSource);
});
};
export default reloadSourceOnError;

2
src/videojs-contrib-hls.js

@ -18,6 +18,7 @@ import Config from './config';
import renditionSelectionMixin from './rendition-mixin'; import renditionSelectionMixin from './rendition-mixin';
import window from 'global/window'; import window from 'global/window';
import PlaybackWatcher from './playback-watcher'; import PlaybackWatcher from './playback-watcher';
import reloadSourceOnError from './reload-source-on-error';
const Hls = { const Hls = {
PlaylistLoader, PlaylistLoader,
@ -662,6 +663,7 @@ videojs.Hls = Hls;
videojs.m3u8 = m3u8; videojs.m3u8 = m3u8;
videojs.registerComponent('Hls', Hls); videojs.registerComponent('Hls', Hls);
videojs.options.hls = videojs.options.hls || {}; videojs.options.hls = videojs.options.hls || {};
videojs.plugin('reloadSourceOnError', reloadSourceOnError);
module.exports = { module.exports = {
Hls, Hls,

105
test/reload-source-on-error.test.js

@ -0,0 +1,105 @@
import QUnit from 'qunit';
import videojs from 'video.js';
import sinon from 'sinon';
import reloadSourceOnError from '../src/reload-source-on-error';
QUnit.module('ReloadSourceOnError', {
beforeEach() {
this.clock = sinon.useFakeTimers();
// setup a player
this.player = new videojs.EventTarget();
this.player.currentValues = {
currentTime: 10,
duration: 12
};
this.tech = {
currentSource_: {
src: 'thisisasource.m3u8',
type: 'doesn\'t/matter'
}
};
this.player.tech = () => {
return this.tech;
};
this.player.duration = () => {
return this.player.currentValues.duration;
};
this.player.src = (source) => {
this.player.currentValues.currentTime = 0;
this.player.src.calledWith.push(source);
};
this.player.src.calledWith = [];
this.player.currentTime = (time) => {
if (time) {
this.player.currentTime.calledWith.push(time);
this.player.currentValues.currentTime = time;
}
return this.player.currentValues.currentTime;
};
this.player.currentTime.calledWith = [];
this.player.play = () => {
this.player.play.called++;
};
this.player.play.called = 0;
reloadSourceOnError.call(this.player);
this.clock.tick(60 * 1000);
},
afterEach() {
this.clock.restore();
}
});
QUnit.test('triggers on player error', function() {
this.player.trigger('error', -2);
QUnit.equal(this.player.src.calledWith.length, 1, 'player.src was called');
QUnit.deepEqual(this.player.src.calledWith[0], this.tech.currentSource_, 'player.src was called with player.currentSource');
});
QUnit.test('seeks to currentTime in VOD', function() {
this.player.trigger('error', -2);
this.player.trigger('loadedmetadata');
QUnit.equal(this.player.currentTime.calledWith.length, 1, 'player.currentTime was called');
QUnit.deepEqual(this.player.currentTime.calledWith[0], 10, 'player.currentTime was called with the right value');
});
QUnit.test('doesn\'t seek to currentTime in live', function() {
this.player.currentValues.duration = Infinity;
this.player.trigger('error', -2);
this.player.trigger('loadedmetadata');
QUnit.equal(this.player.currentTime.calledWith.length, 0, 'player.currentTime was not called');
QUnit.deepEqual(this.player.currentTime(), 0, 'player.currentTime is still zero');
});
QUnit.test('only allows a retry once every 30 seconds', function() {
this.player.trigger('error', -2);
this.player.trigger('loadedmetadata');
QUnit.equal(this.player.src.calledWith.length, 1, 'player.src was called once');
// Advance 60 seconds
this.clock.tick(60 * 1000);
this.player.trigger('error', -2);
this.player.trigger('loadedmetadata');
QUnit.equal(this.player.src.calledWith.length, 2, 'player.src was called twice');
// Advance 29 seconds
this.clock.tick(29 * 1000);
this.player.trigger('error', -2);
this.player.trigger('loadedmetadata');
QUnit.equal(this.player.src.calledWith.length, 2, 'player.src was called twice');
});

2
test/test-helpers.js

@ -207,7 +207,7 @@ export const mockTech = function(tech) {
tech.paused_ = false; tech.paused_ = false;
tech.trigger('play'); tech.trigger('play');
}; };
tech.pause_ = tech.pause_;
tech.pause_ = tech.pause;
tech.pause = function() { tech.pause = function() {
tech.pause_(); tech.pause_();
tech.paused_ = true; tech.paused_ = true;

Loading…
Cancel
Save