Skip to content

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 ​

js
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 instance object.
  • __dashjs_factory_name registers the module under a stable name — this is what makes it overridable via player.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:

js
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 with Module(context).getInstance(). Used for controllers, models and most infrastructure.
  • FactoryMaker.getClassFactory(Module) — a new instance per create() call: Module(context).create(config). Used where multiple instances per player exist, for example one StreamProcessor per 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:

js
player.extend('AbrController', MyAbrControllerFactory, true /* override */);
  • With override: true the custom factory replaces the built-in module.
  • With override: false the custom object is merged over the built-in instance — useful to override single methods.
  • Inside an extended module, this.factory and this.context are injected, so built-in singletons can be looked up via this.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.