Airi

Players

How to configure Shaka Player and Video.js to play your protected content.

What you need #

Rather not assemble it yourself?

This guide is for wiring your own player by hand. If you just want playback without writing any of this, the Airi Player is free and embeds in one line.

Two things: the manifest URL and the license server URLs. The player picks the DRM system on its own; you just hand it the addresses.

Widevine
https://drm.airi.live/v2/widevine
PlayReady
https://drm.airi.live/v2/playready
FairPlay
https://drm.airi.live/fairplay
WisePlay
https://drm.airi.live/v2/wiseplay

These URLs are public, and that is fine

They ship inside the player configuration, so anyone can read them. They grant nothing: the license server checks the key and applies your rules before issuing anything.

If you have no packaged content yet, Encrypt your video has a script — Node and Python — that does the whole path: request keys, transcode with ffmpeg, encrypt in cbcs.

Shaka Player #

The most complete for DASH and HLS, and what we recommend if you are starting fresh.

Minimum configuration
import shaka from 'shaka-player/dist/shaka-player.compiled.js';

shaka.polyfill.installAll();

const video = document.querySelector('video');
const player = new shaka.Player();
await player.attach(video);

player.configure({
  drm: {
    servers: {
      'com.widevine.alpha':      'https://drm.airi.live/v2/widevine',
      'com.microsoft.playready': 'https://drm.airi.live/v2/playready',
      'com.apple.fps':           'https://drm.airi.live/fairplay'
    }
  }
});

await player.load('https://your-bucket.s3.amazonaws.com/movie/manifest.mpd');
video.play();

That is all for Widevine and PlayReady. FairPlay additionally needs the Apple certificate:

FairPlay
player.configure({
  drm: {
    servers: { 'com.apple.fps': 'https://drm.airi.live/fairplay' },
    advanced: {
      'com.apple.fps': {
        // Certificate issued by Apple to your developer account
        serverCertificateUri: 'https://your-bucket.s3.amazonaws.com/fairplay.cer'
      }
    }
  }
});

// FairPlay works over HLS only
await player.load('https://your-bucket.s3.amazonaws.com/movie/master.m3u8');

Which system the browser picks, and why it matters

When a device has more than one, the player chooses on its own — and it does so before knowing what your rule will demand. Edge has both Widevine and PlayReady: it tends to pick Widevine, which on desktop is software-only (L3). If your rule asks for hardware the licence is refused, even though PlayReady could have served it.

There are two ways to steer it. The first is to state a preference:

example
player.configure({
  drm: {
    servers: { /* ... */ },
    preferredKeySystems: ['com.microsoft.playready']
  }
});

The second is better when you want the browser to rule out the ones that cannot serve you: declare the robustness you need, and the browser itself rejects a system that cannot meet it, letting the player move to the next.

example
player.configure({
  drm: {
    servers: { /* ... */ },
    advanced: {
      'com.widevine.alpha':      { videoRobustness: 'HW_SECURE_DECODE' },
      'com.microsoft.playready': { videoRobustness: '3000' }
    }
  }
});
example
// Check the hardware client exists before asking for it.
const hw = await navigator.requestMediaKeySystemAccess(
  'com.microsoft.playready.recommendation.3000',
  [{ initDataTypes: ['cenc'], videoCapabilities: [{
       contentType: 'video/mp4; codecs="avc1.640028"', robustness: '3000' }] }]
).then(() => true, () => false);

if (hw) {
  player.configure('drm.keySystemsMapping', {
    'com.microsoft.playready': 'com.microsoft.playready.recommendation.3000'
  });
}

That robustness has to match your rule

Ask for hardware in the player while your Airi rule says “Compatible” and you exclude devices that could have played. The other way round — player asking for nothing, rule demanding hardware — the failure arrives late, as a refused licence.

On the demo you can force each system and watch the effect on your own machine.

Seeing what is happening

When something fails, this is what you want to look at:

example
player.addEventListener('error', (event) => {
  console.error('code', event.detail.code, event.detail.data);
});

// While playing:
player.drmInfo();          // { keySystem, encryptionScheme, keyIds }
player.getKeyStatuses();   // { '<kid>': 'usable' | 'output-restricted' | ... }
player.getStats();         // width, height, streamBandwidth

`output-restricted` is not a configuration error

It means the license arrived but the device does not meet what the policy demands — typically missing HDCP or hardware protection. It is per-quality rules working exactly as intended.

Video.js #

If you already use Video.js, DRM comes through the videojs-contrib-eme plugin. It is not bundled.

Install
npm install video.js videojs-contrib-eme
Configuration
import videojs from 'video.js';
import 'videojs-contrib-eme';

const player = videojs('my-video');
player.eme();   // must be enabled explicitly

player.src({
  src: 'https://your-bucket.s3.amazonaws.com/movie/manifest.mpd',
  type: 'application/dash+xml',
  keySystems: {
    'com.widevine.alpha':      'https://drm.airi.live/v2/widevine',
    'com.microsoft.playready': 'https://drm.airi.live/v2/playready'
  }
});

FairPlay on Video.js takes more manual work, since you have to supply the certificate and transform the challenge:

FairPlay with Video.js
player.src({
  src: 'https://your-bucket.s3.amazonaws.com/movie/master.m3u8',
  type: 'application/x-mpegURL',
  keySystems: {
    'com.apple.fps.1_0': {
      certificateUri: 'https://your-bucket.s3.amazonaws.com/fairplay.cer',
      licenseUri: 'https://drm.airi.live/fairplay'
    }
  }
});

For FairPlay, Shaka is easier

If your audience includes iPhone and Safari and you can still choose a player, Shaka Player handles FairPlay with less code and fewer special cases.

When it does not play #

Nearly every failure is one of these five, in order of frequency.

1. Nothing loads, and the console mentions CORS

The bucket holding the segments does not allow requests from your domain. Rule this out first, because the symptom looks like a DRM problem and is not. Allow GET and HEAD.

2. Works in Chrome but not in Safari

Two possible causes, worth checking in this order: the content is encrypted in cenc rather than cbcs — FairPlay does not understand cenc — or the Apple certificate is missing. The first means repackaging; the second is configuration.

3. It plays, but stays on the low quality

It is working as intended. The device does not meet the security level the high quality demands, so the license server gives it the one it can have. Check on the demo: it tells you what level your own machine has.

4. The license server returns 400 or 403

The video’s keyId matches none of your keys. Usually it was packaged with a test key and a different one requested afterwards, or the key_id was passed to the packager in base64 rather than hexadecimal.

5. A robustness error on load

The browser does not offer the level the content asks for. On desktop Chrome that is normal for anything demanding hardware: that browser is L3.

The demo diagnoses your device

airi.live/drm/demo checks in your browser which DRM systems it has, how far its robustness goes, and whether it accepts cenc, cbcs or both. When something will not play, start there.