# Video.js Admin & Developer Guide

This guide is the architectural and operational reference for Video.js — for the engineers who integrate it deeply into an application, the architects who reason about how it fits a system, and the contributors who modify or extend it. It covers the internal architecture, the complete configuration surface, the playback technology and middleware layers, security considerations, the build and test pipeline, code patterns, and a candid look at known issues and improvement areas.

For installation basics, playback control, captions, and end-user-facing usage, see the companion User Guide. This document deliberately goes deeper: it should be thorough enough that an AI or engineer could reconstruct how the library is organized and behaves.

> [!NOTE]
> This guide documents Video.js **8.23.8** (Apache-2.0). "Video.js" is a registered trademark of Brightcove, Inc. The library is a client-side browser library — there is **no server component**, no database, and no backend to operate. "Operations" here means integration, build, deployment of the static assets, and runtime behavior in the browser.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [System Architecture](#2-system-architecture)
3. [Repository Structure](#3-repository-structure)
4. [The Component Architecture](#4-the-component-architecture)
5. [The Tech (Playback Technology) Layer](#5-the-tech-playback-technology-layer)
6. [The Middleware Layer](#6-the-middleware-layer)
7. [The Plugin System](#7-the-plugin-system)
8. [Event System and Mixins](#8-event-system-and-mixins)
9. [Configuration Reference](#9-configuration-reference)
10. [The `videojs` Namespace API](#10-the-videojs-namespace-api)
11. [Player API Reference](#11-player-api-reference)
12. [Error Model](#12-error-model)
13. [Requirements and Compatibility](#13-requirements-and-compatibility)
14. [Build, Tooling, and Distribution](#14-build-tooling-and-distribution)
15. [Deployment and Integration](#15-deployment-and-integration)
16. [Security Analysis](#16-security-analysis)
17. [Performance Considerations](#17-performance-considerations)
18. [Testing Strategy](#18-testing-strategy)
19. [Code Quality and Conventions](#19-code-quality-and-conventions)
20. [Contributing](#20-contributing)
21. [Potential Improvements and Tech Debt](#21-potential-improvements-and-tech-debt)
22. [Quick Reference](#22-quick-reference)

---

## 1. Executive Summary

Video.js is a **framework**, not just a player widget. At its core is a small custom UI-component system and a pluggable playback-technology abstraction. Everything visible — the control bar, buttons, progress bar, menus — is a `Component` in a tree rooted at the `Player`. Everything that decodes media is a `Tech`. Between the player and the tech sits an optional `middleware` pipeline. Behavior is extended through `plugins`. This separation is what lets a single API and skin work identically across very different playback backends.

| Aspect | Detail |
|--------|--------|
| Project | Video.js — Web Video Player & Framework |
| Version analyzed | 8.23.8 |
| License | Apache-2.0 |
| Language | JavaScript (ES2015+, transpiled via Babel) |
| Distribution | npm package `video.js` + CDN builds |
| Module formats | CommonJS (`dist/video.cjs.js`), ES module (`dist/video.es.js`), UMD (CDN `video.min.js`) |
| Types | Bundled TypeScript definitions (`dist/types/video.d.ts`) |
| Entry point | `src/js/index.js` → `src/js/video.js` |
| Core runtime deps | `@videojs/http-streaming` (HLS/DASH), `videojs-contrib-quality-levels`, `videojs-vtt.js`, `@videojs/xhr`, `videojs-font`, plus VHS sub-dependencies |
| Has backend | No — entirely client-side |
| Started | May 2010; 900+ contributors |

**Key architectural facts an integrator must internalize:**

- The default `techOrder` is `['html5']`. Flash and IE8 support were **removed** in v8.
- HLS and DASH are handled in-process by the bundled streaming engine — no separate dependency.
- The global options object (`videojs.options`) is literally `Player.prototype.options_`; per-player options are deep-merged on top of it.
- Players are tracked globally by element `id`; failing to `dispose()` before reusing an `id` is the most common integration bug.

---

## 2. System Architecture

```text
                         Your Web Page / Application
                                    │
                       videojs(id, options, ready)
                                    │
                                    ▼
   ┌─────────────────────────────────────────────────────────────┐
   │                          Player (Component)                   │
   │                                                               │
   │   ┌──────────────┐   options merge   ┌──────────────────┐    │
   │   │ Component     │◄──────────────────│ videojs.options  │    │
   │   │ tree (UI)     │   (defaults +     │ (= Player.proto. │    │
   │   │  ControlBar,  │    user opts)     │   options_)      │    │
   │   │  buttons,     │                   └──────────────────┘    │
   │   │  menus, ...   │                                           │
   │   └──────────────┘                                           │
   │          │ events (EventTarget mixin)                         │
   │          ▼                                                    │
   │   ┌──────────────┐    middleware pipeline   ┌────────────┐    │
   │   │  Player core │◄────────────────────────►│ Middleware │    │
   │   │  (state,     │  (intercept getters/     │  chain     │    │
   │   │  playback    │   setters by source type)└────────────┘    │
   │   │  control)    │                                            │
   │   └──────────────┘                                            │
   │          │  delegates decode/playback to                      │
   │          ▼                                                    │
   │   ┌─────────────────────────────────────────────────────┐    │
   │   │                    Tech (abstraction)                 │    │
   │   │   Html5 tech ── wraps native <video>/<audio> ─────────┼──► browser
   │   │       └── source handler: @videojs/http-streaming     │    │  media
   │   │            (HLS .m3u8 / DASH .mpd via MSE)            │    │  engine
   │   └─────────────────────────────────────────────────────┘    │
   │          ▲                                                    │
   │          │ plugins (basic fn / advanced Plugin subclass)      │
   │   ┌──────────────┐                                            │
   │   │   Plugins    │                                            │
   │   └──────────────┘                                            │
   └─────────────────────────────────────────────────────────────┘
```

### Layers, top to bottom

1. **Public namespace (`videojs`)** — the factory function and the static registry/utility API (`src/js/video.js`).
2. **Player** — the root component; owns state, orchestrates the tech, exposes the playback API (`src/js/player.js`).
3. **Component tree** — the UI; every control is a `Component` subclass (`src/js/component.js`, `src/js/control-bar/**`).
4. **Middleware** — optional interceptors between player and tech, selected by source type (`src/js/tech/middleware.js`).
5. **Tech** — the playback abstraction; `Html5` is the only built-in tech in v8 (`src/js/tech/**`).
6. **Source handlers** — register with a tech to handle specific formats; the bundled streaming engine is the source handler for HLS/DASH.
7. **Tracks** — text/audio/video track lists modeled on the W3C spec (`src/js/tracks/**`).
8. **Plugins** — user-supplied extensions (`src/js/plugin.js`).
9. **Utilities** — DOM, events, time, URL, object, function, number, string, browser, logging, stylesheet helpers (`src/js/utils/**`).

---

## 3. Repository Structure

```text
video.js/
├── src/
│   ├── js/
│   │   ├── index.js            # Package entry: imports video + VHS + quality-levels
│   │   ├── video.js            # The videojs() function + static namespace API
│   │   ├── player.js           # Player class (the largest, central module)
│   │   ├── component.js        # Base Component class + registry
│   │   ├── clickable-component.js, button.js, big-play-button.js,
│   │   │                       # close-button.js, transient-button.js
│   │   ├── modal-dialog.js, error-display.js, title-bar.js,
│   │   │   loading-spinner.js, poster-image.js, resize-manager.js,
│   │   │   live-tracker.js, spatial-navigation.js
│   │   ├── event-target.js     # EventTarget base class
│   │   ├── plugin.js           # Plugin base class + registry
│   │   ├── media-error.js      # MediaError class (W3C error codes)
│   │   ├── fullscreen-api.js   # Cross-browser fullscreen mapping
│   │   ├── setup.js            # Auto-setup scanning for data-setup
│   │   ├── consts/             # errors.js (error metadata)
│   │   ├── control-bar/        # All control-bar components (LOW triage, many files)
│   │   ├── menu/, slider/, mixins/   # UI building blocks
│   │   ├── tech/               # tech.js, html5.js, middleware.js, loader.js
│   │   ├── tracks/             # text/audio/video track lists, settings, display
│   │   └── utils/              # dom, events, fn, obj, time, url, str, num,
│   │                           # browser, log, guid, promise, buffer, stylesheet…
│   ├── css/                    # Sass source for the default skin (vjs.scss, components/)
│   └── images/                 # icons.svg sprite
├── lang/                       # 50+ language JSON dictionaries (en.json is the template)
├── docs/                       # Guides, examples, legacy docs
├── test/                       # unit (QUnit/karma), api, require (browserify/webpack/node)
├── sandbox/                    # Manual test HTML pages (copied from .example files)
├── build/                      # Build scripts (minify.js, sandbox.js, translations.js, …)
├── dist/                       # Build output (generated; not in source control)
├── package.json                # Scripts, deps, version 8.23.8
├── rollup.config.js            # JS bundling config
├── postcss.config.js, tsconfig.json, .jsdoc.js
└── composer.json               # PHP/Packagist metadata (library listing only)
```

> [!NOTE]
> A triage detail worth knowing: the `src/js/control-bar/` and `src/js/tracks/` folders contain many small component files. They are individually small but collectively make up most of the visible UI. The architecturally load-bearing modules are `player.js`, `component.js`, `video.js`, `tech/`, and `plugin.js`.

---

## 4. The Component Architecture

The `Component` class (`src/js/component.js`) is the foundation of the entire UI. Understanding it is the key to extending Video.js.

### Core ideas

- **A component is a JS object paired with a DOM element.** `component.el()` returns the element; `component.createEl()` builds it.
- **Components form a tree.** A parent holds children via `addChild()`/`removeChild()`; adding a child inserts its element into the parent's content element (`contentEl()`).
- **Components are registered by name.** `Component.registerComponent(name, Class)` (exposed as `videojs.registerComponent`) adds a component to a global registry so it can be referenced by string in the `children` option.
- **Default children are declarative.** A component's `options_.children` array (or a `children` map) drives `initChildren()`, which instantiates each child. Setting a child's option to `false` skips it; `true` uses defaults.

### The class hierarchy

```text
Component
├── ClickableComponent
│   └── Button
│       ├── BigPlayButton
│       ├── CloseButton
│       └── TransientButton
│   └── PosterImage
├── ModalDialog
│   └── ErrorDisplay
├── Player
├── LiveTracker
├── LoadingSpinner
├── ResizeManager
├── TitleBar
└── (control-bar/menu/slider/tracks components…)

EventTarget
└── SpatialNavigation

MediaError   (standalone, mimics W3C MediaError)
Plugin       (base for advanced plugins)
```

`ClickableComponent` adds keyboard/tap activation and ARIA `role="button"`; `Button` specializes it into a native `<button>`. `ModalDialog` is a focus-trapping overlay (used by `ErrorDisplay` and `player.createModal()`).

### Key Component methods (for extension authors)

| Method | Purpose |
|--------|---------|
| `createEl(tag, props, attrs)` | Build the component's DOM element. Override to customize markup. |
| `buildCSSClass()` | Return the component's CSS class string. Override and append `super.buildCSSClass()`. |
| `addChild(child, opts, index)` / `removeChild(child)` | Manage children. |
| `getChild(name)` / `getChildById(id)` / `getDescendant(...names)` | Traverse the tree. |
| `on/off/one/any/trigger` | Events (from the evented mixin). |
| `show()/hide()/addClass/removeClass/toggleClass` | Visibility and classes. |
| `dimensions/width/height/currentWidth/currentHeight` | Sizing. |
| `setTimeout/setInterval/requestAnimationFrame` (+ `clear*`) | Timers auto-cleaned on `dispose()`. |
| `ready(fn)` / `triggerReady()` | Async readiness. |
| `localize(string, tokens, default)` | Translate UI text. |
| `dispose()` | Tear down the component and all children (recursively). |

> [!IMPORTANT]
> Always use the component's own `setTimeout`/`setInterval`/`requestAnimationFrame` wrappers rather than the `window` equivalents. They are tracked in `Set`s and cleared automatically on `dispose()`, preventing leaks and callbacks firing against disposed components. Using `window.clearTimeout` on an id from `Component#setTimeout` will *not* clean up its dispose listener.

### Registering a custom component

```js
const Component = videojs.getComponent('Component');

class TitleOverlay extends Component {
  createEl() {
    return videojs.dom.createEl('div', {
      className: 'vjs-title-overlay',
      textContent: this.options_.text || ''
    });
  }
}

videojs.registerComponent('TitleOverlay', TitleOverlay);

const player = videojs('my-player');
player.addChild('TitleOverlay', { text: 'Now Playing' });
```

> [!WARNING]
> A `Tech` must **not** be registered with `registerComponent` — use `Tech.registerTech()` (or `videojs.registerTech()`). Registering a tech as a component logs a warning and is rejected; `registerComponent` throws `Illegal component, "<name>"; ...` for non-`Component` classes. The `Player` component cannot be re-registered after any player has been created.

---

## 5. The Tech (Playback Technology) Layer

A **Tech** is the abstraction that actually plays media. It presents a uniform interface (`play`, `pause`, `currentTime`, `volume`, `duration`, `buffered`, `canPlayType`, `supportsFullScreen`, …) and emits a uniform set of events (`loadstart`, `play`, `pause`, `playing`, `ended`, `volumechange`, `durationchange`, `error`, plus optional `timeupdate`, `progress`, fullscreen events). The player talks only to the tech interface, never to the browser's media element directly.

### The Html5 tech

In Video.js 8 the only built-in tech is `Html5` (`src/js/tech/html5.js`), which wraps the native `<video>`/`<audio>` element. The default `techOrder` is therefore `['html5']`.

> [!NOTE]
> Flash, Silverlight, and other plugin-based techs from earlier major versions are gone. Documentation, examples, or `techOrder` arrays referencing a `flash` tech or `.swf` files are obsolete for v8.

### Source handlers and adaptive streaming

A tech can register **source handlers** that take over playback for specific formats. The bundled `@videojs/http-streaming` (VHS) registers as a source handler on the Html5 tech and plays HLS (`.m3u8`) and MPEG-DASH (`.mpd`) using the browser's Media Source Extensions (MSE). It is imported by `src/js/index.js`, so the standard build always includes it. `videojs-contrib-quality-levels` is also imported, giving a `player.qualityLevels()` API surface for representing available renditions.

### Tech-first vs source-first selection

When choosing a `(source, tech)` pair, Video.js defaults to **tech-first**: for each tech in `techOrder`, it tries every source. With only one tech in v8 this simply means "the first source the HTML5 tech (with its source handlers) can play wins." Provide sources in priority order and always include `type`.

### Configuring the Html5 tech

Tech options are passed under the `html5` key:

```js
videojs('my-player', {
  html5: {
    nativeTextTracks: false,    // force emulated text tracks
    nativeAudioTracks: false,
    nativeVideoTracks: false,
    vhs: {                      // options forwarded to @videojs/http-streaming
      overrideNative: true
    }
  }
});
```

> [!TIP]
> Forcing emulated text tracks (`nativeTextTracks: false`) gives consistent caption styling and behavior across browsers, at the cost of relying on `videojs-vtt.js` for parsing/rendering. Native tracks behave differently per browser (some allow multiple simultaneous tracks).

---

## 6. The Middleware Layer

Middleware (`src/js/tech/middleware.js`) sits between the player and the tech and can intercept method calls based on the **source type**. This is an advanced extension point used to adapt or augment playback for particular formats without modifying the player or tech.

```js
videojs.use('video/foo', function(player) {
  return {
    setSource(srcObj, next) {
      // transform the source, then continue the chain
      next(null, srcObj);
    },
    currentTime(ct) {
      return ct;       // intercept getter results
    },
    setCurrentTime(ct) {
      return ct;       // intercept setter values
    }
  };
});
```

A middleware can short-circuit the chain by returning `videojs.middleware.TERMINATOR`. Middleware is keyed by MIME type (use `'*'` to match all sources). This mechanism powers integrations like ad insertion and DRM coordination that need to wrap time/seek/source semantics.

---

## 7. The Plugin System

Plugins (`src/js/plugin.js`) are the primary extension mechanism for application-level behavior. There are two flavors.

### Basic plugins

A function registered by name. When called on a player, it runs with `this` bound to the player and is marked active. Setup events (`beforepluginsetup`, `pluginsetup`, and `:name` variants) fire around it.

```js
videojs.registerPlugin('myBasic', function(options) {
  this.addClass('has-my-basic');
});
```

### Advanced plugins

A subclass of the base `Plugin` class. It gets the evented and stateful mixins, a scoped logger (`this.log`), a `player` reference, default state, and automatic `dispose()` wiring on player teardown. The base class throws if instantiated directly (`Plugin must be sub-classed; not directly instantiated.`).

```js
const Plugin = videojs.getPlugin('plugin');

class MyAdvanced extends Plugin {
  constructor(player, options) {
    super(player);
    this.state = { count: 0 };
    this.on(player, 'play', () => this.setState({ count: this.state.count + 1 }));
  }
}

videojs.registerPlugin('myAdvanced', MyAdvanced);
```

### Registry and lifecycle

| API | Purpose |
|-----|---------|
| `videojs.registerPlugin(name, plugin)` | Register (basic fn or `Plugin` subclass). Throws on illegal names or re-registering core methods. |
| `videojs.deregisterPlugin(name)` | Remove from the registry (base `plugin` cannot be de-registered). |
| `videojs.getPlugin(name)` / `getPlugins()` / `getPluginVersion(name)` | Inspect the registry. |
| `player.hasPlugin(name)` | Whether registered. |
| `player.usingPlugin(name)` | Whether active on this player. |

> [!CAUTION]
> Plugin names become properties on the player prototype. A name that collides with an existing player method/property is rejected (`Illegal plugin name, "<name>", already exists`). Namespace plugin names defensively. The legacy `videojs.plugin()` is deprecated in favor of `videojs.registerPlugin()`.

---

## 8. Event System and Mixins

### EventTarget

`EventTarget` (`src/js/event-target.js`) implements a DOM-like event interface — `on`/`off`/`one`/`any`/`trigger` plus standard aliases `addEventListener`/`removeEventListener`/`dispatchEvent`. When a triggered event name is in `allowedEvents_`, the corresponding `on<Event>` handler is also invoked.

### Mixins

Two mixins are applied to components and plugins:

- **`evented`** (`src/js/mixins/evented.js`) — grants event methods and an event bus (using the component's `el_` when available, so events bubble through the DOM).
- **`stateful`** (`src/js/mixins/stateful.js`) — grants a `state` object and `setState()` that triggers `statechanged` events.

The `Component` constructor mixes these in conditionally (`options.evented !== false`). It also binds `handleLanguagechange` and listens for the player's `languagechange` event so UI text re-localizes automatically.

### Tech event re-triggering

The player re-emits a fixed set of tech events as its own (`TECH_EVENTS_RETRIGGER`: `progress`, `abort`, `suspend`, `emptied`, `stalled`, `loadedmetadata`, `loadeddata`, `timeupdate`, `resize`, `volumechange`, `texttrackchange`). Certain events are queued when the playback rate is zero (`canplay`, `canplaythrough`, `playing`, `seeked`). This is why you can listen on the **player** for media events regardless of which tech produced them.

---

## 9. Configuration Reference

The complete option surface is documented in the User Guide's [Player Options Reference](videojs__video.js__UserGuide.md). The table below is the authoritative list of **defaults** as defined in `Player.prototype.options_` (video.js 8.23.8), which is what `videojs.options` exposes.

| Option | Default |
|--------|---------|
| `techOrder` | `['html5']` (from `Tech.defaultTechOrder_`) |
| `html5` | `{}` |
| `enableSourceset` | `true` |
| `inactivityTimeout` | `2000` |
| `playbackRates` | `[]` |
| `liveui` | `false` |
| `children` | `['mediaLoader','posterImage','titleBar','textTrackDisplay','loadingSpinner','bigPlayButton','liveTracker','controlBar','errorDisplay','textTrackSettings','resizeManager']` |
| `language` | browser preference → `'en'` |
| `languages` | `{}` |
| `notSupportedMessage` | `'No compatible source was found for this media.'` |
| `normalizeAutoplay` | `false` |
| `fullscreen` | `{ options: { navigationUI: 'hide' } }` |
| `breakpoints` | `{}` |
| `responsive` | `false` |
| `audioOnlyMode` | `false` |
| `audioPosterMode` | `false` |
| `spatialNavigation` | `{ enabled: false, horizontalSeek: false }` |
| `enableSmoothSeeking` | `false` |
| `disableSeekWhileScrubbingOnMobile` | `false` |
| `disableSeekWhileScrubbingOnSTV` | `false` |

### Default breakpoints

| Name | Max width (px) | Class |
|------|----------------|-------|
| `tiny` | 210 | `vjs-layout-tiny` |
| `xsmall` | 320 | `vjs-layout-x-small` |
| `small` | 425 | `vjs-layout-small` |
| `medium` | 768 | `vjs-layout-medium` |
| `large` | 1440 | `vjs-layout-large` |
| `xlarge` | 2560 | `vjs-layout-x-large` |
| `huge` | `Infinity` | `vjs-layout-huge` |

### Global configuration flags

Set these on `window` **before** Video.js loads:

| Flag | Effect |
|------|--------|
| `VIDEOJS_NO_DYNAMIC_STYLE = true` | Skip injecting the default-dimensions and per-player dimension `<style>` elements. Width/height are then applied directly to the media element. |
| `VIDEOJS_NO_BASE_THEME = true` | Skip injecting base skin styles (you must include them manually). |
| `HELP_IMPROVE_VIDEOJS = false` | Opt out of the small anonymous analytics pixel in CDN builds. |

> [!NOTE]
> The default injected dimensions are `300×150` for `.video-js`, and `vjs-fluid` uses `padding-top: 56.25%` (16:9). These constants live in `src/js/video.js`.

---

## 10. The `videojs` Namespace API

`videojs` is both the player factory and the library namespace (`src/js/video.js`). Below is the complete static surface.

### Player lifecycle

| Member | Description |
|--------|-------------|
| `videojs(id, options, ready)` | Create or fetch a player. |
| `videojs.getPlayer(id)` | Fetch an existing player without creating one. |
| `videojs.getPlayers()` / `videojs.players` | Object of players keyed by id. |
| `videojs.getAllPlayers()` | Array of all (non-disposed) players. |

### Registries

| Member | Description |
|--------|-------------|
| `videojs.registerComponent(name, comp)` / `videojs.getComponent(name)` | Component registry. |
| `videojs.registerTech(name, tech)` / `videojs.getTech(name)` | Tech registry. |
| `videojs.registerPlugin(name, plugin)` / `videojs.deregisterPlugin(name)` | Plugin registry. |
| `videojs.getPlugin(name)` / `videojs.getPlugins()` / `videojs.getPluginVersion(name)` | Plugin lookups. |
| `videojs.use(type, middleware)` | Register middleware; `videojs.middleware.TERMINATOR` ends a chain. |

### Track classes

`videojs.TrackList`, `videojs.TextTrack`, `videojs.TextTrackList`, `videojs.AudioTrack`, `videojs.AudioTrackList`, `videojs.VideoTrack`, `videojs.VideoTrackList`.

### Hooks, events, and globals

| Member | Description |
|--------|-------------|
| `videojs.hook(name, fn)` / `videojs.hookOnce` / `videojs.removeHook` / `videojs.hooks(name)` | Setup hooks: `beforesetup` (transform options) and `setup` (post-create). |
| `videojs.on/one/off/any/trigger` | Standalone event helpers. |
| `videojs.options` | The global default options object (= `Player.prototype.options_`). |
| `videojs.VERSION` | The library version string. |
| `videojs.addLanguage(code, data)` | Register a global translation dictionary. |
| `videojs.log` / `videojs.createLogger(name)` | Logging (supports `log.level('debug')`). |
| `videojs.EventTarget` / `videojs.Error` | The `EventTarget` class / error metadata registry. |
| `videojs.xhr` | Cross-browser XHR wrapper (`@videojs/xhr`). |

### Utility modules (non-deprecated)

`videojs.dom`, `videojs.obj`, `videojs.fn`, `videojs.num`, `videojs.str`, `videojs.url`, `videojs.time`, `videojs.browser`.

> [!IMPORTANT]
> Many older top-level helpers are deprecated and slated for removal in 9.0 — see the [Deprecated and Removed APIs table in the User Guide](videojs__video.js__UserGuide.md#deprecated-and-removed-apis). Prefer the namespaced equivalents (`videojs.obj.merge`, `videojs.time.createTimeRanges`, `videojs.url.parseUrl`, `videojs.dom.*`, native `Function.prototype.bind`).

### Setup hooks example

```js
// Inject a default option into every player before it's created
videojs.hook('beforesetup', (videoEl, options) => {
  return videojs.obj.merge(options, { fluid: true });
});

// Run code on every player after creation
videojs.hook('setup', (player) => {
  player.log('player created:', player.id());
});
```

---

## 11. Player API Reference

The `Player` class (`src/js/player.js`) extends `Component`. Its public methods follow the HTML5 media API, with getter/setter semantics (call with no argument to get, with an argument to set). The complete public method set in 8.23.8:

### Playback & time
`play()`, `pause()`, `paused()`, `played()`, `ended()`, `scrubbing()`, `currentTime()`, `duration()`, `remainingTime()`, `remainingTimeDisplay()`, `buffered()`, `bufferedPercent()`, `bufferedEnd()`, `seekable()`, `seeking()`, `load()`, `reset()`.

### Volume
`volume()`, `muted()`, `defaultMuted()`.

### Rate
`playbackRate()`, `defaultPlaybackRate()`, `playbackRates()`.

### Source & format
`src()`, `currentSource()`, `currentSources()`, `currentSrc()`, `currentType()`, `canPlayType()`, `selectSource()`, `addSourceElement()`, `removeSourceElement()`, `preload()`, `autoplay()`, `loop()`, `poster()`, `playsinline()`, `crossOrigin()`.

### Sizing & layout
`width()`, `height()`, `dimension()`, `videoWidth()`, `videoHeight()`, `fluid()`, `fill()`, `aspectRatio()`, `responsive()`, `breakpoints()`, `currentBreakpoint()`, `currentBreakpointClass()`.

### Fullscreen & PiP
`requestFullscreen()`, `exitFullscreen()`, `isFullscreen()`, `supportsFullScreen()`, `enterFullWindow()`, `exitFullWindow()`, `fullWindowOnEscKey()`, `requestPictureInPicture()`, `exitPictureInPicture()`, `isInPictureInPicture()`, `disablePictureInPicture()`.

### State, controls & errors
`controls()`, `usingNativeControls()`, `error()`, `readyState()`, `networkState()`, `userActive()`, `reportUserActivity()`, `hasStarted()`, `isAudio()`, `audioOnlyMode()`, `audioPosterMode()`, `debug()`, `getCache()`, `getVideoPlaybackQuality()`.

### Tracks
`addTextTrack()`, `addRemoteTextTrack()`, `removeRemoteTextTrack()` (plus `textTracks()`, `remoteTextTracks()`, `remoteTextTrackEls()`, `audioTracks()`, `videoTracks()` from track support).

### Media metadata, language & misc
`tech()`, `version()`, `language()`, `languages()`, `toJSON()`, `createModal()`, `loadMedia()`, `getMedia()`, `hasPlugin()`, `usingPlugin()`, `dispose()`.

> [!TIP]
> `player.loadMedia(media, ready)` / `player.getMedia()` provide a structured way to set source, poster, text tracks, and metadata together — convenient for playlist-style integrations. `player.toJSON()` serializes the player's option/state snapshot.

---

## 12. Error Model

Video.js uses a `MediaError` class (`src/js/media-error.js`) that mirrors the W3C `MediaError`. `player.error()` returns one of these. The standard codes — **order is fixed by the W3C spec and must not change**:

| Code | Constant | Default message |
|------|----------|-----------------|
| `0` | `MEDIA_ERR_CUSTOM` | *(custom message)* |
| `1` | `MEDIA_ERR_ABORTED` | You aborted the media playback |
| `2` | `MEDIA_ERR_NETWORK` | A network error caused the media download to fail part-way. |
| `3` | `MEDIA_ERR_DECODE` | The media playback was aborted due to a corruption problem or because the media used features your browser did not support. |
| `4` | `MEDIA_ERR_SRC_NOT_SUPPORTED` | The media could not be loaded, either because the server or network failed or because the format is not supported. |
| `5` | `MEDIA_ERR_ENCRYPTED` | The media is encrypted and we do not have the keys to decrypt it. |

A `MediaError` also carries optional `message`, `status` (e.g. HTTP status from a plugin), and `metadata` (an object with an `errorType` aligned to `videojs.Error`) for richer diagnostics. The `ErrorDisplay` component (a `ModalDialog`) renders the localized message over the player when an `error` event fires.

### Internal error/throw points worth knowing

| Message | Source | Meaning |
|---------|--------|---------|
| `No techOrder specified. Did you overwrite videojs.options...` | `player.js` | `techOrder` was emptied — usually from replacing `videojs.options` wholesale instead of overriding properties. |
| `Improper value supplied for aspect ratio...` | `player.js` | `aspectRatio` not in `width:height` form. |
| `The element or ID supplied is not valid. (videojs)` | `video.js` | `videojs()` given a non-element/missing id. |
| `Component <name> does not exist` | `component.js` | `addChild` referenced an unregistered component. |
| `Illegal component name/component...` | `component.js` | Bad `registerComponent` usage (e.g. registering a tech). |
| `Plugin must be sub-classed...` / `Illegal plugin name...` | `plugin.js` | Misused plugin base class / colliding plugin name. |

> [!TIP]
> When you intentionally customize global defaults, **mutate properties** of `videojs.options` (e.g. `videojs.options.autoplay = true`) rather than reassigning `videojs.options = {...}`, which discards `techOrder` and other required defaults and triggers the "No techOrder specified" error.

---

## 13. Requirements and Compatibility

### Runtime (the browser)

- **Modern evergreen browsers** (Chrome, Firefox, Safari, Edge) on desktop and mobile, plus many web-based smart-TV browsers.
- **HTML5 media** support is required (the only built-in tech wraps `<video>`/`<audio>`).
- **Media Source Extensions (MSE)** are required for HLS/DASH via the bundled streaming engine; Safari can also play HLS natively.
- The **Fullscreen API** and **Picture-in-Picture API** are used when available, with a CSS "full window" fallback (`preferFullWindow`).
- IE8 and Flash are **not** supported in v8.

### Build/development (contributors)

- **Node.js** and **npm** (the project uses npm scripts and `npm-run-all`).
- Toolchain: **Rollup** (JS bundling), **Babel** (`@babel/preset-env` + runtime transform), **Sass** + **PostCSS**/Autoprefixer (CSS), **TypeScript** (`tsc` for type emit), **JSDoc** (API docs), **Karma** + **QUnit** + **Sinon** (tests), **vjsstandard** (lint), **uglify-js**/**clean-css** (minify), **husky** + **lint-staged** (pre-commit).

---

## 14. Build, Tooling, and Distribution

The build is orchestrated entirely through npm scripts in `package.json`. The headline targets:

| Script | What it does |
|--------|--------------|
| `npm run build` | Full production build: `build-dev`, `minify`, `copy`, `zip`. |
| `npm run build-dev` | `clean`, lint errors, then build JS, CSS, languages, tests, and types. |
| `npm run build:js` | Bundle JS with Rollup (`rollup -c`). |
| `npm run build:css` | Compile Sass → CSS (default + CDN variants) and run PostCSS. |
| `npm run build:lang` | Compile `lang/*.json` into distributable language scripts (`vjslang`). |
| `npm run build:types` | Emit TypeScript definitions (`tsc`). |
| `npm run minify` | Minify JS (`build/minify.js`) and CSS (`clean-css`). |
| `npm run lint` | Lint with `vjsstandard`. |
| `npm test` | `build-dev` (pretest) then run unit (`karma`), node-require, and a11y tests in parallel. |
| `npm run docs:api` | Generate API docs with JSDoc. |
| `npm start` | Dev server: build the sandbox, then watch + serve via Karma. |
| `npm run watch` | Watch and rebuild JS, CSS, types, and languages. |

### Distribution artifacts

The `dist/` output (declared in `package.json`) includes:

- `dist/video.cjs.js` — CommonJS (`main`).
- `dist/video.es.js` — ES module (`module`).
- `dist/video.min.js` — minified UMD for the CDN/`<script>` usage.
- `dist/video-js.css` / `dist/video-js.min.css` — default skin.
- `dist/alt/video-js-cdn.css` — CDN skin variant.
- `dist/lang/*.json` — language files.
- `dist/font/` — icon fonts (from `videojs-font`).
- `dist/types/video.d.ts` — TypeScript definitions.

> [!NOTE]
> The single environment variable observed in the build is `CI_TEST_TYPE` (read in `rollup.config.js`), which controls coverage instrumentation during CI test runs. There are **no runtime environment variables** — this is a browser library configured entirely through options and global `window` flags.

---

## 15. Deployment and Integration

"Deployment" for Video.js means shipping its static assets and initializing players in your application. There is nothing to run server-side.

### Deployment checklist

- [ ] Serve a **version-pinned** CSS + JS pair (matching versions).
- [ ] Decide delivery: CDN (simplest), self-hosted `dist/`, or bundled via npm.
- [ ] If bundling, import both `video.js` and its CSS; tree-shaking is limited because the entry pulls in VHS and quality-levels.
- [ ] Ensure media and track servers send correct **MIME types** and **CORS headers** if cross-origin.
- [ ] Initialize players after their elements are in the DOM; **dispose** on teardown.
- [ ] Set the appropriate **Content-Security-Policy** (see [Security](#16-security-analysis)).

### Framework integration patterns

- **React/Vue/Angular/Svelte:** create the player in the mount/`useEffect` hook against a `ref`, and `dispose()` in the unmount/cleanup. Prefer the `<video-js>` element to avoid a flash of native controls. Consider `VIDEOJS_NO_DYNAMIC_STYLE` if the framework manages `<head>`.
- **Lazy loading:** dynamically `import('video.js')` to keep it out of the initial bundle.
- **Multiple players:** unique ids per element; the global registry tracks them.

### Rollback / version management

Because the library is a static asset, rollback is simply pointing back to a previously pinned version's CSS+JS URLs (or reverting the npm dependency). Keep the version pin in source control so changes are reviewable.

---

## 16. Security Analysis

Video.js runs untrusted-by-nature content (media and text tracks, often from third parties) inside the browser. The relevant surfaces:

### Cross-origin content

The `crossorigin` attribute (`anonymous` / `use-credentials`) governs CORS for media and tracks. Cross-origin text tracks require CORS on the server **and** the attribute; browsers commonly extend `crossorigin` to the media source itself, so media files also need CORS headers. Use `anonymous` unless credentials are genuinely required.

### Content Security Policy (CSP)

Video.js injects `<style>` elements at runtime for default and per-player dimensions, and historically uses some inline style attributes. This can conflict with strict CSP:

- To avoid runtime-injected `<style>` elements, set `window.VIDEOJS_NO_DYNAMIC_STYLE = true` (and size players yourself) and/or `window.VIDEOJS_NO_BASE_THEME = true` (and include the skin manually).
- CSP-friendliness around runtime inline style **attributes** is an actively tracked area (community issues request `style-src-attr 'none'` compatibility — see [Potential Improvements](#21-potential-improvements-and-tech-debt)).

> [!WARNING]
> If you enforce a strict `style-src` (no `'unsafe-inline'`), test thoroughly. You may need the no-dynamic-style/no-base-theme flags plus a nonce/hash strategy for any remaining injected styles, and you should track upstream CSP fixes.

### Text track parsing

Emulated text tracks are parsed by `videojs-vtt.js`. Adaptive-streaming manifests (DASH) are parsed by VHS's `mpd-parser`, which historically pulled in an XML parser (`@xmldom/xmldom`) — a dependency where vulnerabilities have been reported in the ecosystem. Keep Video.js and its streaming dependencies up to date and run `npm audit`.

### Trust model

- Video.js executes no remote code itself; it plays media and renders track text.
- Plugins run with full access to the player and page — **only load plugins you trust**, the same as any third-party script.
- The CDN build includes a small (~1% sampled) anonymous analytics pixel; disable it with `window.HELP_IMPROVE_VIDEOJS = false` if your policy requires.

### Dependency hygiene

Run `npm audit` regularly. The streaming stack (`@videojs/http-streaming`, `m3u8-parser`, `mpd-parser`, `mux.js`, `aes-decrypter`) is the most security-relevant dependency cluster because it parses untrusted manifests and segments.

---

## 17. Performance Considerations

- **`preload`** — use `'metadata'` or `'none'` for pages with many players to avoid eager downloads; `'auto'` is honored only when the browser agrees (mobile often ignores it).
- **`inactivityTimeout`** — controls auto-hide of the control bar; the player polls user activity. `0` disables auto-hide.
- **Timers** — components batch timer cleanup via `Set`s and a single dispose listener (rather than per-timer listeners) for efficiency.
- **Animation frames** — use the named-rAF API (`requestNamedAnimationFrame`) to coalesce repeated UI updates (e.g. progress bar) into a single queued frame.
- **Disposal** — always `dispose()` unused players; orphaned players keep timers, listeners, and DOM alive. This is both a leak and a correctness issue with streaming techs.
- **Bundle size** — the standard build includes the streaming engine and quality-levels. If you only ever play progressive MP4, a custom build excluding VHS can reduce size (advanced; requires building from source).
- **`enableSmoothSeeking`** — improves perceived scrubbing but increases update frequency; the `disableSeekWhileScrubbingOnMobile`/`OnSTV` options reduce work on constrained devices.

---

## 18. Testing Strategy

The project has a substantial test suite under `test/`:

| Area | Detail |
|------|--------|
| Unit tests | QUnit specs run via Karma (`test/unit/**`), covering components, the player, tracks, plugins, utils, and tech. |
| API tests | `test/api/` validates the public API shape. |
| Require tests | `test/require/` verifies the package loads correctly under Node (`node.js`), Browserify, and Webpack. |
| Accessibility | `test:a11y` (`build/test-a11y.js`) runs `access-sniff` over a sandbox page. |
| Coverage | Istanbul via `rollup-plugin-istanbul`; printed when `CI_TEST_TYPE=coverage`. |

The unit tests double as living documentation: `test/unit/component.test.js`, `player.test.js`, `plugin-advanced.test.js`, and `plugin-static.test.js` demonstrate subclassing components/techs/plugins and the expected lifecycle. Run the full suite with `npm test` (which first does a dev build), or use `npm start` for an interactive Karma watch server.

> [!TIP]
> When writing a custom component, tech, or plugin, mirror the patterns in the corresponding `test/unit/*.test.js` file — they exercise registration, the constructor signature `(player, options)`, and disposal, which are the seams most likely to break.

---

## 19. Code Quality and Conventions

- **Lint standard:** `vjsstandard` (a `standard`-derived ruleset). `npm run lint` checks; the pre-commit hook (`husky` + `lint-staged`) auto-fixes staged `.js` files and formats Markdown via `remark`.
- **Language level:** authored in modern JS (classes, modules, arrow functions, `Set`/`Map`), transpiled by Babel with `@babel/preset-env` to broad browser support.
- **Globals via `global`:** the `global` package is used to access `window`/`document` (`import window from 'global/window'`) so the code is safe to load in non-browser/SSR contexts without throwing at import time.
- **Registration pattern:** nearly every module ends with `Component.registerComponent('Name', Name)` (or `Tech.registerTech` / `registerPlugin`), wiring itself into the appropriate registry as a side effect of import. This is why `src/js/index.js` simply imports modules to "activate" them.
- **JSDoc-first:** the codebase is heavily annotated with JSDoc, used to generate the API docs and inform the emitted TypeScript types.
- **Getter/setter methods:** the consistent `foo()` get / `foo(value)` set convention pervades the Player and Component APIs.
- **Defensive DOM/ARIA:** components set ARIA roles/labels and manage focus (e.g. `ModalDialog` focus trapping, `ClickableComponent` keyboard activation) as first-class concerns.

---

## 20. Contributing

- **License & DCO:** contributions are under Apache-2.0. There is **no CLA**; by submitting a PR you agree to the [Developer's Certificate of Origin 1.1](https://github.com/videojs/admin/blob/main/CONTRIBUTING.md#developers-certificate-of-origin-11).
- **Governance:** project decisions are overseen by the Video.js Technical Steering Committee (TSC).
- **Code of Conduct:** participation is governed by the project's Contributor Code of Conduct.
- **Workflow:** fork, branch, run `npm install`, develop with `npm start`, ensure `npm run lint` and `npm test` pass, and open a PR. Commit messages follow the conventional-changelog `videojs` preset (used to auto-generate `CHANGELOG.md`).
- **Translations:** add or update a `lang/XX.json` file using `lang/en.json` as the template; the build compiles these into distributable language scripts.
- **Docs:** Markdown is linted/validated with `remark` (`docs:lint`); API docs come from JSDoc (`docs:api`).

> [!NOTE]
> A major version is on the horizon: the README announces "Big changes coming in Video.js 10, early 2026." Expect the v9.0 removals (the deprecated helpers in this guide) to land before or alongside it. Plan migrations off deprecated APIs now.

---

## 21. Potential Improvements and Tech Debt

Observations drawn from the source and the project's own issue tracker:

- **CSP friendliness.** Runtime inline style attributes and injected `<style>` elements complicate strict CSP deployments. Open community issues request support for `style-src-attr 'none'` by avoiding runtime inline style attributes. Until resolved, strict-CSP integrators must rely on the no-dynamic-style/no-base-theme flags and careful testing.
- **Deprecated API removal (v9.0).** A dozen top-level helpers are deprecated (`mergeOptions`, `bind`, time/url helpers, DOM aliases, `videojs.plugin`). These remain as compatibility shims and add surface area; their removal in 9.0 will simplify the namespace but requires consumer migration.
- **Font icons → SVG.** The icon font is still the default; SVG icons are behind `experimentalSvgIcons`. Source comments note the intent to remove font icons entirely in a future major (v9), which will remove a class of icon-rendering and CSP edge cases.
- **Null-safety hardening.** The tracker reports several defensive-coding gaps (e.g. fullscreen calls after the tech is unloaded, slider element access without null checks, `copyStyleSheetsToWindow()` when `document.head` is null, `updateSourceCaches_()` assuming source shape). These are robustness improvements rather than functional breaks.
- **Event-listener edge case.** A reported bug where `one()` with multiple event types removes all listeners on the first trigger indicates a corner of the event system worth auditing for multi-type registrations.
- **Streaming dependency surveillance.** The DASH/HLS parsers (notably the XML parser used for manifests) sit on a recurring vulnerability path in the ecosystem; keeping the streaming stack patched is ongoing maintenance, not a one-time fix.
- **Accessibility refinements.** Open issues note screen-reader announcements for the progress slider and an invalid-markup case for `menuitemradio` — areas where the otherwise strong a11y story can be tightened.

> [!IMPORTANT]
> None of the above block production use. Video.js is mature, widely deployed (billions of monthly CDN end-users), and actively maintained. Treat these as the backlog you'd inherit if you forked or deeply customized the library.

---

## 22. Quick Reference

### Initialize / fetch / dispose

```js
const player = videojs('my-player', { controls: true, fluid: true });
const same   = videojs.getPlayer('my-player');   // never creates
player.dispose();                                 // always on teardown
```

### Most-used options

```js
{
  controls: true,
  autoplay: 'muted',          // browser-safe autoplay
  preload: 'auto',            // 'auto' | 'metadata' | 'none'
  fluid: true,                // or fill / aspectRatio: '16:9'
  responsive: true,
  playbackRates: [0.5, 1, 1.5, 2],
  liveui: true,               // for live streams
  language: 'en',
  html5: { nativeTextTracks: false }
}
```

### Source (incl. HLS/DASH — no extra packages)

```js
player.src({ src: '/stream/index.m3u8', type: 'application/x-mpegURL' });
player.src({ src: '/stream/manifest.mpd', type: 'application/dash+xml' });
```

### Registries

```js
videojs.registerComponent('Name', ComponentClass);
videojs.registerTech('Name', TechClass);
videojs.registerPlugin('name', pluginFnOrClass);
videojs.use('video/type', middlewareFactory);
videojs.addLanguage('es', { Play: 'Reproducción' });
```

### Global flags (set before load)

```js
window.VIDEOJS_NO_DYNAMIC_STYLE = true;   // strict CSP / framework-managed head
window.VIDEOJS_NO_BASE_THEME    = true;   // supply skin manually
window.HELP_IMPROVE_VIDEOJS     = false;  // opt out of CDN analytics
```

### Key facts

| | |
|--|--|
| Version | 8.23.8 (Apache-2.0) |
| Default `techOrder` | `['html5']` (no Flash) |
| HLS/DASH | bundled (`@videojs/http-streaming`) |
| Entry | `src/js/index.js` → `src/js/video.js` |
| Player tracking | global, by element `id` |
| Next major | Video.js 10 (announced, early 2026) |

---

_Generated June 2026 from videojs/video.js at 69ea0e0. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
