mirror of
https://github.com/iv-org/videojs-quality-selector.git
synced 2025-04-25 09:49:11 -04:00

When the HTML5 preload attribute is set to 'none' or when using Safari (even when the preload attribute is not 'none'), the video does not resume playing after the quality is changed using the quality selector menu. The quality selector plugin was listening for the 'loadeddata' event in order to know when to resume playback, but the 'loadeddata' event does not fire when the preload attribute is set to 'none', and Safari does not fetch enough data to emit a 'loadeddata' event.
81 lines
3.1 KiB
JavaScript
81 lines
3.1 KiB
JavaScript
'use strict';
|
|
|
|
var Class = require('class.extend');
|
|
|
|
module.exports = Class.extend({
|
|
init: function(player, seekToTime) {
|
|
this._player = player;
|
|
this._seekToTime = seekToTime;
|
|
this._hasFinished = false;
|
|
this._keepThisInstanceWhenPlayerSourcesChange = false;
|
|
this._seekWhenSafe();
|
|
},
|
|
|
|
_seekWhenSafe: function() {
|
|
var HAVE_FUTURE_DATA = 3;
|
|
|
|
// `readyState` in Video.js is the same as the HTML5 Media element's `readyState`
|
|
// property.
|
|
//
|
|
// `readyState` is an enum of 5 values (0-4), each of which represent a state of
|
|
// readiness to play. The meaning of the values range from HAVE_NOTHING (0), meaning
|
|
// no data is available to HAVE_ENOUGH_DATA (4), meaning all data is loaded and the
|
|
// video can be played all the way through.
|
|
//
|
|
// In order to seek successfully, the `readyState` must be at least HAVE_FUTURE_DATA
|
|
// (3).
|
|
//
|
|
// @see http://docs.videojs.com/player#readyState
|
|
// @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
|
|
// @see https://dev.w3.org/html5/spec-preview/media-elements.html#seek-the-media-controller
|
|
if (this._player.readyState() < HAVE_FUTURE_DATA) {
|
|
this._seekFn = this._seek.bind(this);
|
|
// The `canplay` event means that the `readyState` is at least HAVE_FUTURE_DATA.
|
|
this._player.one('canplay', this._seekFn);
|
|
} else {
|
|
this._seek();
|
|
}
|
|
},
|
|
|
|
onPlayerSourcesChange: function() {
|
|
if (this._keepThisInstanceWhenPlayerSourcesChange) {
|
|
// By setting this to `false`, we know that if the player sources change again
|
|
// the change did not originate from a quality selection change, the new sources
|
|
// are likely different from the old sources, and so this pending seek no longer
|
|
// applies.
|
|
this._keepThisInstanceWhenPlayerSourcesChange = false;
|
|
} else {
|
|
this.cancel();
|
|
}
|
|
},
|
|
|
|
onQualitySelectionChange: function() {
|
|
// `onPlayerSourcesChange` will cancel this pending seek unless we tell it not to.
|
|
// We need to reuse this same pending seek instance because when the player is
|
|
// paused, the `preload` attribute is set to `none`, and the user selects one
|
|
// quality option and then another, the player cannot seek until the player has
|
|
// enough data to do so (and the `canplay` event is fired) and thus on the second
|
|
// selection the player's `currentTime()` is `0` and when the video plays we would
|
|
// seek to `0` instead of the correct time.
|
|
if (!this.hasFinished()) {
|
|
this._keepThisInstanceWhenPlayerSourcesChange = true;
|
|
}
|
|
},
|
|
|
|
_seek: function() {
|
|
this._player.currentTime(this._seekToTime);
|
|
this._keepThisInstanceWhenPlayerSourcesChange = false;
|
|
this._hasFinished = true;
|
|
},
|
|
|
|
hasFinished: function() {
|
|
return this._hasFinished;
|
|
},
|
|
|
|
cancel: function() {
|
|
this._player.off('canplay', this._seekFn);
|
|
this._keepThisInstanceWhenPlayerSourcesChange = false;
|
|
this._hasFinished = true;
|
|
},
|
|
});
|