Videojs is a widely used video player with rich default functions and high scalability. It supports HLS and DASH playback by default. There are also many third-party open source plug-ins, such as those supporting FLV playback.

The source analysis of this paper is based on Tag V7.12.0 of video.js source repository 2021-4-7.

Videojs directory description

The videoJS source code root directory has build, docs, lang, sandbox, SRC, test directories. Build is a package related script, docs is a document, lang is multilingual, sandbox is a variety of Demo, SRC is the main source, test is a unit test, dist is a package generated directory.

The main focus is of course the SRC source directory. It should be noted that the lang multilingual is not packaged into JS, and the lang file exists separately in the dist directory generated by the package.

Source directory SRC

In the SRC source directory, code and styles are stored separately, in the CSS and JS directories. Videojs uses SCSS writing style, in components, almost every component has a corresponding style file. Another open source player, Plyr, has a similar structure.

Let’s first look at the entry file index.js, the code is as follows:

import videojs from './video';
import '@videojs/http-streaming';
export default videojs;
Copy the code

Index.js simply introduces the video.js file and the @Videojs/HTTP-streaming library, which supports HLS and DASH playback. This library is decouplable from the VideoJS source code. The actual entry file is video.js.

If we open the root directory rollup.config.js, we can see that there is a package configuration in the root directory, which is to generate video.core. That is, the VideoJS core without @VideoJS/HTTP-streaming.

Real entry video.js

When we use VideoJS in our business, we basically use the videoJS method it provides, pass in some parameters, and get a Player object, as shown below.

this.player = videojs(this.videoNode, {
    controls: true.autoplay: true.errorDisplay: false.controlBar: controlBar,
    techOrder: ['html5'].html5: {
      hls: {
        withCredentials: false.overrideNative: !videojs.browser.IS_SAFARI,
        }
    },
}
Copy the code

The VideoJS method is the default export of Video.js. As you can see from the screenshot below, there are more than 500 lines of video.js code. In addition to the definition of videoJS methods, Videojs is also treated as an object, adding many properties and methods.

videojs.players = Player.players;
videojs.getComponent = Component.getComponent;
// ...
videojs.extend = extend;
videojs.mergeOptions = mergeOptions;
videojs.bind = Fn.bind;
videojs.registerPlugin = Plugin.registerPlugin;
videojs.deregisterPlugin = Plugin.deregisterPlugin;
Copy the code

Component gets the PlayerComponent class, instantiates the PlayerComponent object player with the parameters passed in, and returns it.

  const PlayerComponent = Component.getComponent('Player');
  player = new PlayerComponent(el, options, ready);
  return player;
Copy the code

Component.getcomponent is defined in component.js, and PlayerComponent is defined in player.js.

Component. js and Player. js are the two most important files in VideoJS.

Component base class Component.js

Component.js defines the Component class, which is the base class for all components.

The two most important static methods in Component are registerComponent and getComponent, which register and get components, respectively.

This registration mechanism is a good way to decouple the component from the string key. After the component is registered, it can be retrieved from the string anywhere without importing the corresponding file.

In video.js, the PlayerComponent is retrieved via Component.getComponent.

In addition to the two static methods above, there are some important functions in Component.

  • Constructor (the first argument to the constructor is Player, which ensures that all components can get the Player object)
  • CreateEl (create DOM)
  • El (Get component DOM)

Subcomponent operation methods

  • InitChildren (add child components to DOM, components can be nested indefinitely)
  • addChild
  • removeChild
  • getChild

Style manipulation correlation

  • buildCSSClass
  • addClass
  • removeClass
  • toggleClass
  • hasClass

DOM manipulation is similar to jQuery

  • show
  • hide
  • The $$$

Use the built-in LoadingSpinner in VideoJS as an example of how a Component inherits from Component.

import Component from './component';
import * as dom from './utils/dom';

class LoadingSpinner extends Component {

  // Just override the createEl method
  createEl() {
    const isAudio = this.player_.isAudio();
    const playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');
    const controlText = dom.createEl('span', {
      className: 'vjs-control-text'.innerHTML: this.localize('{1} is loading.', [playerType])
    });

    const el = super.createEl('div', {
      className: 'vjs-loading-spinner'.dir: 'ltr'
    });

    el.appendChild(controlText);

    returnel; }}// Register the component itself in the file so that you only need to import the file globally once
Component.registerComponent('LoadingSpinner', LoadingSpinner);
export default LoadingSpinner;
Copy the code

Player core player.js

As mentioned earlier, Player is also a derivative of Component, but Player is a bit special because it is the container for all the other components.

The Player is the heart of The VideoJS core because the code file alone has over 5000 lines. 5000 + lines should be criticized, maintenance costs must be high, can be split into multiple files.

As for the number of lines in a code file, I generally recommend no more than 500 lines, and preferably no more than 200.

In the player. Js line 5148 have registered player code Component. RegisterComponent (‘ player, the player); .

The createEl function in player.js determines how to create the video and copy the attributes based on the tag argument passed in.

There is a lot of wrapping around MediaElement in player.js. MediaElement can be manipulated through a Player object.

There are other important methods in player.js: src_, loadTech_, selectSource, etc. After specifying media playback sources, these methods call Tech that supports playback from these specified media sources, such as HLS or FLV video sources, to play the video.

Tech is an important and complex concept in VideoJS. Due to space limitation, there is no more description here. I will write another article to explain it in the future.

The default component for VideoJS

Videojs is rich enough on its own when we use it. At the same time, VideoJS also supports various extensions, and its internal default functions are basically Component, which is a child of the player container.

As described in the previous component registration mechanism, the default component file is introduced in player.js.

Note is introduced, just to make components can execute Component. RegisterComponent to register.

import './tech/loader.js';
import './poster-image.js';
import './tracks/text-track-display.js';
import './loading-spinner.js';
import './big-play-button.js';
import './close-button.js';
import './control-bar/control-bar.js';
import './error-display.js';
import './tracks/text-track-settings.js';
import './resize-manager.js';
import './live-tracker.js';
Copy the code

The children of Component are defined by children, and in the initChildren method they are added to the DOM according to children.

The Player is also a Component, and its default child Component is defined as follows.

Player.prototype.options_ = {
  techOrder: Tech.defaultTechOrder_,
  html5: {},
  children: [
    'mediaLoader'.'posterImage'.'textTrackDisplay'.'loadingSpinner'.'bigPlayButton'.'liveTracker'.'controlBar'.'errorDisplay'.'textTrackSettings'.'resizeManager']};Copy the code

Among these default child components of Player, there is a special child component called the ControlBar, which itself has many default child components, such as play buttons, volume bars, progress bars, full-screen buttons, and so on. The default child component is defined in Control-bar.js.

ControlBar.prototype.options_ = {
  children: [
    'playToggle'.'volumePanel'.'currentTimeDisplay'.'timeDivider'.'durationDisplay'.'progressControl'.'liveDisplay'.'seekToLive'.'remainingTimeDisplay'.'customControlSpacer'.'playbackRateMenuButton'.'chaptersButton'.'descriptionsButton'.'subsCapsButton'.'audioTrackButton'.'fullscreenToggle']};Copy the code

Like the default child Component of player, the controlBar child Component is defined by an array of strings, each of which is a key that the other Component registers with to obtain the Component instance.

The Component registration mechanism and the Children child definition are key to VideoJS extensibility. The videoJS extensibility will be explained in detail in a follow-up article.