1
0
mirror of https://github.com/videojs/video.js.git synced 2025-02-08 12:05:47 +02:00

fix: cache middleware instances per player (#4939)

Middleware factories currently get run each time a source is set. Because middleware are assocated with a player, the factories should only run once per player.

This PR fixes it so that we associate a middleware instance with a middleware factory per player.
Each time a player is disposed, we will clear the cache of the middleware instances for that player.

Fixes #4677.
This commit is contained in:
Gary Katsevman 2018-02-12 17:30:27 -05:00 committed by GitHub
parent 6189baac3c
commit 29a8ee1d60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 1 deletions

View File

@ -523,6 +523,8 @@ class Player extends Component {
this.tag = null;
}
middleware.clearCacheForPlayer(this);
// the actual .el_ is removed here
super.dispose();
}

View File

@ -2,6 +2,7 @@ import { assign } from '../utils/obj.js';
import toTitleCase from '../utils/to-title-case.js';
const middlewares = {};
const middlewareInstances = {};
export const TERMINATOR = {};
@ -102,6 +103,43 @@ function executeRight(mws, method, value, terminated) {
}
}
export function clearCacheForPlayer(player) {
middlewareInstances[player.id()] = null;
}
/**
* {
* [playerId]: [[mwFactory, mwInstance], ...]
* }
*/
function getOrCreateFactory(player, mwFactory) {
const mws = middlewareInstances[player.id()];
let mw = null;
if (mws === undefined || mws === null) {
mw = mwFactory(player);
middlewareInstances[player.id()] = [[mwFactory, mw]];
return mw;
}
for (let i = 0; i < mws.length; i++) {
const [mwf, mwi] = mws[i];
if (mwf !== mwFactory) {
continue;
}
mw = mwi;
}
if (mw === null) {
mw = mwFactory(player);
mws.push([mwFactory, mw]);
}
return mw;
}
function setSourceHelper(src = {}, middleware = [], next, player, acc = [], lastRun = false) {
const [mwFactory, ...mwrest] = middleware;
@ -112,7 +150,7 @@ function setSourceHelper(src = {}, middleware = [], next, player, acc = [], last
// if we have an mwFactory, call it with the player to get the mw,
// then call the mw's setSource method
} else if (mwFactory) {
const mw = mwFactory(player);
const mw = getOrCreateFactory(player, mwFactory);
mw.setSource(assign({}, src), function(err, _src) {

View File

@ -250,6 +250,9 @@ QUnit.test('setSource is run asynchronously', function(assert) {
let acc;
middleware.setSource({
id() {
return 'vid1';
},
setTimeout: window.setTimeout
}, { src: 'foo', type: 'video/foo' }, function(_src, _acc) {
src = _src;
@ -281,6 +284,9 @@ QUnit.test('setSource selects a source based on the middleware given', function(
middleware.use('video/foo', fooFactory);
middleware.setSource({
id() {
return 'vid1';
},
setTimeout: window.setTimeout
}, {src: 'foo', type: 'video/foo'}, function(_src, _acc) {
src = _src;
@ -323,6 +329,9 @@ QUnit.test('setSource can select multiple middleware from multiple types', funct
middleware.use('video/bar', barFactory);
middleware.setSource({
id() {
return 'vid1';
},
setTimeout: window.setTimeout
}, {src: 'foo', type: 'video/foo'}, function(_src, _acc) {
src = _src;
@ -377,6 +386,9 @@ QUnit.test('setSource will select all middleware of a given type, until src chan
middleware.use('video/foo', fooFactory3);
middleware.setSource({
id() {
return 'vid1';
},
setTimeout: window.setTimeout
}, {src: 'foo', type: 'video/foo'}, function(_src, _acc) {
src = _src;
@ -409,3 +421,91 @@ QUnit.test('a middleware without a mediator method will not throw an error', fun
assert.equal(pauseCalled, 1, 'pauseCalled was called once and no error was thrown');
});
QUnit.test('a middleware factory is not called on source change', function(assert) {
let mwfactoryCalled = 0;
const mw = {
setSource(_src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
};
const fooFactory = () => {
mwfactoryCalled++;
return mw;
};
middleware.use('video/foo', fooFactory);
// set "initial" source"
middleware.setSource({
id() {
return 'vid1';
},
setTimeout: window.setTimeout
}, {src: 'foo', type: 'video/foo'}, function() {});
this.clock.tick(1);
assert.equal(mwfactoryCalled, 1, 'the factory was called once');
// "change" source
middleware.setSource({
id() {
return 'vid1';
},
setTimeout: window.setTimeout
}, {src: 'bar', type: 'video/foo'}, function() {});
this.clock.tick(1);
assert.equal(mwfactoryCalled, 1, 'the factory was not called again');
middleware.getMiddleware('video/foo').pop();
});
QUnit.test('a middleware factory is called on a new source with a new player', function(assert) {
let mwfactoryCalled = 0;
const mw = {
setSource(_src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
};
const fooFactory = () => {
mwfactoryCalled++;
return mw;
};
middleware.use('video/foo', fooFactory);
// set "initial" source with player vid1
middleware.setSource({
id() {
return 'vid1';
},
setTimeout: window.setTimeout
}, {src: 'foo', type: 'video/foo'}, function() {});
this.clock.tick(1);
assert.equal(mwfactoryCalled, 1, 'the factory was called once');
// set "initial" source with player vid2
middleware.setSource({
id() {
return 'vid2';
},
setTimeout: window.setTimeout
}, {src: 'bar', type: 'video/foo'}, function() {});
this.clock.tick(1);
assert.equal(mwfactoryCalled, 2, 'the factory was called again');
middleware.getMiddleware('video/foo').pop();
});