Presets and stages

mattebox@0.14.0

This chapter covers stages, presets, and how to build your own stack.

Stages

Chapter 17 lists every stage.

A stage is a plugin. It has a name, can provide capabilities, can require other stages, and installs when you call attach. Importing a stage does nothing on its own, and stages never import each other.

The kernel is not a stage. It is always present and is a working player on its own.

Import paths

Every stage has its own import path.

Import path Layer Examples
mattebox/protocols/<name> Protocols hls-cmaf, hls-live, dash-cmaf, dash-live
mattebox/containers/<name> Containers ts-transmux, packed-audio
mattebox/stages/<name> Stages abr, text-webvtt, eme-core, recovery
mattebox/eme DRM alone attachEme, without an engine
import hlsCmaf from 'mattebox/protocols/hls-cmaf';
import tsTransmux from 'mattebox/containers/ts-transmux';
import abr from 'mattebox/stages/abr';

Stage requirements

Some stages require others. text-webvtt-segmented needs text-webvtt. eme-cenc needs eme-core. alt-audio needs codec-switch. The engine resolves these when you call mattebox() and fails with a clear error when one is missing.

mattebox({ stages: [textWebvttSegmented()] });
// Error: 'text-webvtt-segmented' requires 'text-webvtt', which nothing provides

Order in the array does not matter. The loader installs stages in dependency order.

Two stages that provide the same capability cannot be loaded together. Two WebVTT parsers is an error.

Feature detection

A stage with a public API adds a property on the engine. Stages that are not loaded add nothing, so use in to check.

if ('live' in engine) {
  engine.live.seekToEdge();
}

engine.capabilities() lists what is loaded.

console.log([...engine.capabilities()]);
// ['hls-cmaf', 'application/vnd.apple.mpegurl', 'abr', ...]

Presets

A preset is a factory that returns an engine with a fixed set of stages. Import one from mattebox/presets/<name> and call it.

import hls from 'mattebox/presets/hls';

const engine = hls();
await engine.attach(video);
engine.load(url);

Pick by protocol, whether you have MPEG-TS segments, and whether content is protected.

Preset Line MPEG-TS DRM
hls HLS
hls-drm HLS yes
hls-ts HLS yes
hls-ts-drm HLS yes yes
dash DASH
dash-drm DASH yes
dual HLS + DASH
dual-drm HLS + DASH yes
dual-ts HLS + DASH yes
dual-ts-drm HLS + DASH yes yes
full HLS + DASH yes yes
kernel none

Every preset except kernel includes the same base: on demand and live playback, adaptive quality with a size cap, recovery, content steering, alternate audio with codec switching, WebVTT subtitles, forced subtitles, CEA-608 captions, CMAF live timing, program date time, and the codec probe. The HLS lines add AES-128 segment decryption. -ts adds the transmuxer, packed audio, and ID3 metadata. -drm adds the three EME stages. full is dual-ts-drm plus the ten stages below, and kernel is nothing, for a stack you build by hand.

Ten stages are only in full. Add them with stages when you need them.

Stage Why
thumbnails Fetches image playlists, so it is opt-in
chapters Only your app knows the chapters file
trick-play Only your UI knows when to scan
timed-metadata Only your page reads it
text-cea708 Streams that carry it also carry CEA-608, which the base reads
text-ttml Most streams carry WebVTT, which the base reads
hdr Excludes HDR where nothing is known, which you may not want
qoe Only your analytics read it
cmsd Acts only on headers your CDN sends and exposes
rendition-names Only your menu reads it

cmcd is in no preset, because it changes every request to the CDN. Add it with stages. See chapter 11.

The caption stages read the NAL unit headers of every H.264 and HEVC video segment, a fraction of a millisecond per segment. If your content has no in-band captions, remove them with without: ['nal-scan', 'text-cea608'].

A preset takes the same config and transport options as mattebox(), plus two more. stages merges by name: a stage the preset already has is replaced, which is how you pass it options, and any other stage is appended. without removes stages by name.

import hls from 'mattebox/presets/hls';
import recovery from 'mattebox/stages/recovery';
import thumbnails from 'mattebox/stages/thumbnails';

const engine = hls({
  config: { bufferGoalSeconds: 40 },
  stages: [recovery({ skipAfter: 2 }), thumbnails()], // recovery replaced, thumbnails added
  without: ['content-steering'],
});

Removing a stage that another stage requires throws, with both names in the message. stages() on the preset returns the stage list with the same merge applied, if you want to reorder it or build on it yourself.

const stack = hls.stages({ without: ['pdt'] });
const engine = mattebox({ stages: [thumbnails(), ...stack] });

No preset writes to localStorage or keeps anything across page loads. The -drm presets load eme-core with no license server. Set one with engine.drm.setLicenseUrl before loading, or pass emeCore({ ... }) in stages.

Size per preset

Every preset, min+gzip, one file each. The -ts presets and full carry the transmux Worker inside.

Bundle size of every Mattebox preset, min+gzip, one file each. kernel 22.8 KB, hls 44.7 KB, dash 46.0 KB, dual 53.3 KB, dual-ts-drm 64.0 KB, full 81.1 KB.

Kernel config

mattebox() also takes config and transport. Every option has a default.

const engine = mattebox({
  stages: [hlsCmaf(), abr()],
  config: {
    bufferGoalSeconds: 30,
    backBufferSeconds: 20,
    traceCapacity: 1000,
  },
});
Option Meaning
bufferGoalSeconds Forward buffer the scheduler aims for
backBufferSeconds Media kept behind the playhead under quota pressure
manifestTimeoutMs Timeout for the manifest fetch
traceCapacity Trace entries kept for stats.trace(), none by default
bufferErrorLimit Consecutive SourceBuffer failures before a fatal error

Transport options are covered in chapter 11.

Example

HLS and DASH on demand, with ABR and subtitles.

import { mattebox } from 'mattebox';
import dashCmaf from 'mattebox/protocols/dash-cmaf';
import hlsCmaf from 'mattebox/protocols/hls-cmaf';
import abr from 'mattebox/stages/abr';
import abrCapSize from 'mattebox/stages/abr-cap-size';
import textWebvtt from 'mattebox/stages/text-webvtt';
import textWebvttSegmented from 'mattebox/stages/text-webvtt-segmented';

const engine = mattebox({
  stages: [
    hlsCmaf(),
    dashCmaf(),
    abr(),
    abrCapSize(),
    textWebvtt(),
    textWebvttSegmented(),
  ],
});

Next: 03 HLS and DASH.

Read this page's source on GitHub