mirror of
https://github.com/videojs/video.js.git
synced 2024-12-27 02:43:45 +02:00
Move manual events into tech controller
Migrate the timers that manage creating timeupdate and progress events when the tech doesn't support them natively. Now, techs that extend MediaTechController will continue to automatically pick up synthetic playback and buffering events but they're scoped much more closely to the entity that needs them. In addition, time and progress tracking have been moved much earlier into the component initialization which fixes #1414.
This commit is contained in:
parent
692a0e9c09
commit
f4ce62d4b1
@ -145,6 +145,7 @@ vjs.Flash.prototype.src = function(src){
|
||||
vjs.Flash.prototype['setCurrentTime'] = function(time){
|
||||
this.lastSeekTarget_ = time;
|
||||
this.el_.vjs_setProperty('currentTime', time);
|
||||
vjs.MediaTechController.prototype.setCurrentTime.call(this);
|
||||
};
|
||||
|
||||
vjs.Flash.prototype['currentTime'] = function(time){
|
||||
|
@ -18,6 +18,16 @@ vjs.MediaTechController = vjs.Component.extend({
|
||||
options.reportTouchActivity = false;
|
||||
vjs.Component.call(this, player, options, ready);
|
||||
|
||||
// Manually track progress in cases where the browser/flash player doesn't report it.
|
||||
if (!this.features['progressEvents']) {
|
||||
this.manualProgressOn();
|
||||
}
|
||||
|
||||
// Manually track timeudpates in cases where the browser/flash player doesn't report it.
|
||||
if (!this.features['timeupdateEvents']) {
|
||||
this.manualTimeUpdatesOn();
|
||||
}
|
||||
|
||||
this.initControlsListeners();
|
||||
}
|
||||
});
|
||||
@ -153,6 +163,109 @@ vjs.MediaTechController.prototype.onTap = function(){
|
||||
this.player().userActive(!this.player().userActive());
|
||||
};
|
||||
|
||||
/* Fallbacks for unsupported event types
|
||||
================================================================================ */
|
||||
// Manually trigger progress events based on changes to the buffered amount
|
||||
// Many flash players and older HTML5 browsers don't send progress or progress-like events
|
||||
vjs.MediaTechController.prototype.manualProgressOn = function(){
|
||||
this.manualProgress = true;
|
||||
|
||||
// Trigger progress watching when a source begins loading
|
||||
this.trackProgress();
|
||||
|
||||
// Watch for a native progress event call on the tech element
|
||||
// In HTML5, some older versions don't support the progress event
|
||||
// So we're assuming they don't, and turning off manual progress if they do.
|
||||
// As opposed to doing user agent detection
|
||||
this.one('progress', function(){
|
||||
|
||||
// Update known progress support for this playback technology
|
||||
this.features['progressEvents'] = true;
|
||||
|
||||
// Turn off manual progress tracking
|
||||
this.manualProgressOff();
|
||||
});
|
||||
};
|
||||
|
||||
vjs.MediaTechController.prototype.manualProgressOff = function(){
|
||||
this.manualProgress = false;
|
||||
this.stopTrackingProgress();
|
||||
};
|
||||
|
||||
vjs.MediaTechController.prototype.trackProgress = function(){
|
||||
|
||||
this.progressInterval = setInterval(vjs.bind(this, function(){
|
||||
// Don't trigger unless buffered amount is greater than last time
|
||||
|
||||
var bufferedPercent = this.player().bufferedPercent();
|
||||
|
||||
if (this.bufferedPercent_ != bufferedPercent) {
|
||||
this.player().trigger('progress');
|
||||
}
|
||||
|
||||
this.bufferedPercent_ = bufferedPercent;
|
||||
|
||||
if (bufferedPercent === 1) {
|
||||
this.stopTrackingProgress();
|
||||
}
|
||||
}), 500);
|
||||
};
|
||||
vjs.MediaTechController.prototype.stopTrackingProgress = function(){ clearInterval(this.progressInterval); };
|
||||
|
||||
/*! Time Tracking -------------------------------------------------------------- */
|
||||
vjs.MediaTechController.prototype.manualTimeUpdatesOn = function(){
|
||||
this.manualTimeUpdates = true;
|
||||
|
||||
this.player().on('play', vjs.bind(this, this.trackCurrentTime));
|
||||
this.player().on('pause', vjs.bind(this, this.stopTrackingCurrentTime));
|
||||
// timeupdate is also called by .currentTime whenever current time is set
|
||||
|
||||
// Watch for native timeupdate event
|
||||
this.one('timeupdate', function(){
|
||||
// Update known progress support for this playback technology
|
||||
this.features['timeupdateEvents'] = true;
|
||||
// Turn off manual progress tracking
|
||||
this.manualTimeUpdatesOff();
|
||||
});
|
||||
};
|
||||
|
||||
vjs.MediaTechController.prototype.manualTimeUpdatesOff = function(){
|
||||
this.manualTimeUpdates = false;
|
||||
this.stopTrackingCurrentTime();
|
||||
this.off('play', this.trackCurrentTime);
|
||||
this.off('pause', this.stopTrackingCurrentTime);
|
||||
};
|
||||
|
||||
vjs.MediaTechController.prototype.trackCurrentTime = function(){
|
||||
if (this.currentTimeInterval) { this.stopTrackingCurrentTime(); }
|
||||
this.currentTimeInterval = setInterval(vjs.bind(this, function(){
|
||||
this.player().trigger('timeupdate');
|
||||
}), 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
|
||||
};
|
||||
|
||||
// Turn off play progress tracking (when paused or dragging)
|
||||
vjs.MediaTechController.prototype.stopTrackingCurrentTime = function(){
|
||||
clearInterval(this.currentTimeInterval);
|
||||
|
||||
// #1002 - if the video ends right before the next timeupdate would happen,
|
||||
// the progress bar won't make it all the way to the end
|
||||
this.player().trigger('timeupdate');
|
||||
};
|
||||
|
||||
vjs.MediaTechController.prototype.dispose = function() {
|
||||
// Turn off any manual progress or timeupdate tracking
|
||||
if (this.manualProgress) { this.manualProgressOff(); }
|
||||
|
||||
if (this.manualTimeUpdates) { this.manualTimeUpdatesOff(); }
|
||||
|
||||
vjs.Component.prototype.dispose.call(this);
|
||||
};
|
||||
|
||||
vjs.MediaTechController.prototype.setCurrentTime = function() {
|
||||
// improve the accuracy of manual timeupdates
|
||||
if (this.manualTimeUpdates) { this.player().trigger('timeupdate'); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide a default setPoster method for techs
|
||||
*
|
||||
|
113
src/js/player.js
113
src/js/player.js
@ -163,10 +163,6 @@ vjs.Player.prototype.dispose = function(){
|
||||
if (this.tag && this.tag['player']) { this.tag['player'] = null; }
|
||||
if (this.el_ && this.el_['player']) { this.el_['player'] = null; }
|
||||
|
||||
// Ensure that tracking progress and time progress will stop and plater deleted
|
||||
this.stopTrackingProgress();
|
||||
this.stopTrackingCurrentTime();
|
||||
|
||||
if (this.tech) { this.tech.dispose(); }
|
||||
|
||||
// Component dispose
|
||||
@ -311,16 +307,6 @@ vjs.Player.prototype.loadTech = function(techName, source){
|
||||
|
||||
var techReady = function(){
|
||||
this.player_.triggerReady();
|
||||
|
||||
// Manually track progress in cases where the browser/flash player doesn't report it.
|
||||
if (!this.features['progressEvents']) {
|
||||
this.player_.manualProgressOn();
|
||||
}
|
||||
|
||||
// Manually track timeudpates in cases where the browser/flash player doesn't report it.
|
||||
if (!this.features['timeupdateEvents']) {
|
||||
this.player_.manualTimeUpdatesOn();
|
||||
}
|
||||
};
|
||||
|
||||
// Grab tech-specific options from player options and add source and parent element to use.
|
||||
@ -344,11 +330,6 @@ vjs.Player.prototype.loadTech = function(techName, source){
|
||||
vjs.Player.prototype.unloadTech = function(){
|
||||
this.isReady_ = false;
|
||||
|
||||
// Turn off any manual progress or timeupdate tracking
|
||||
if (this.manualProgress) { this.manualProgressOff(); }
|
||||
|
||||
if (this.manualTimeUpdates) { this.manualTimeUpdatesOff(); }
|
||||
|
||||
this.tech.dispose();
|
||||
|
||||
this.tech = false;
|
||||
@ -368,98 +349,7 @@ vjs.Player.prototype.unloadTech = function(){
|
||||
// vjs.log('loadedTech')
|
||||
// },
|
||||
|
||||
/* Fallbacks for unsupported event types
|
||||
================================================================================ */
|
||||
// Manually trigger progress events based on changes to the buffered amount
|
||||
// Many flash players and older HTML5 browsers don't send progress or progress-like events
|
||||
vjs.Player.prototype.manualProgressOn = function(){
|
||||
this.manualProgress = true;
|
||||
|
||||
// Trigger progress watching when a source begins loading
|
||||
this.trackProgress();
|
||||
|
||||
// Watch for a native progress event call on the tech element
|
||||
// In HTML5, some older versions don't support the progress event
|
||||
// So we're assuming they don't, and turning off manual progress if they do.
|
||||
// As opposed to doing user agent detection
|
||||
if (this.tech) {
|
||||
this.tech.one('progress', function(){
|
||||
|
||||
// Update known progress support for this playback technology
|
||||
this.features['progressEvents'] = true;
|
||||
|
||||
// Turn off manual progress tracking
|
||||
this.player_.manualProgressOff();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
vjs.Player.prototype.manualProgressOff = function(){
|
||||
this.manualProgress = false;
|
||||
this.stopTrackingProgress();
|
||||
};
|
||||
|
||||
vjs.Player.prototype.trackProgress = function(){
|
||||
|
||||
this.progressInterval = setInterval(vjs.bind(this, function(){
|
||||
// Don't trigger unless buffered amount is greater than last time
|
||||
|
||||
var bufferedPercent = this.bufferedPercent();
|
||||
|
||||
if (this.cache_.bufferedPercent != bufferedPercent) {
|
||||
this.trigger('progress');
|
||||
}
|
||||
|
||||
this.cache_.bufferedPercent = bufferedPercent;
|
||||
|
||||
if (bufferedPercent == 1) {
|
||||
this.stopTrackingProgress();
|
||||
}
|
||||
}), 500);
|
||||
};
|
||||
vjs.Player.prototype.stopTrackingProgress = function(){ clearInterval(this.progressInterval); };
|
||||
|
||||
/*! Time Tracking -------------------------------------------------------------- */
|
||||
vjs.Player.prototype.manualTimeUpdatesOn = function(){
|
||||
this.manualTimeUpdates = true;
|
||||
|
||||
this.on('play', this.trackCurrentTime);
|
||||
this.on('pause', this.stopTrackingCurrentTime);
|
||||
// timeupdate is also called by .currentTime whenever current time is set
|
||||
|
||||
// Watch for native timeupdate event
|
||||
if (this.tech) {
|
||||
this.tech.one('timeupdate', function(){
|
||||
// Update known progress support for this playback technology
|
||||
this.features['timeupdateEvents'] = true;
|
||||
// Turn off manual progress tracking
|
||||
this.player_.manualTimeUpdatesOff();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
vjs.Player.prototype.manualTimeUpdatesOff = function(){
|
||||
this.manualTimeUpdates = false;
|
||||
this.stopTrackingCurrentTime();
|
||||
this.off('play', this.trackCurrentTime);
|
||||
this.off('pause', this.stopTrackingCurrentTime);
|
||||
};
|
||||
|
||||
vjs.Player.prototype.trackCurrentTime = function(){
|
||||
if (this.currentTimeInterval) { this.stopTrackingCurrentTime(); }
|
||||
this.currentTimeInterval = setInterval(vjs.bind(this, function(){
|
||||
this.trigger('timeupdate');
|
||||
}), 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
|
||||
};
|
||||
|
||||
// Turn off play progress tracking (when paused or dragging)
|
||||
vjs.Player.prototype.stopTrackingCurrentTime = function(){
|
||||
clearInterval(this.currentTimeInterval);
|
||||
|
||||
// #1002 - if the video ends right before the next timeupdate would happen,
|
||||
// the progress bar won't make it all the way to the end
|
||||
this.trigger('timeupdate');
|
||||
};
|
||||
// /* Player event handlers (how the player reacts to certain events)
|
||||
// ================================================================================ */
|
||||
|
||||
@ -782,9 +672,6 @@ vjs.Player.prototype.currentTime = function(seconds){
|
||||
|
||||
this.techCall('setCurrentTime', seconds);
|
||||
|
||||
// improve the accuracy of manual timeupdates
|
||||
if (this.manualTimeUpdates) { this.trigger('timeupdate'); }
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
'test/unit/mediafaker.js',
|
||||
'test/unit/player.js',
|
||||
'test/unit/core.js',
|
||||
'test/unit/media.js',
|
||||
'test/unit/media.html5.js',
|
||||
'test/unit/controls.js',
|
||||
'test/unit/poster.js',
|
||||
|
@ -6,6 +6,8 @@ function sinon(){}
|
||||
sinon.stub = function(){};
|
||||
sinon.spy = function(){};
|
||||
sinon.mock = function(){};
|
||||
sinon.useFakeTimers = function(){};
|
||||
sinon.clock.tick = function(){};
|
||||
|
||||
Function.prototype.alwaysCalledOn = function(){};
|
||||
Function.prototype.alwaysCalledWith = function(){};
|
||||
|
@ -69,7 +69,9 @@ test('currentTime is the seek target during seeking', function() {
|
||||
parentEl = document.createElement('div'),
|
||||
tech = new vjs.Flash({
|
||||
id: noop,
|
||||
bufferedPercent: noop,
|
||||
on: noop,
|
||||
trigger: noop,
|
||||
options_: {}
|
||||
}, {
|
||||
'parentEl': parentEl
|
||||
@ -103,6 +105,7 @@ test('dispose removes the object element even before ready fires', function() {
|
||||
tech = new vjs.Flash({
|
||||
id: noop,
|
||||
on: noop,
|
||||
trigger: noop,
|
||||
options_: {}
|
||||
}, {
|
||||
'parentEl': parentEl
|
||||
|
@ -11,11 +11,13 @@ module('HTML5', {
|
||||
el: function(){ return el; },
|
||||
options_: {},
|
||||
options: function(){ return {}; },
|
||||
bufferedPercent: function() { return 0; },
|
||||
controls: function(){ return false; },
|
||||
usingNativeControls: function(){ return false; },
|
||||
on: function(){ return this; },
|
||||
off: function() { return this; },
|
||||
ready: function(){}
|
||||
ready: function(){},
|
||||
trigger: function(){}
|
||||
};
|
||||
tech = new vjs.Html5(player, {});
|
||||
},
|
||||
|
142
test/unit/media.js
Normal file
142
test/unit/media.js
Normal file
@ -0,0 +1,142 @@
|
||||
var noop = function() {}, clock, features;
|
||||
|
||||
module('Media Tech', {
|
||||
'setup': function() {
|
||||
clock = sinon.useFakeTimers();
|
||||
features = videojs.util.mergeOptions({}, videojs.MediaTechController.prototype.features);
|
||||
},
|
||||
'teardown': function() {
|
||||
clock.restore();
|
||||
videojs.MediaTechController.prototype.features = features;
|
||||
}
|
||||
});
|
||||
|
||||
test('should synthesize timeupdate events by default', function() {
|
||||
var timeupdates = 0, playHandler, i, tech;
|
||||
tech = new videojs.MediaTechController({
|
||||
id: noop,
|
||||
on: function(event, handler) {
|
||||
if (event === 'play') {
|
||||
playHandler = handler;
|
||||
}
|
||||
},
|
||||
trigger: function(event) {
|
||||
if (event === 'timeupdate') {
|
||||
timeupdates++;
|
||||
}
|
||||
}
|
||||
});
|
||||
playHandler.call(tech);
|
||||
tech.on('timeupdate', function() {
|
||||
timeupdates++;
|
||||
});
|
||||
|
||||
clock.tick(250);
|
||||
equal(timeupdates, 1, 'triggered one timeupdate');
|
||||
});
|
||||
|
||||
test('stops timeupdates if the tech produces them natively', function() {
|
||||
var timeupdates = 0, tech, playHandler, expected;
|
||||
tech = new videojs.MediaTechController({
|
||||
id: noop,
|
||||
on: function(event, handler) {
|
||||
if (event === 'play') {
|
||||
playHandler = handler;
|
||||
}
|
||||
},
|
||||
bufferedPercent: noop,
|
||||
trigger: function(event) {
|
||||
if (event === 'timeupdate') {
|
||||
timeupdates++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
playHandler.call(tech);
|
||||
// simulate a native timeupdate event
|
||||
tech.trigger('timeupdate');
|
||||
|
||||
expected = timeupdates;
|
||||
clock.tick(10 * 1000);
|
||||
equal(timeupdates, expected, 'did not simulate timeupdates');
|
||||
});
|
||||
|
||||
test('stops manual timeupdates while paused', function() {
|
||||
var timeupdates = 0, tech, playHandler, pauseHandler, expected;
|
||||
tech = new videojs.MediaTechController({
|
||||
id: noop,
|
||||
on: function(event, handler) {
|
||||
if (event === 'play') {
|
||||
playHandler = handler;
|
||||
} else if (event === 'pause') {
|
||||
pauseHandler = handler;
|
||||
}
|
||||
},
|
||||
bufferedPercent: noop,
|
||||
trigger: function(event) {
|
||||
if (event === 'timeupdate') {
|
||||
timeupdates++;
|
||||
}
|
||||
}
|
||||
});
|
||||
playHandler.call(tech);
|
||||
clock.tick(10 * 250);
|
||||
ok(timeupdates > 0, 'timeupdates fire during playback');
|
||||
|
||||
pauseHandler.call(tech);
|
||||
timeupdates = 0;
|
||||
clock.tick(10 * 250);
|
||||
equal(timeupdates, 0, 'timeupdates do not fire when paused');
|
||||
|
||||
playHandler.call(tech);
|
||||
clock.tick(10 * 250);
|
||||
ok(timeupdates > 0, 'timeupdates fire when playback resumes');
|
||||
});
|
||||
|
||||
test('should synthesize progress events by default', function() {
|
||||
var progresses = 0, tech;
|
||||
tech = new videojs.MediaTechController({
|
||||
id: noop,
|
||||
on: noop,
|
||||
bufferedPercent: function() {
|
||||
return 0;
|
||||
},
|
||||
trigger: function(event) {
|
||||
if (event === 'progress') {
|
||||
progresses++;
|
||||
}
|
||||
}
|
||||
});
|
||||
tech.on('progress', function() {
|
||||
progresses++;
|
||||
});
|
||||
|
||||
clock.tick(500);
|
||||
equal(progresses, 1, 'triggered one event');
|
||||
});
|
||||
|
||||
test('stops progress events if the tech produces them natively', function() {
|
||||
var end = 0, buffered = 0, progresses = 0, tech;
|
||||
tech = new videojs.MediaTechController({
|
||||
id: noop,
|
||||
on: noop,
|
||||
// progress will be detected any time it is queried
|
||||
bufferedPercent: function() {
|
||||
return buffered++;
|
||||
},
|
||||
trigger: function(event) {
|
||||
if (event === 'progress') {
|
||||
progresses++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// simulate a native progress event
|
||||
tech.trigger('progress');
|
||||
tech.on('progress', function() {
|
||||
progresses++;
|
||||
});
|
||||
|
||||
clock.tick(10 * 1000);
|
||||
equal(progresses, 0, 'did not simulate progress');
|
||||
});
|
Loading…
Reference in New Issue
Block a user