Presets and stages
mattebox@0.12.1
This chapter covers stages, presets, and how to build your own stack.
Stages
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 and bandwidth
memory, 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 eleven stages below, and
kernel is nothing, for a stack you build by hand.
Eleven 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 |
cmcd |
Changes every request to the CDN, so it is opt-in |
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 |
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] });
Every preset stores the measured bandwidth in localStorage for the next
session. If storage is missing or throws, nothing is stored. Pass
abrPersist(yourStorage) in stages to store it elsewhere. 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.
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.