Video.js Blog

Pat O'Neill2017-01-26

Feature Spotlight: Advanced Plugins

Note: Advanced plugins are being introduced in Video.js 6.0 and are only supported from that version forward.

If you've been a Video.js user for a while, you're likely familiar with the concept of plugins: functions that become methods of any player you create. If you're not familiar with Video.js plugins, we have a comprehensive plugins guide available.

These plugins - which we'll call basic plugins - are lightweight and offer complete control of the player. That's really useful and it isn't changing - existing plugins should continue to work!

But what if you want a richer set of features? Or more guidance on how to structure your plugin? Or more tools out of the box that help manage complex plugin-rich players?

Well, until Video.js 6.0, you had to figure things out on your own.

Introducing Advanced Plugins

One of Video.js' strengths is its rich ecosystem of plugins; so, in the last few months, we wanted to focus our efforts on improving the plugin author experience.

While projects like the plugin generator and videojs-spellbook make becoming a plugin author easier than ever, the Video.js team thought it was important to provide a foundational API and set of conventions on which the future of Video.js plugins could be built.

Our solution is advanced plugins.

Advanced Plugins are Component-Like

One of the design goals for advanced plugins was to provide an API that was reminiscent of the existing components system. We achieved this in a number of ways.

At the lowest level, this included a name change for the plugin registration function from videojs.plugin to videojs.registerPlugin (taking a naming cue from videojs.registerComponent and videojs.registerTech).

Beyond a simple registration method name change, advanced plugins are class-based. A trivial example of an advanced plugin might look something like this:

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

class HelloWorld extends Plugin {
  constructor(player) {
    super(player);
    this.player.addClass('hello-world');
  }
}

videojs.registerPlugin('helloWorld', HelloWorld);

This plugin can be initialized in the same way as a basic plugin - via a player method whose name matches the registered name of the plugin.

In the case of advanced plugins, this method is a factory function, which instantiates the plugin class and returns an instance.

It's useful to know that the player method that is created will always be a function. If a player already has an instance of an advanced plugin, its associated method will simply return the pre-existing instance rather than re-initialize it:

const player = videojs('my-player');
const instance = player.helloWorld();

// Logs: 'true'
videojs.log(instance === player.helloWorld());

The helloWorld method will return this plugin object until it is disposed - after which it will create a new plugin instance again.

Events

Similar to components, advanced plugins can listen to and trigger events via the on, one, off, and trigger methods.

This provides a loosely coupled communication channel for plugins and other objects (components, players, etc) to manage their own state and respond to changes in the state of one another.

Additional Event Data

The Video.js event system allows additional data to be passed to listeners as a second argument when triggering events (the first argument is the event object itself).

Plugin events pass a consistent set of properties in this object (including any custom properties passed to trigger):

  • instance: The plugin instance, which triggered the event.
  • name: The name of the plugin as a string (e.g. 'helloWorld').
  • plugin: The plugin class/constructor function (e.g. HelloWorld).

For example, a listener for an event on a plugin can expect something like this:

const player = videojs('my-player');
const instance = player.helloWorld();

instance.on('some-custom-event', (e, data) => {
  videojs.log(data.instance === instance); // true
  videojs.log(data.name === 'helloWorld'); // true
  videojs.log(data.plugin === videojs.getPlugin('helloWorld')); // true
  videojs.log(data.foo); // "bar"
});

instance.trigger('some-custom-event', {foo: 'bar'});

Lifecycle

Another similarity between plugins and components is the concept of a lifecycle - more specifically, setup and teardown processes.

We get the setup feature as a side effect of normal object creation in JavaScript, but we are left to our own devices when it comes to object destruction and ensuring that references between objects are cleaned up to avoid leaking memory.

Video.js components have long had a dispose method and event that deal with removing a component from the DOM and memory. Advanced plugins have the same feature:

const player = videojs('my-player');

const firstInstance = player.helloWorld();

// Logs: 'true'
videojs.log(firstInstance === player.helloWorld());

firstInstance.on('dispose', () => videojs.log('disposing a helloWorld instance'));

// Logs: 'disposing a helloWorld instance'
firstInstance.dispose();

const secondInstance = player.helloWorld(); 

// Logs: 'false'
videojs.log(firstInstance === secondInstance);

The pluginsetup Event

Plugins do have one lifecycle feature that components do not: the pluginsetup event.

This event is triggered on a player when a plugin is initialized on it:

const player = videojs('my-player');

player.on('pluginsetup', (e, hash) => {
  if (hash.name === 'helloWorld') {
    videojs.log('A helloWorld instance was created!');
  }
});

// Logs: 'A helloWorld instance was created!'
player.helloWorld();

React-inspired Statefulness

One of the exciting additions in Video.js for both advanced plugins and components is React-inspired statefulness. Essentially, this means that all plugin objects and component objects have a state property, which is a plain object that can be used to store variable state for that object. Then, there is a setState method that updates this object and triggers a statechanged event.

This system allows plugins and components to use their evented nature to communicate in-memory state changes through a consistent API:

// A static property of the constructor can be used to pre-populate state 
// for all instances.
HelloWorld.defaultState = {color: 'red'};

const player = videojs('my-player');
const instance = player.helloWorld();

instance.on('statechanged', (e) => {
  const {color} = e.changes;

  if (color) {
    videojs.log(`The helloWorld color changed from "${color.from}" to "${color.to}"!`);
  }
});

// Logs: 'The helloWorld color changed from "red" to "blue"!'
instance.setState({color: 'blue'}); 

Player Plugin Awareness

Finally, we couldn't add new plugin infrastructure without working on one of the more pernicious problems of managing complex combinations of plugins: the player can't report which plugins it has initialized - or not. To this end, the player has two new methods: hasPlugin and usingPlugin. These methods work for both types of plugins.

The hasPlugin Method

This method reports whether a plugin matching a given name is available on the player:

const player = videojs('my-player');

// Logs: 'true'
videojs.log(player.hasPlugin('helloWorld'));

// Logs: 'false'
videojs.log(player.hasPlugin('fooBar'));

This method ignores whether or not the plugin has been initialized and merely reports whether or not it has been registered.

The usingPlugin Method

This method reports not only whether a plugin is available on a player, but whether it is currently active on the player:

const player = videojs('my-player');

// Logs: 'false'
videojs.log(player.usingPlugin('helloWorld'));

player.helloWorld();

// Logs: 'true'
videojs.log(player.usingPlugin('helloWorld'));

One caveat to note here. While this works for both types of plugins, only advanced plugins can change this value more than once. A basic plugin has no built-in lifecycle or events; so, it's not possible to determine whether one has been "disposed".

Go Forth and Code

We hope these additions and improvements to the plugin architecture will make writing Video.js plugins more pleasurable and remove some of the low-level legwork involved in ensuring plugins aren't creating memory leaks and other problems.

The design of advanced plugins is such that we can add features as 6.0 matures and we get more community feedback. As always, we strongly encourage our users to give back to the Video.js project in whatever way they can.

For a more complete discussion of plugins generally, visit the Video.js plugins guide.

Gary Katsevman2017-01-23

Video.js 6.0.0-RC.0: The first Release Candidate

The first Release Candidate for 6.0 has been released

Last week, we began wrapping up months of effort to make Video.js even better with the first Release Candidate (RC) of Video.js 6.0. In order to make it better, however, we had to make a few breaking changes and we also made a lot of improvements under the hood.

How to try it out

The RC is now published on npm under the beta tag with verion 6.0.0-RC.0.

npm install video.js@beta

Please try it out and let us know how it is on GitHub.

