1
0
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:
David LaPalomento 2014-08-13 19:44:36 -04:00 committed by Steve Heffernan
parent 692a0e9c09
commit f4ce62d4b1
8 changed files with 265 additions and 114 deletions

View File

@ -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){

View File

@ -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
*

View File

@ -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;
}

View File

@ -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',

View File

@ -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(){};

View File

@ -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

View File

@ -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
View 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');
});