Dependency Injection
dash.js does not use classes. Every module follows the same closure-factory pattern, and FactoryMaker (src/core/FactoryMaker.js) acts as the dependency injection container.
Anatomy of a module
import FactoryMaker from '../core/FactoryMaker.js';
function GapController() {
const context = this.context; // the DI scope, injected by FactoryMaker
let instance, // the public API object
logger, // private state lives in closure variables
playbackController;
function setConfig(config) {
if (config.playbackController) {
playbackController = config.playbackController;
}
}
instance = {
setConfig
};
return instance;
}
GapController.__dashjs_factory_name = 'GapController';
export default FactoryMaker.getSingletonFactory(GapController);The important parts:
- Private state is held in closure variables, the public API is whatever ends up on the returned
instanceobject. __dashjs_factory_nameregisters the module under a stable name — this is what makes it overridable viaplayer.extend().- The module exports a factory, not an instance.
Contexts: per-player scoping
Every MediaPlayer().create() call creates its own context object. All "singletons" are scoped to that context:
const eventBus = EventBus(context).getInstance();Two player instances on the same page therefore get two completely independent sets of modules — two event buses, two settings objects, two scheduling pipelines. This is what makes multiple independent players per page possible.
Singleton vs. class factories
FactoryMaker.getSingletonFactory(Module)— one instance per context, retrieved withModule(context).getInstance(). Used for controllers, models and most infrastructure.FactoryMaker.getClassFactory(Module)— a new instance percreate()call:Module(context).create(config). Used where multiple instances per player exist, for example oneStreamProcessorper media type.
Overriding modules with player.extend()
Because every module is registered by name, applications can replace or extend any of them without forking dash.js:
player.extend('AbrController', MyAbrControllerFactory, true /* override */);- With
override: truethe custom factory replaces the built-in module. - With
override: falsethe custom object is merged over the built-in instance — useful to override single methods. - Inside an extended module,
this.factoryandthis.contextare injected, so built-in singletons can be looked up viathis.factory.getSingletonInstance(this.context, 'VideoModel').
Important: player.extend() must be called before player.initialize() — once the module graph is instantiated, replacing factories has no effect.