What to look forward to

  • We're finally removing Flash from core as outlined in a previous post.
  • Plugins are being updated to a React-inspired component architecture. The old style is staying around.
  • We're recommitting to accessiblity by fixing the accessibilty of our volume control and bringing back outlines!
  • Middleware. A brand new feature to interface between Video.js's techs and the player.

Feature Spotlights

Over the coming weeks, we'll post feature spotlights talking about the big things that are happening. We might also revisit some old features.

Jon-Carlos Rivera2016-10-19

Introducing Thumbcoil

A transmuxer takes media contained in some file format, extracts the raw compressed video and audio from inside (a process called demuxing) and repackages the compressed data into another format (termed remuxing) without performing any re-compression.

In the Beginning

While building Mux.js - the transmuxer at the heart of videojs-contrib-hls - we faced a problem: How do we determine if the output from Mux.js is correct?

Early on we managed to figure out how to coax FFmpeg into creating MP4s from MPEG2-TS segments that would play back in a browser with Media Source Extensions (MSE) which at the time meant only Chrome. However, we needed a simple way to compare the output of our transmuxer with what was produced by FFmpeg. The comparison had to be aware of the MP4 format since the two outputs are extremely unlikely to be byte-identical.

MP4 files are composed of boxes - hierarchical logical units that, conveniently, all start with a 32-bit length and a 32-bit box-type. Boxes will often contain other sub-boxes.

The answer to that problem was to build an "mp4-inspector" - a tool that would parse MP4 and display a sort of JSON-like dump of any relevant boxes and their contents. By generating a dump of the output from Mux.js and comparing it to a known-good fragment generated with FFmpeg, we could see where our transmuxer's output differed.

The "mp4-inspector" was built as a web page so that we can have a graphical color-coded diff of the two segments. Over time the page gained a video element and we started appending the results of transmuxing segments directly into the video element's MediaSource to aid in instant feedback and validation of changes to Mux.js.

A Brave New World

A media container such as MP4 encapsulates the video and audio stream. It has metadata describing the streams, timing information for each frame, and the stream data itself.

As development continued, we would sometimes encounter streams that would fail in new and interesting ways. Some of these failures were, admittedly, due to bugs in Mux.js. As Mux.js itself became more robust, failures were increasingly caused by problems with the streams or issues with a particular implementation of the MSE specification.

It eventually dawned on us that we really needed to learn more about what was happening inside of those videos. We needed to see not just what was happening at the media container level but we had to go deeper - we needed to peek into the video data itself. For that purpose we created Thumbcoil.

Inside of a container, video and audio are contained in data called bitstreams. Bitstreams are the data produced by encoders to represent the audio signals or video frames. Some common bitstreams are AAC for audio and H.264 for video.

Thumbcoil

Thumbcoil is a suite of tools designed to give you a peek into the internals of H.264 video bitstreams contained inside either an MP4 or MPEG2-TS container file. Using the tools in Thumbcoil you can get a detailed view of the internal structure of the two supported media container formats.

In addition, the tools have the ability to show you the information contained within the most important NAL-units that make up the H.264 bitstream. Ever wonder what kind of secret information the video encoder has squirreled away for decoders to use? Now, with Thumbcoil, you can finally see for yourself!

Motivation

An H.264 encoded bitstream is composed of what are called NAL, or network abstraction layer, units. NALs are a simple packet format designed to use bits as efficiently as possible.

Believe it or not, there are very few good tools to generate a somewhat graphical display of the structure of media containers and the data that they contain. Debugging problems with video playback is usually a tedious task involving various esoteric FFmpeg and FFprobe incantations. Unfortunately at it's best, FFprobe is only able to print out a small portion of the data we were interested in.

The exact data inside of the various parameter sets for instance is not available via the command-line. Inside of FFprobe, that data is parsed and stored but there is no easy way to dump that information in a human readable form.

In H.264, there are two special types of NAL-units - the SPS or seq_parameter_set and the PPS or pic_parameter_set. These two NAL units contain a lot of information. The decoders require this information to reconstruct the video.

Thumbcoil not only provides parameter set information in excruciating detail but also keeps the information with its surrounding context - the boxes it was contained by or the frame it was specified along with. This context is often very important to understanding issues or peculiarities in streams.

Built Upon Fancy Stuff

One of the more interesting things about how Thumbcoil parses parameter sets is that is builds what is internally called a "codec" for each NAL unit type. These codecs are specified using what is essentially a fancy parser combinator-type setup.

Much of the data in the two parameter sets are stored using a method called exponential-golomb encoding. This method uses a variable number of bits to store numbers and is particularly suited to values that tends to be small.

Each function used to build the codec returns an object with two functions: decode and encode. This means that we can specify the format of, say, a seq_parameter_set NAL unit just once and then we can both parse from and write to the bitstream for that particular NAL unit.

The "grammar" used to specify NAL unit codecs is very similar to the grammar used by the H.264 specification (ISO/IEC 14496-10). The data-types that the codecs in Thumbcoil understand are, with some extensions, merely the same types defined in the specification such as signed- and unsigned- exponential golomb encoded integers.

In addition to the parameter sets, Thumbcoil provides insight into the structure of the slice layers themselves by parsing the slice_header data though we stop short of parsing any of the actual slice_data because things quickly become more difficult and less useful as you descend into that madness.

But what is the deal with the name?

"Thumbcoil" doesn't mean anything, really. It's an inside joke that is funny to exactly 3 people in the world - myself included. The odd name does have one benefit in that it makes for a short and easy to remember domain-name: thumb.co.il.

As with all Video.js projects, Thumbcoil is open-source software and we welcome suggestions, issues, and contributions at https://github.com/videojs/thumbcoil.

Gary Katsevman2016-08-25

Video.js 5.12.0 and 5.11.5 releases

Today, there are two releases of video.js. The first, is a patch release for the 5.11 branch. With this release, we're also updating 5.11 to stable and publishing it to the CDN.

The 5.12 release is a pre-release. It's a pretty big milestone too. Video.js finally outputs non-pre-built files so bundlers like webpack can decide what to do with them.

Notable Changes

This release is mostly a maintenance and build-change release.

  • Lint the entire codebase with our linter and run the linter as a pre-push git hook.
  • Fix CSS issues in IE8.
  • Updated dependencies using Greenkeeper.io
  • Make video.js provide ES5 source files for bundlers like webpack and browserify to use. This also makes video.js requirable in node.
    • Video.js still provides a pre-build dist files for the CSS and JavaScript for those that aren't using bundlers.
  • We've added a grunt task called check-translations that will output a list of translations missing from language files based on the en.json file which serves as a template. If you know another lanugage, this would be an easy and quick way to get started contriburing to video.js!

Known Issues

No new known issues but we have started looking into the known issues from the last release.

Looking forward

Going forward, we're looking into switching to standard-version. This will allow us to accept PRs more easily and have the changelog generated for us. Also, this will make it easier for other core contributors to accept PRs. We've previously been using the contrib tool for accepting PRs and have the CHANGELOG generated automatically at that time but getting new people started with it for merging PRs was not the easiest experience.

