# Reproductores

Source: https://airi.live/docs/reproductores
Language: es. The other: https://airi.live/docs/reproductores.md?lang=en
Index: https://airi.live/llms.txt — every page in one file: https://airi.live/llms-full.txt

Cómo configurar Shaka Player y Video.js para reproducir tu contenido protegido.

## Lo que necesitas

> **¿Prefieres no montarlo tú?:** Esta guía es para integrar tu propio reproductor a mano. Si lo que quieres es reproducir sin escribir nada de esto, [el Reproductor de Airi](/docs/reproductor) es gratis y se incrusta con una línea.

Dos cosas: la URL del manifiesto y las URLs del servidor de licencias. El reproductor elige por sí solo qué sistema DRM usar; tú solo le das las tres direcciones.

- **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`

> **Estas URLs son públicas, y está bien:** Viajan dentro de la configuración del reproductor, así que cualquiera puede verlas. No dan acceso a nada: el servidor de licencias comprueba la clave y aplica tus reglas antes de conceder nada.

Si aún no tienes contenido empaquetado, en [Cifrar tu vídeo](/docs/empaquetar) tienes un script, en Node y en Python, que hace todo el camino: pide las claves, transcodifica con ffmpeg y cifra en `cbcs`.

## Shaka Player

Es el más completo para DASH y HLS, y el que recomendamos si empiezas de cero.

Configuración mínima:

```
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://tu-bucket.s3.amazonaws.com/pelicula/manifest.mpd');
video.play();
```

Eso es todo para Widevine y PlayReady. FairPlay necesita además el certificado de Apple:

FairPlay:

```
player.configure({
  drm: {
    servers: { 'com.apple.fps': 'https://drm.airi.live/fairplay' },
    advanced: {
      'com.apple.fps': {
        // Certificado emitido por Apple a tu cuenta de desarrollador
        serverCertificateUri: 'https://tu-bucket.s3.amazonaws.com/fairplay.cer'
      }
    }
  }
});

// FairPlay solo funciona sobre HLS
await player.load('https://tu-bucket.s3.amazonaws.com/pelicula/master.m3u8');
```

### Cuál sistema elige el navegador, y por qué importa

Si el dispositivo tiene más de uno, el reproductor elige por su cuenta — y lo hace **antes** de saber qué va a exigir tu regla. Edge tiene Widevine y PlayReady: suele coger Widevine, que en escritorio es solo software (L3). Si tu regla pide hardware, la licencia se deniega, aunque PlayReady sí hubiera podido servirla.

> **No cambia de sistema solo:** Una vez elegido, el reproductor no reintenta con otro: la denegación llega del servidor de licencias, cuando el sistema ya está comprometido. Es la causa habitual de «en Edge no va y no entiendo por qué».

Tienes dos formas de dirigirlo. La primera es declarar una preferencia:

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

La segunda es mejor si lo que quieres es que el navegador **descarte solo** los que no sirven: declara la robustez que necesitas, y será el propio navegador quien rechace un sistema que no la alcanza, dejando que el reproductor pase al siguiente.

```
player.configure({
  drm: {
    servers: { /* ... */ },
    advanced: {
      'com.widevine.alpha':      { videoRobustness: 'HW_SECURE_DECODE' },
      'com.microsoft.playready': { videoRobustness: '3000' }
    }
  }
});
```

> **En Windows, pedir SL3000 así no basta — y falla de la peor manera:** Windows trae **dos** clientes PlayReady, y el nombre del sistema de claves decide cuál te atiende. `com.microsoft.playready` a secas te da el de software, cuyo certificado es SL2000 por muy capaz que sea la máquina. Pide SL3000 con él y el servidor de licencias rechaza — correctamente, porque el cliente que le presentaste no llega — en un equipo que sí podía reproducir.
> 
> El cliente de hardware solo responde si lo nombras, y el nombre se cambia con `keySystemsMapping`, **no** con `preferredKeySystems`: este último solo reordena candidatos que ya estén en el manifiesto, y el manifiesto trae el nombre base. Si prefieres un nombre ausente, Shaka vuelve al orden del manifiesto y puede acabar en otro DRM sin avisarte.

```
// Comprueba primero si existe el cliente de hardware; si no, no lo pidas.
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'
  });
}
```

> **Y si no hay cliente de hardware, retira la calidad antes de pedirla:** Cuando la comprobación de arriba dice que no, no dejes que el reproductor pida la clave que va a ser rechazada: `player.configure('restrictions.maxHeight', 720)` antes de `load()`. La razón es que PlayReady no deniega, contesta un error de servidor, y recuperarse de eso a mitad de carga es un problema abierto de Shaka ([#5421](https://github.com/shaka-project/shaka-player/issues/5421), [#2135](https://github.com/shaka-project/shaka-player/issues/2135)): la calidad se descarta pero la reproducción no arranca. Además, cada intento rechazado es una licencia que se te factura.
> 
> Un aviso sobre ese tope: si deja fuera **todas** las variantes —`maxHeight` 720 sobre un contenido que solo tiene 1080p, por ejemplo— Shaka lanza `RESTRICTIONS_CANNOT_BE_MET` (4012), que es un error crítico y no una degradación. Calcula el tope desde las alturas que existen, no desde un número fijo.

> **Esa robustez tiene que coincidir con tu regla:** Si pides hardware en el reproductor pero tu regla en Airi es «Compatible», estarás excluyendo dispositivos que sí podían reproducir. Y al revés, si el reproductor no pide nada y la regla exige hardware, el fallo aparece tarde y en forma de licencia denegada.

En [la demo](/drm/demo) puedes forzar cada sistema y ver el efecto en tu propio equipo.

### Saber qué está pasando

Cuando algo falle, esto es lo que querrás mirar:

```
player.addEventListener('error', (event) => {
  console.error('código', event.detail.code, event.detail.data);
});

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