Raw Changelog

  • @misteroneill, @BrandonOCasey, and @pagarwal123 updates all the code to pass the linter (view)
  • @misteroneill added ghooks to run linter on git push (view)
  • @BrandonOCasey removed unused base-styles.js file (view)
  • @erikyuzwa, @gkatsev updated CSS build to inlcude the IE8-specific CSS from a separate file instead of it being inside of sass (view) (view2)
  • @gkatsev added null checks around navigator.userAgent (view)
  • greenkeeper updated karma dependencies (view)
  • @BrandonOCasey updated language docs to link to IANA language registry (view)
  • @gkatsev removed unused dependencies (view)
  • @misteroneill enabled and updated videojs-standard and fixed an issue with linting (view)
  • @misteroneill updated tests to qunit 2.0 (view)
  • @gkatsev added slack badge to README (view)
  • @gkatsev reverted back to qunitjs 1.x to unbreak IE8. Added es5-shim to tests (view)
  • @gkatsev updated build system to open es5 folder for bundles and dist folder other users (view)
  • greenkeeper updated uglify (view)
  • greenkeeper updated grunt-concurrent (view)
  • greenkeeper updated karma-chrome-launcher (view)
  • @gkatsev added tests for webpack and browserify bundling and node.js requiring (view)
  • @rlchung fixed tests that weren't disposing players when they finished (view)

Git diffstats

These are deltas between 5.11.5 and 5.12.0 with the dist folder ignored.

 .babelrc                                           |   4 -
 .gitignore                                         |   1 -
 .jshintrc                                          |  49 ++
 .npmignore                                         |   1 -
 .travis.yml                                        |   2 +-
 CHANGELOG.md                                       |  26 -
 Gruntfile.js                                       |   2 +-
 README.md                                          |   2 -
 build/grunt.js                                     | 131 ++-
 build/tasks/cdn-links.js                           |   2 +-
 build/tasks/languages.js                           |  35 -
 build/tasks/saucelabs.js                           |  24 +
 component.json                                     |   2 +-
 docs/guides/languages.md                           | 164 +++-
 docs/translations-needed.md                        | 363 --------
 lang/de.json                                       |   8 +-
 lang/en.json                                       |   2 -
 lang/fr.json                                       |  19 +-
 package.json                                       |  80 +-
 src/css/_utilities.scss                            |   2 +-
 src/css/components/_control-bar.scss               |  16 +
 src/css/components/_fullscreen.scss                |   2 +
 src/css/components/_play-pause.scss                |   2 +
 src/css/components/_progress.scss                  |   2 +-
 src/css/components/menu/_menu.scss                 |   3 +-
 src/css/ie8.css                                    |  30 -
 src/js/base-styles.js                              |  18 +
 src/js/button.js                                   |  28 +-
 src/js/clickable-component.js                      |  29 +-
 src/js/component.js                                |  79 +-
 .../audio-track-controls/audio-track-button.js     |  11 +-
 .../audio-track-controls/audio-track-menu-item.js  |  18 +-
 src/js/control-bar/control-bar.js                  |  39 +-
 src/js/control-bar/fullscreen-toggle.js            |   2 +-
 src/js/control-bar/live-display.js                 |   2 +-
 src/js/control-bar/mute-toggle.js                  |  20 +-
 src/js/control-bar/play-toggle.js                  |   8 +-
 .../playback-rate-menu-button.js                   |  27 +-
 .../playback-rate-menu/playback-rate-menu-item.js  |  10 +-
 .../progress-control/load-progress-bar.js          |  22 +-
 .../progress-control/mouse-time-display.js         |  22 +-
 .../progress-control/play-progress-bar.js          |   6 +-
 .../progress-control/progress-control.js           |   5 +-
 src/js/control-bar/progress-control/seek-bar.js    |  41 +-
 .../progress-control/tooltip-progress-bar.js       |  10 +-
 .../spacer-controls/custom-control-spacer.js       |   4 +-
 .../caption-settings-menu-item.js                  |  18 +-
 .../text-track-controls/captions-button.js         |  13 +-
 .../text-track-controls/chapters-button.js         |  57 +-
 .../chapters-track-menu-item.js                    |  18 +-
 .../text-track-controls/descriptions-button.js     |  15 +-
 .../off-text-track-menu-item.js                    |  25 +-
 .../text-track-controls/subtitles-button.js        |   4 +-
 .../text-track-controls/text-track-button.js       |  17 +-
 .../text-track-controls/text-track-menu-item.js    |  36 +-
 .../time-controls/current-time-display.js          |  13 +-
 .../control-bar/time-controls/duration-display.js  |  15 +-
 .../time-controls/remaining-time-display.js        |   7 +-
 src/js/control-bar/track-button.js                 |   7 +-
 src/js/control-bar/volume-control/volume-bar.js    |  12 +-
 .../control-bar/volume-control/volume-control.js   |  10 +-
 src/js/control-bar/volume-menu-button.js           |  19 +-
 src/js/error-display.js                            |   5 +-
 src/js/event-target.js                             |  17 +-
 src/js/extend.js                                   |   7 +-
 src/js/fullscreen-api.js                           |   6 +-
 src/js/media-error.js                              |  57 +-
 src/js/menu/menu-button.js                         |  31 +-
 src/js/menu/menu-item.js                           |  10 +-
 src/js/menu/menu.js                                |  63 +-
 src/js/modal-dialog.js                             |  20 +-
 src/js/player.js                                   | 383 ++++-----
 src/js/plugins.js                                  |   2 +-
 src/js/popup/popup-button.js                       |  10 +-
 src/js/popup/popup.js                              |  11 +-
 src/js/poster-image.js                             |   7 +-
 src/js/setup.js                                    |  40 +-
 src/js/slider/slider.js                            |  34 +-
 src/js/tech/flash-rtmp.js                          |  25 +-
 src/js/tech/flash.js                               | 157 ++--
 src/js/tech/html5.js                               | 347 +++-----
 src/js/tech/loader.js                              |  20 +-
 src/js/tech/tech.js                                | 210 +++--
 src/js/tracks/audio-track-list.js                  |   5 +-
 src/js/tracks/audio-track.js                       |  10 +-
 src/js/tracks/html-track-element-list.js           |   4 +-
 src/js/tracks/html-track-element.js                |   8 +-
 src/js/tracks/text-track-cue-list.js               |  12 +-
 src/js/tracks/text-track-display.js                | 144 ++--
 src/js/tracks/text-track-list-converter.js         |  28 +-
 src/js/tracks/text-track-list.js                   |   6 +-
 src/js/tracks/text-track-settings.js               | 347 ++++----
 src/js/tracks/text-track.js                        |  51 +-
 src/js/tracks/track-enums.js                       |  29 +-
 src/js/tracks/track-list.js                        |  12 +-
 src/js/tracks/track.js                             |  13 +-
 src/js/tracks/video-track-list.js                  |   4 +-
 src/js/tracks/video-track.js                       |  10 +-
 src/js/utils/browser.js                            |  32 +-
 src/js/utils/buffer.js                             |   9 +-
 src/js/utils/dom.js                                |  95 +--
 src/js/utils/events.js                             | 429 +++++-----
 src/js/utils/fn.js                                 |   6 +-
 src/js/utils/format-time.js                        |   2 +-
 src/js/utils/guid.js                               |   2 +-
 src/js/utils/log.js                                |  14 +-
 src/js/utils/merge-options.js                      |  14 +-
 src/js/utils/stylesheet.js                         |   7 +-
 src/js/utils/time-ranges.js                        |  68 +-
 src/js/utils/to-title-case.js                      |   2 +-
 src/js/utils/url.js                                |  29 +-
 src/js/video.js                                    |  53 +-
 test/api/api.js                                    | 421 +++++-----
 test/globals-shim.js                               |   2 -
 test/index.html                                    |   2 -
 test/karma.conf.js                                 |  17 +-
 test/require/browserify.js                         |   8 -
 test/require/node.js                               |   9 -
 test/require/webpack.js                            |   8 -
 test/unit/button.test.js                           |  32 +-
 test/unit/clickable-component.test.js              |  31 +-
 test/unit/close-button.test.js                     |  22 +-
 test/unit/component.test.js                        | 639 +++++++--------
 test/unit/controls.test.js                         | 101 ++-
 test/unit/events.test.js                           | 240 +++---
 test/unit/extend.test.js                           |  20 +-
 test/unit/media-error.test.js                      |  69 --
 test/unit/menu.test.js                             |  54 +-
 test/unit/modal-dialog.test.js                     | 138 ++--
 test/unit/player.test.js                           | 912 ++++++++++-----------
 test/unit/plugins.test.js                          | 196 +++--
 test/unit/poster.test.js                           |  68 +-
 test/unit/setup.test.js                            |  20 +-
 test/unit/tech/flash-rtmp.test.js                  |  68 +-
 test/unit/tech/flash.test.js                       | 203 ++---
 test/unit/tech/html5.test.js                       | 491 +++++------
 test/unit/tech/tech-faker.js                       |  86 +-
 test/unit/tech/tech.test.js                        | 438 +++++-----
 test/unit/test-helpers.js                          |  71 +-
 test/unit/tracks/audio-track-list.test.js          | 108 ++-
 test/unit/tracks/audio-track.test.js               |  88 +-
 test/unit/tracks/audio-tracks.test.js              |  70 +-
 test/unit/tracks/html-track-element-list.test.js   |  50 +-
 test/unit/tracks/html-track-element.test.js        |  68 +-
 test/unit/tracks/text-track-controls.test.js       | 202 ++---
 test/unit/tracks/text-track-cue-list.test.js       |  79 +-
 test/unit/tracks/text-track-list-converter.test.js |  67 +-
 test/unit/tracks/text-track-list.test.js           |  23 +-
 test/unit/tracks/text-track-settings.test.js       | 178 ++--
 test/unit/tracks/text-track.test.js                | 203 +++--
 test/unit/tracks/text-tracks.test.js               | 401 +++++----
 test/unit/tracks/track-baseline.js                 |  37 +-
 test/unit/tracks/track-list.test.js                | 104 ++-
 test/unit/tracks/track.test.js                     |  16 +-
 test/unit/tracks/video-track-list.test.js          | 111 ++-
 test/unit/tracks/video-track.test.js               |  87 +-
 test/unit/tracks/video-tracks.test.js              |  71 +-
 test/unit/utils/dom.test.js                        | 426 ++++------
 test/unit/utils/fn.test.js                         |  13 +-
 test/unit/utils/format-time.test.js                |  48 +-
 test/unit/utils/log.test.js                        |  26 +-
 test/unit/utils/merge-options.test.js              |  16 +-
 test/unit/utils/time-ranges.test.js                |  49 +-
 test/unit/utils/to-title-case.test.js              |  10 +-
 test/unit/utils/url.test.js                        | 103 ++-
 test/unit/video.test.js                            | 186 ++---
 166 files changed, 5233 insertions(+), 6541 deletions(-)