> **`output-restricted` no es un error de configuración:** Significa que la licencia llegó pero el dispositivo no cumple lo que exige la política — típicamente falta HDCP o protección por hardware. Es exactamente lo que hacen las reglas por calidad, funcionando.

## Video.js

Si ya usas Video.js, el DRM llega con el plugin `videojs-contrib-eme`. No viene incluido.

Instalación:

```
npm install video.js videojs-contrib-eme
```

Configuración:

```
import videojs from 'video.js';
import 'videojs-contrib-eme';

const player = videojs('mi-video');
player.eme();   // hay que activarlo explícitamente

player.src({
  src: 'https://tu-bucket.s3.amazonaws.com/pelicula/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'
  }
});
```

> **Olvidar `player.eme()` es el error más común:** Sin esa llamada el plugin no se engancha y el vídeo falla sin decir por qué. Va antes de `src()`.

FairPlay en Video.js necesita más trabajo manual, porque hay que entregarle el certificado y transformar el reto:

FairPlay con Video.js:

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

> **Para FairPlay, Shaka es más fácil:** Si tu audiencia incluye iPhone y Safari y todavía puedes elegir reproductor, Shaka Player resuelve FairPlay con menos código y menos casos particulares.

## Cuando no reproduce

Casi todos los fallos son uno de estos cinco. Van en orden de frecuencia.

### 1. Nada carga, y la consola habla de CORS

El bucket donde están los segmentos no permite peticiones desde tu dominio. Es lo primero que hay que descartar, porque el síntoma parece un problema de DRM y no lo es. Configura CORS con `GET` y `HEAD` permitidos.

### 2. Funciona en Chrome pero no en Safari

Dos causas posibles, y conviene comprobarlas en este orden: el contenido está cifrado en `cenc` en lugar de `cbcs` —FairPlay no entiende `cenc`—, o falta el certificado de Apple. Lo primero obliga a volver a empaquetar; lo segundo es configuración.

### 3. Reproduce, pero se queda en la calidad baja

Está funcionando como debe. El dispositivo no alcanza el nivel de seguridad que exige la calidad alta, así que el servidor de licencias le da la que sí puede. Compruébalo en [la demo](/drm/demo): te dice qué nivel tiene tu propio equipo.

### 4. El servidor de licencias devuelve 400 o 403

El `keyId` del vídeo no corresponde a ninguna clave tuya. Suele pasar cuando se empaquetó con una clave de prueba y luego se pidió otra, o cuando el `key_id` se pasó en base64 en lugar de hexadecimal al empaquetador.

### 5. Da un error de robustez al cargar

El navegador no ofrece el nivel que pide el contenido. En Chrome de escritorio es lo normal para cualquier cosa que exija hardware: ese navegador es L3.

> **La demo diagnostica tu dispositivo:** [airi.live/drm/demo](/drm/demo) comprueba en tu navegador qué sistemas DRM tiene, hasta qué nivel de robustez llega y si admite `cenc`, `cbcs` o ambos. Cuando algo no reproduce, empieza por ahí.