David LaPalomento2016-08-10

The End of "HTML-First"

When video.js was first released all the way back in 2010, Flash was the only way to play video in Firefox, IE, and the Android browser. And when you could use HTML video, it was really complicated to get right and broken in all sorts of scenarios (live streaming, anyone?). Those problems were a big part of why we wrote video.js in the first place. The HTML standard provided a simple, powerful, and universal API for video: why use anything else?

The superiority of HTML video is pretty well established these days and browser support for video reflects that. If you have your videos in MP4 format, video.js will play them natively in HTML on every modern desktop and mobile browser out there. In video.js 5.0, we started the process of deprecating the last holdout in our supported browsers: the dreaded Internet Explorer 8. If you're saying to yourself "Wha?! You still support IE8??", I share your shock and horror. It's no fun but we've been holding out for the couple folks who want to use video on their sites and still have to support ancient clients. There's a couple more hoops to jump through however, and you should start emotionally preparing yourself for the end of IE8 support if you're one of the people using it. With IE8 heading out to pasture, including a Flash fallback by default in video.js is starting to look a little silly.

So here's what we're thinking: move Flash support out of the core of video.js and into our legacy-compatiblity shim around the time Chrome begins deprecating Flash this December. We'll keep it around for awhile to support some more complex usage (say, live streaming in IE10) but the heart of video.js will go from "HTML-first" to "HTML-only." That should mean more focus from the core committers on some amazing new stuff like improving our plugin framework, enhancing our support for HLS and DASH, and making advanced features like ads easier to integrate and better for viewers.

How does that sound? Let us know in this issue, ping @videojs on Twitter, or come say "hi" in our Slack channel.

Gary Katsevman2016-07-22

Video.js 5.11.0 Prelease

Today sees the prerelease of version 5.11.0. I wanted to take a moment to talk about some of the changes, additions, and known issues.

This is a pre-release only. It's available on npm under the next tag and also available on the CDN under the fully qualified version number: //vjs.zencdn.net/5.11.0/video.js. It'll stay in pre-release state for around a week or more to make sure that there aren't any glaring bugs, so, please give it a shot and open issues if you find anything.

Notable Changes

  • In version 5.0, we wanted to deprecate videojs.players property in favor of of the videojs.getPlayers() getter. However, it's proved to be very useful and it is now being un-deprecated so it will no longer print deprecations in the console.
  • If the player is created without a source set and a user hits play, video.js will now wait for the a source to be provided before starting playback. This eliminates an error that happens when we try to play an empty source.
  • Our custom captions settings dialog was updated to be more accessible by using more aria attributes and better option names in drop downs.

Known Issues

  • In video.js 5.10, to be able to better respond to the source of the video element changing directly without going through video.js, we disposed SourceHandlers on subsequent loadstarts and cleared out the current source. However, this causes an issue with videojs-contrib-dash. A contributor investigated and found out that the two PRs mentioned before cause the issue. If a video is created with an MPEG-DASH source element, we end up seeing a loadstart event because of the source element and then when Dash.js kicks in and starts playback using MSE, we get another loadstart event. Since we see the second loadstat event we dispose of the SourceHandler, that is, videojs-contrib-dash and Dash.js, and the video doesn't play. A work around, while we figure out a correct solution, would be not to use source elements with DASH sources and only use the video.js API in the meantime.

Raw Changelog

  • @BrandonOCasey Document audio/video track usage (view)
  • @hartman Correct documentation to refer to nativeTextTracks option (view)
  • @nickygerritsen Also pass tech options to canHandleSource (view)
  • @misteroneill Un-deprecate the videojs.players property (view)
  • @nickygerritsen Add title to all clickable components (view)
  • @nickygerritsen Update Dutch language file (view)
  • @hartman Add descriptions and audio button to adaptive classes (view)
  • @MattiasBuelens Retain details from tech error (view)
  • @nickygerritsen Fix test for tooltips in IE8 (view)
  • @mboles added loadstart event to jsdoc (view)
  • @hartman added default print styling (view)
  • @ldayananda updated videojs to not do anything if no src is set (view)
  • @nickygerritsen removed unused tracks when changing sources. Fixes ##3000 (view)
  • @vit-koumar updated Flash tech to return Infinity from duration instead of -1 (view)
  • @alex-phillips added ontextdata to Flash tech (view)
  • @MattiasBuelens updated components to use durationchange only (view)
  • @misteroneill improved Logging for IE < 11 (view)
  • @vdeshpande updated control text of modal dialog (view)
  • @ldayananda fixed mouse handling on menus by using mouseleave over mouseout (view)
  • @mister-ben updated language to inherit correctly and respect the attribute on the player (view)
  • @sashyro fixed nativeControlsForTouch option (view)
  • @tbasse fixed techCall null check against tech (view)
  • @rbran100 checked src and currentSrc in handleTechReady to work around mixed content issues in chrome (view)
  • @OwenEdwards fixed caption settings dialog labels for accessibility (view)
  • @OwenEdwards removed spurious head tags in the simple-embed example (view)
  • @ntadej added a null check to errorDisplay usage (view)
  • @misteroneill fixed logging issues on IE by separating fn.apply and stringify checks (view)
  • @misteroneill fixed npm test from running coveralls locally (view)
  • @gkatsev added es6-shim to tests. Fixes Flash duration test (view)
  • @misteroneill corrects test assertions for older IEs in the log module (view)
  • @gkatsev fixed setting lang by looping through loop element variable and not constant tag (view)

Git diffstats

These are deltas between 5.11.0 and 5.10.7

CHANGELOG.md                                       |    33 +
build/grunt.js                                     |     3 +-
component.json                                     |     2 +-
docs/examples/simple-embed/index.html              |     3 -
docs/guides/audio-tracks.md                        |    69 +
docs/guides/languages.md                           |    12 +-
docs/guides/text-tracks.md                         |   184 +
docs/guides/tracks.md                              |   186 +-
docs/guides/video-tracks.md                        |    70 +
docs/index.md                                      |     2 +-
lang/en.json                                       |     1 +
lang/nl.json                                       |    19 +-
package.json                                       |     7 +-
src/css/_print.scss                                |     5 +
src/css/components/_adaptive.scss                  |     9 +-
src/css/components/_captions-settings.scss         |    26 +-
src/css/video-js.scss                              |     2 +
src/js/clickable-component.js                      |    12 +-
.../control-bar/time-controls/duration-display.js  |     8 +-
.../time-controls/remaining-time-display.js        |     1 +
src/js/menu/menu-button.js                         |     4 +-
src/js/modal-dialog.js                             |     2 +-
src/js/player.js                                   |    68 +-
src/js/tech/flash-rtmp.js                          |     3 +-
src/js/tech/flash.js                               |    21 +-
src/js/tech/html5.js                               |    73 +-
src/js/tech/tech.js                                |    16 +-
src/js/tracks/text-track-settings.js               |   176 +-
src/js/utils/browser.js                            |     3 +
src/js/utils/create-deprecation-proxy.js           |    50 -
src/js/utils/log.js                                |   124 +-
src/js/video.js                                    |    16 +-
test/globals-shim.js                               |     1 +
test/unit/button.test.js                           |     5 +-
test/unit/player.test.js                           |    22 +-
test/unit/plugins.test.js                          |    20 +-
test/unit/tech/flash.test.js                       |    51 +-
test/unit/tech/html5.test.js                       |    16 +-
test/unit/tech/tech.test.js                        |    17 +-
test/unit/tracks/text-track-settings.test.js       |    51 +-
test/unit/tracks/text-track.test.js                |    13 +-
test/unit/utils/create-deprecation-proxy.test.js   |    45 -
test/unit/utils/log.test.js                        |   104 +-
103 files changed, 971 insertions(+), 55554 deletions(-)
Gary Katsevman2016-06-01

Video.js 5's fluid mode and playlist picker

How it works

In video.js 5.0, we added support for truly fluid layouts with video.js. You can see an example of it on the video.js website.

It is done by using intrinsic ratios. Video.js does the heavy lifting for you.

How to use it in video.js

In video.js, to make a player fluid, you can either set the fluid option

let player = videojs('preview-player', {
  fluid: true
});

Or you can add one of the fluid classes to the player: .vjs-fluid, .vjs-4-3, .vjs-16-9:

<video id="preview-player" class="video-js vjs-fluid" controls data-setup={}>

.vjs-4-3 maintains a 4:3 aspect ratio for the video and .vjs-16-9 maintains a 16:9 one. .vjs-fluid is a bit more special. It waits for the video metadata to load and then uses the video width and video height to calculate the correct aspect ratio to use for the video.

Playlist picker

This works great if you only have the player by itself. What if you are trying to a attach a playlist to the video element and keep it at the same height Like we did on the advanced example page on the video.js website?

We could calculate how much the padding top should be depending on the width of the playlist picker or the container element but then each time a video changes we would need to recalculate the height of the playlist picker. Instead, we can rely on video.js to do all the work.

Attaching the playlist picker

For this example, We're using the videojs-playlist-ui and videojs-playlist plugins for the playlist functionality. We then wrap the player in a container and put the playlist-ui element in there as well.

<section class="main-preview-player">
  <video id="preview-player" class="video-js vjs-fluid" controls preload="auto" crossorigin="anonymous">
    <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
  </video>

  <ol class="vjs-playlist"></ol>
</section>

Now we can relatively quickly make them align together with some CSS:

.video-js {
  width: 70%;
  float: left;
}
.vjs-playlist {
  width: 30%;
  float: right;
}

How to make it fluid

As you can see in the preceeding screenshot, it isn't aligned correctly with the player like in the screenshot above. Video.js calculates the aspect ratio and then adds a stylesheet to the page:

.preview-player-dimensions.vjs-fluid {
  padding-top: 41.66666666666667%;
}

That percentage results in a 2.4 aspect ratio which matches that of the oceans clip.

So, to make sure that the playlist picker is the same height, we can just add the player dimensions class to it:

<ol class="vjs-playlist preview-player-dimensions vjs-fluid"></ol>

How to make it line up

One of the easiest ways of making these two things line up correctly is to use flexbox. It'll make the player and playlist picker grow to fill up as much space as needed. Also, the playlist picker collapse underneath the player if the width of the page is too small. Flexbox is available on a lot of platforms. However, some browsers were implementing flexbox as the specification for it was evolving. It's probably best to run this css through something like autoprefixer. Using autoprefixer won't make it work on browsers that don't support flexbox but will significantly increase platform support.

First, we set display to flex and add some properties for wrapping and sizing:

.main-preview-player {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

flex-wrap allows playlist picker to wrap to the next line if the width of the container is too small. Then we want to position the player and playlist picker relative to the container and set some default and minimum sizes:

.video-js,
.vjs-playlist {
  position: relative;
  min-width: 300px;
  min-height: 150px;
  height: 0;
}

And finally, we want to apply the flex setting to the player and playlist picker:

.video-js {
  flex: 3 1 70%;
}

.vjs-playlist {
  flex: 1 1 30%;
}

This tells the player to grow and take up 3x the space as the playlist picker and defaults to 70% of the width. The playlist picker itself defaults to 30% of the width and is allowed to grow and shrink as necessary. Now if we load this in a browser we see a problem. The playlist isn't the right height:

This is because the playlist-ui plugin sets its own padding on the element that ends up overriding the preview-player-dimensions padding-top. We can fix this by forcing the padding-top we want. However, while this solves our height problem, where are our items? Oh, you need to scroll to get them. That seems less than ideal.

This happens because our padding-top is inside the playlist picker; it pushed all the elements down requiring scrolling to get to them.

The final solution

Ultimately, what we need to do is wrap the playlist element in a container that flexes so that the padding-top doesn't push the playlist items down.

<div class="playlist-container  preview-player-dimensions vjs-fluid">
  <ol class="vjs-playlist"></ol>
</div>

We also change the vjs-playlist references to playlist-container and absolutely position the playlist picker inside its container:

.playlist-container {
  position: relative;
  min-width: 300px;
  min-height: 150px;
  height: 0;
}

.playlist-container {
  flex: 1 1 30%;
}

.vjs-playlist {
  margin: 0;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

Now we have what we were looking for:

All together now

The HTML:

<section class="main-preview-player">
  <video id="preview-player" class="video-js vjs-fluid" controls preload="auto" crossorigin="anonymous">
    <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
  </video>

  <div class="playlist-container  preview-player-dimensions vjs-fluid">
    <ol class="vjs-playlist"></ol>
  </div>
</section>

And the CSS:

.main-preview-player {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.video-js,
.playlist-container {
  position: relative;
  min-width: 300px;
  min-height: 150px;
  height: 0;
}

.video-js {
  flex: 3 1 70%;
}

.playlist-container {
  flex: 1 1 30%;
}

.vjs-playlist {
  margin: 0;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}
Steve Heffernan2015-09-29

Video.js 5: The Only Thing That’s Changed Is Everything...except for like 3 things that didn't (including the name).

First and foremost, THANK YOU to the 25 contributors who completed and merged 146 pull requests and updated just about every line of code in the project. And thank you to the hundreds of issue commenters and plugin authors who helped shape this latest version. For a widget, we've got a pretty awesome community.

For 5.0 we have some interesting new features, and we made A LOT of new technology choices. This will include an exhaustive dive into those choices, because... why not?

  • Redesigned and rebuilt the UI
    • Using a flex-box-based controls layout for easier add-ons
    • Improved accessibility of the controls
    • Switched from LESS to SASS
    • Switched to Material Icons
  • Rebuilt the library in ES6/Babel/Browserify including Modules and Classes
  • Added support for responsive layouts including auto-sizing to the video content
  • Added support for HLS in desktop browsers without Flash
  • Improved audio-only support
  • Added 6 more language translations bringing the total to 25
  • Switched from Closure Compiler to UglifyJS (we STOPPED mangling object properties)
  • Switched to JSDoc for documentation
  • Switched to BrowserStack for automated browser testing
  • Switched to Fastly for our CDN
  • New definition around plugins
  • New player skin designer on Codepen
  • New definition around playback technologies ("techs")
  • New project governance model
  • New Website and Logo!

If you're a Video.js user or lover of semver, you're probably looking at the changelog and dying inside (or out). You're probably already thinking about how little you want to do the mega-upgrade dance every time the major version is bumped. To be clear, there will be at least some upgrade cost between major versions because that's just how things go. However, from 5.0 on, we plan on being a lot more liberal with major versions to avoid stop-the-world mega releases like this.

The fact is, this release cleaned up a lot of technical debt and cruft over years of maintaining a popular open source library. Working with the codebase is fun again (not like it wasn't before, Judgy McJudgerson) and we think we've bought ourselves at least 6 months before we have to upgrade to ES9000. In all seriousness, we plan on being quicker with releases, both breaking and non, in order to make incremental upgrades less painful and, well, quicker. Besides, Chrome and Firefox are going to be in the thousands soon for their releases, and we want some of that fun.

Redesigned and rebuilt UI

Video.js has had an all-CSS skin (including for Flash) since it was created in 2010 (the first version was literally all CSS, no images, fonts, or svgs. And still works today!) Over those years we've seen some very creative customizations and learned a lot about what users are hoping to do with player design when they're able to use native web techologies. For 5.0 we've both simplified the default layout and added more flexibility than ever before.

Flex Box Layout

One of our biggest challenges with the layout of the controls is keeping the control bar flexible and able to accomodate any new buttons that a plugin author might want to add. In 4.0 we used CSS floats to allow a new button to be appended to the control bar and flow into space. This however led to a very awkward tab order for anyone navigating the controls with the tab key. In 5.0 we were finally able to take advantage of flex box, which improves on the flexibility of the previous version while also maintaining the right tab order. In IE8 (because yeah, we still support that) we fall back to display:table, which works surprisingly well.

Improved accessibility

Accessibility has been a hot topic as of late, with new regulations helping push the industry forward and defining what accessbility means for a player. The tab-order mentioned previously was an eye sore in our accessiblity support, and we're all happy to have that fixed in 5.0. Addtionally, after a long debate, we switched our Button elements to use the actual HTML button tag instead of divs. Using divs previously gave us a large degree of safety from external styles clobbering our own styles. This can be a big problem for html-based widgets that are dropped into other frameworks that add styles directly to native elements (ahem Foundation). In the end however the accessibility experts in our community made a strong enough case, pointing out that there's still a number of devices that do not handle ARIA roles and javascript enhanced divs well enough to fully rely on them.

Switched from LESS to SASS

The original decision to use Less over Sass in version 4 was largely driven by the fact that it could be run in the browser. We knew we wanted to start using a preprocessor, but we still wanted to provide things like the skin designer which meant we needed to be able to do all pre-processing on the client. Less was totally appropriate for the job and allowed us to start modernizing the skin in appearance and tooling.

Since then, thanks to Emscripten, Sass has joined Less in the browser. This meant that we were free to use whichever of the two we liked, so all the contributors put on Less or Sass branded boxing gloves and fought until two gloves were still standing, which turned out to be Sass. Aside from the battle royale, there were a few reasons we decided to go with Sass for the new base skin in version 5.

  • Familiarity. Core contributors that started working on the new base skin were a little more experienced with Sass and simply preferred it.
  • Speed. On top of allowing us to use Sass without requiring Ruby, LibSass is fast.
  • Popularity. Your parents are right, not everything's a popularity contest... but it is when you want to pick between two basically equivalent* tools. We wanted to pick something that more devs would be familiar with, and that seems to be Sass. On that note, it was also requested fairly often on the issue tracker.
  • Community. This goes hand in hand with popularity, but the Sass community is large and growing, with currently popular projects using it (and switching to it) and new tooling popping up every day along side industry standards such as Compass and Bourbon.

Big changes that we made during the switch that are unrelated to the tooling:

  • We broke apart the source files.
  • As mentioned above, we switched to a Flexbox-based layout. "What about IE8," you say again? Tables. Srsly.
  • Improved support for responsive layouts.
  • We simplified the amount of customization we explicitly allow in the source to encourage people to build on top of the base skin, not edit it directly.
  • On the simplification note, we tried to simplify the base skin as much as possible. Our goal was to allow designers to build anything on top of the base without having to entirely start from scratch.
  • And more!

Switched to Material Icons

The switch to an icon font in version 4 was a huge win for Video.js. It allowed designers to do things as basic as style component icon colors with just CSS. It simplified the code base by allowing us not not worry about things like image sprites or other image optimizations. The only recurring issue we ran into was the process around adding new icons to the set, which ultimately involved just uploading the font back to IcoMoon and re-exporting.

In version 5, we've switched everything about our icon set. First we went with a new set of icons: Google's Material Icons. This was a big step forward in terms of appearance, but we had the same issue as far as adding new icons. To fix that process we created new Font tooling for the project.

The tooling is really simple, but it allows anyone to write out a JSON configuration file pointing to any SVGs they have access to. The output of that tool is the font files themselves along with a new SCSS partial that gets imported into our Sass workflow in the core project at build.

We primarily use the Material icons, but occasionally we have to pull in a social media icon from another set, which this process greatly simplifies. See icons missing in the set that you'd like to use? Give the tool a try and let us know what you think!

Rebuilt the library in ES6/Babel/Browserify including Modules and Classes

5.0 comes with bit of enlightenment as we ditch our not-built-here syndrome and make a leap to post-modern development practices.

When we started work on the new version we originally planned to just use browserify and CommonJS modules, but when we took a closer look at the great work being done on Babel, it only made sense to jump again to ES6 modules and save ourselves another code transition down the road, while at the same time gaining a lot of great new JavaScript features.

Side-stepping any argument of the validity of JavaScript Classes, Video.js has always used classes for the internal UI framework. We used a custom class implementation that was of course incompatible with other custom implementations, so the move to ES6 opens the door for some potential interoperability with other frameworks as more people use ES6 classes specifically.

With the move to modules we've also opened the door to using more of the glorious ecosystem that is npm. We've started to toss out some of the non-core internal libraries that required us to be experts in just about everything, and replace them with other community-built modules. So far this includes libraries like lodash and raynos/xhr, and we're actively looking for opportunies where we can share more code.

What does this mean for the end user?

This should make it easier for end-users of videojs, especially if they use browserify themselves. With this change, users can just require('video.js') in their app.js or whenever they need it to instantiate players. In addition, plugins that were written using browserify themselves can be added very easily using browserify and require as well. Of course, you can use Babel and ES6 modules as well. For example, your app.js could look like the following, assuming browserify and babel for ES6 syntax:

// import videojs
import videojs from 'video.js';
// import several plugins.
// Each requires videojs itself and registers itself accordingly.
import 'videojs-playlist';
import 'videojs-thumbnail';
// this isn't ready yet, unfortunately
import 'videojs-contrib-hls';

// make a player
let player = videojs('my-video');
// initialize some plugins
player.playlist(myplaylist);
player.thumbnail(myThumbnailConfig);

Added support for responsive layouts including auto-sizing to the video content

Check out this video from the SF Vid Tech meetup for quick overview.

Proper support for responsive layouts has been a long time request. We've had tips and guides for how to make it work in the old version, but now it's seamlessly built into the player. First we have two easy CSS classes you can just add to the embed code, vjs-16-9 and vjs-4-3.

<video class="video-js vjs-16-9 vjs-default-skin" ...></video>;

Or you can set the player to a fluid layout in the options and it will automatically update to match the aspect ratio of the content., whether you set a width, height, or neither.

var myPlayer = videojs(id, { fluid: true, preload: 'metadata' });

This was very tricky to implement, but well worth the effort. We have a jsbin example page where you can try out different options.

Added support for HTTP Live Streaming in desktop browsers WITHOUT Flash

We've been chasing down the last excuses for using Flash for video for awhile now and have reached some major milestones. videojs-contrib-hls 1.0 (it's about time!) will use Media Source Extensions, an advanced video element API, to provide HLS playback in Chrome and Microsoft Edge. Besides the enhanced debuggability of doing everything in Javascript, most of the computation has been moved to a web worker and is hardware accelerated on platforms that support it. That means even 60fps or 4k streams should play back without a stutter and your battery will get less of a workout, too. As part of that work, we've split off the code we use to repackage HLS video into a separate project in the video.js org: mux.js. If you're interested in how video works down at the bit level, check it out! We're always looking for new contributors.

Switched from Closure Compiler to Uglify (We STOPPED mangling object properties)

In version 4.0 we introduced Google's Closure Compiler into our build chain. It's advanced optimizations mode saved us 25% in filesize over UglifyJS at the time. The downsides were that it required us to write the code in very specific ways and mangled internal object properties, which made contributing, debugging, and writing plugins much more difficult. The reality today is that with gzip and improved bandwidth, video.js users are pushing us less and less to squeeze the last bit of filesize out of the library. So for 5.0 we've switched back to UglifyJS. Plugin authors rejoice!

Improved audio-only support

Hey did you know? Video.js also supports the HTML5 audio tag. Try it out and let us know what you think.

<audio id="my_audio_1" class="video-js vjs-default-skin" controls data-setup="{}"><source src="MY_AUDIO_FILE.mp3" type="audio/mp3"></source></audio>

Added 6 more language translations

Ever since we implemented localization in Video.js there's been a wave of contributions from all over the world. With 5.0 comes Danish, Bosnian, Serbian, Croatian, Finnish, and Turkish. This brings the number of lanugages supported to 25!

New definition around plugins

Plugins mostly have not changed from how they worked in Videojs 4.x. You still register and use them the same way. The only major change is that if you are instantiating a plugin inside the config of a player, this plugin will get run before the player is ready. This is so that if plugins are doing some UI work or adding themselves as children of the player they can do so early on. This means that plugins that require the player to be ready would need to handle that themselves or else not support instantiation via the player config. Ex:

videojs('my-video', {
  plugins: {
    playlists: {},
  },
});

As part of the update to videojs 5 and our switch from Google's Closure Compiler to Uglify, we've been focusing on making the plugin experience better. We've made sure to export some of the utility functions that we use inside our own codebase to make writing plugins easier. This way plugins don't need to include extra code themselves if a function, merge, for example, is available from videojs.

Also, we're encouraging plugin authors to publish their plugins on npm. If the plugins are tagged with videojs-plugin, they'll show up on our spiffy new plugins listing page.

New player skin designer on Codepen

We're taking a slightly different approach with the designer now. Instead of exposing all CSS properties from the default skin, we've set up a template starter that has fewer options and allows you to get at the more common customizations more easily.

It's also using Codepen, which is a great service and much better than hosting the designer ourselves. If you use it to make a cool skin, be sure to let us know. Tweet at @videojs or comment on the designer.

http://codepen.io/heff/pen/EarCt/left/?editors=010

New project governance model

Jumping right on that band wagon, we're implementing a governance model for the project that's similar to Node.js/io.js and 0MQ's C4.1. Along with some better structure around the project it comes with a lower bar for becoming an official maintainer of the project. We're aiming to have a more diverse set of people and companies representing the leadership of Video.js.

Improved the definition of a playback technology or "tech"

The creator of our most popular plugin (the YouTube plugin) took on the task of improving the relationship between the player and techs. "Tech" is the term we use to describe the translation layer between the player API and the underlying video objects, which can be the HTML5 video element, plugins like Flash and Silverlight, or other players like Youtube and Vimeo. If you've built a tech or are intersted in building one, check out the changes in 5.0.

Switched to JSDoc from a home-grown docs parser

In the last version we had a custom built docs generator that used esprima to parse the AST and create a lot of the API docs just from the code, with JSDoc tags filling in the rest. That could still be a good project, but it's become too much to maintain and too limited for plugin authors.

For 5.0 we've switched to jsdoc. Check out the new docs site. Anyone want to help us design it? :)

Switched to BrowserStack for automated browser testing

For 5.0 we have moved from Sauce Labs to BrowserStack as our browser provider for automated testing. BrowserStack is a cross-browser testing tool that provides desktop and mobile browsers and are currently rolling out support for real mobile devices. We moved to BrowserStack to reduce the amount of timeout errors, build time and false positives we were seeing in our builds previously. This stability has allowed us to expand our testing coverage to include iOS, Android and all supported versions of Internet Explorer.

Switched to Fastly for our CDN

The CDN-hosted version of Video.js at vjs.zencdn.net is downloaded over 2 BILLION times every month, even with agressive browser-side caching. Video.js is relatively small in file size but that's still 34 TB per month. We were previously piggy-backing on Brightcove's Akamai account, but Fastly offered to host the library for free. We gave it a shot and fell in love. In full disclosure, our testing found that Fastly didn't perform quite as well as Akamai internationally, but Fastly's user interface, API, and real-time purge are things of beauty and make our CDN deploy process much simpler.

Video.js Logo

We simplified it a bit. What do you think?

If you don't feel comfortable contributing to Video.js we can always use help on the website. It gets over 200,000 unique views every month. Contribute at the website repo.

HALP!

It's our community that keeps Video.js actually decent and truly free (Apache 2), so don't be shy. Jump in and submit a bug, build a plugin, or design a skin.

In conclusion, it's been a good year of a lot of good work. Thanks!

Matthew McClure2015-06-08

It's here: 5.0 release candidates!

Today were releasing our first official 5.0 release candidate. Were really excited to get this into the wild, but a lot has changed under hood. It should be stable for normal usage, but weve got a limited number of test devices (and testers for that matter), so what we need now is for people to bang on 5.0 and let us know what breaks!

You can either grab the master branch from Github and run grunt to create your own build, or well push each new release candidate to the CDN. Were going to be moving quickly and pushing new candidates when we uncover / fix bugs, so be sure to check for new versions! As of this post, the newest release is 5.0-rc.2. If youd like to see a demo, weve got one up on JSBin.

Whats different?

New Base Theme

The most obvious difference youll see when you load up the release candidate is most likely going to be the new base theme. Weve worked hard to simplify the theme and make it possible to do as much as possible with purely CSS.

screenshot

There are elements in the control bar that arent shown by default, but can be by simply overriding a little CSS. For example, if youd rather use an inline volume control instead of the popup menu, you could make these changes:

css .video-js .vjs-volume-menu-button { display: none; } .video-js .vjs-mute-control, .video-js .vjs-volume-control { display: flex; }

This just hides the .vjs-volume-menu-button control, and unhides the mute control / inline volume control. Other things that are available but hidden by default are a time divider (.vjs-time-devider), total duration (.vjs-duration), and a spacer (.vjs-custom-control-spacer).

ES6

Thanks to the incredible Babel project, were able to write ES2015 and have it transpiled to ES5. Its fantastic, and means we get glorious things such as string interpolation, so reading our codebase just got a whole lot ${_.sample(['better', 'awesomer', 'more fantastic', 'less plus-y'])}.

On our Wiki we have a page devoted to ES6 features we use extensively. There are probably a few little things missing, but if you familiarize yourself with those features you should feel comfortable reading the Video.js source.

Were also using Browserify (with a Babelify transform), so now were able to take advantage of all the browser packages NPM has to offer. One big area this has allowed us to improve is utility functions, which are now provided by Lodash.

Plugins

Plugins are initialized earlier than before, giving plugin developers even more control over the player experience. Theyre now initialized before other components are added (like the control bar), but after the player div is created. Keep in mind youll now need to wait for the ready event to do things such as append items to the control bar.

The plugin-development world should have also gotten more predictable since weve gotten a little less aggressive with our minification. Keep in mind that when you want to subclass a component externally (not using ES2015), youll want to use videojs.extends.

You can find more information on our 5.0 change details wiki page.

Want to be helpful?

Use it! Weve spent months using this player in very specific ways while development, but theres always the issue of not being able to see the forest for the trees. If you find issues while testing the player, please let us know on the issue tracker.

Matthew McClure2015-02-18

Video.js 4.12 - The last of the 4 minors

Much improved WebVTT support

We added basic WebVTT support really early on, which we were pretty proud of. As browsers added support, however, our shim started to lag a bit, or at the very least, cause issues with browsers that required native support (here’s looking at you, iOS).

So, we’re happy to announce that our text track support is vastly improved with this version! Instead of continuing to try and build out our own WebVTT parser, we’ve pulled in Mozilla’s fantastic work, VTT.js. This is the WebVTT implementation used in Firefox / Gecko, so we’re confident that this is a substantial leap forward for Video.js and our accessibility efforts.

Don’t use captions? Feeling disappointed?

You shouldn’t feel disappointed, this is a great day! This release includes a lot of fixes, and although fixes are always exciting to the people affected, other people can feel a hefty dose of “meh”. We’re excited to say, however, that this release is exciting for another, very important reason.

This is the last minor release in the version 4 family. There might be a few patches, but from now on, master is officially version 5.0-in-waiting. Party.

party

Notable updates and fixes for 4.12

  • The hide / show methods now toggle a class (.vjs-hidden) instead of applying inline styles. Make sure to update your stylesheet as well!
  • A new .vjs-scrubbing class gets applied while, you guessed it, scrubbing so menus can be prevented from erroneously showing on hover.
  • Improved url parsing in IE9.
  • Ever wondered what version you’re using? Now there’s a VERSION key. Woot.
  • New .vjs-ended class that’s added when playback reaches the end of the timeline (also known as: ended).
  • Player networkState and readyState properties available across techs
  • Video.js is now exported as a named AMD module
  • Mobile scrolling improvements
  • And more! Fixes galore!

Translation party continues

We’re now up to 19 community submitted translations! Entirely new translations in this release include:

  • Traditional Chinese
  • Vietnamese
  • Czech
  • Catalan
  • Bulgarian
  • Turkish

Video.js in the wild

To be terribly honest, there is a level of starstruck over a few of these. Normally we just do one, but hey…

Social Media

Global News Outlets

New plugins from the community

  • videojs-Background: A plugin that allows for videos to be displayed as a full background to any element.
  • videojs-brightcoveAnalytics: Allow tracking of views/impressions & engagement data in videojs for Brightcove videos
  • videojs-caption: Have the most flexibility and power for displaying caption videojs
  • videojs-transcript: Display an interactive transcript from caption or subtitle tracks.
  • videojs-autoplay-toggle: adds an autoplay toggle which will persist to cookies or localstorage.
  • videojs-offset: Allows you to play a segment of the video.
  • videojs-youtube-progress: Preserve progress seeker when control bar is hidden.
  • projectorjs: A small no-dependencies JavaScript library that enables the display of overlays on native HTML5 video elements, or (optionally) video elements powered by videojs.
  • videojs-record: A video.js plugin for recording audio/video files.
  • videojs-dashjs: Loads dash.js as a tech to support MPEG-DASH videos.
  • videojs-silverlight: Allows you to play WMV, WMA, MP4, MP3, WAV and FLAC medias through Silverlight within Video.js.
  • videojs-vlc: Allows you to play all media through VLC web plug-in within Video.js.
  • videojs-aurora: Allows you to play WAV, OGG, MP3, M4A, AAC and FLAC medias through Aurora.js within Video.js.
  • videojs-java: Allows you to play AIFF, AVI, GSM, MID, MPG, MP2, MOV, AU and WAV medias through Java within Video.js.

Raw list of changes

  • @PeterDaveHello added a Traditional Chinese translation (view)
  • @mmcc updated the hide/show functions to use a class instead of inline styles (view)
  • @mister-ben added better handling of the additional videojs() arguments when the player is already initialized (view)
  • @anhskohbo added a Vietnamese translation (view)
  • @Sxmanek added a Czech translation (view)
  • @jcaron23 added the vjs-scrubbing CSS class and prevented menus from showing while scrubbing (view)
  • @dmlap fixed URL parsing in IE9 (view)
  • @gkatsev Fixed issue where ManualTimeUpdatesOff was not de-registering events (view)
  • @brycefisher Added a guide on player disposal (view)
  • @toniher added a Catalan translation (view)
  • @mmcc added a VERSION key to the videojs object (view)
  • @mmcc fixed an issue with text track hiding introduced in #1681 (view)
  • @dmlap exported video.js as a named AMD module (view)
  • @dmlap fixed poster hiding when the loadstart event does not fire (view)
  • @chikathreesix fixed an object delete error in Chrome (view)
  • @steverandy fixed an issue with scrolling over the player on touch devices (view)
  • @mmcc improved tap sensitivity (view)
  • @mister-ben added a vjs-ended class when playback reaches the end of the timeline (view)
  • @dmlap Add network and ready state properties (view)
  • @woollybogger exported the hasClass function (view)
  • @DevGavin fixed the Chinese translation (view)
  • @iSimonWeb added font-path variable (view)
  • @shoshomiga added a Bulgarian translation (view)
  • @ragecub3 added a Turkish translation (view)
  • @gkatsev greatly improved text track support and implemented vtt.js as the webvtt parser (view)
  • @gkatsev fixed captions showing by default in Chrome and Safari (view)
  • @mister-ben fixed a woff warning in Firefox (view)