Las extensiones de medios encriptados (EME) proporcionan una API que permite que las aplicaciones web interactúen con los sistemas de protección de contenido para permitir la reproducción de audio y video encriptados.
EME está diseñado para permitir que se usen la misma aplicación y los mismos archivos encriptados en cualquier navegador, independientemente del sistema de protección subyacente. El primero es posible gracias al flujo y las APIs estandarizados, mientras que el segundo es posible gracias al concepto de encriptación común.
EME es una extensión de la especificación HTMLMediaElement
, de ahí el nombre. Ser una "extensión" significa que la compatibilidad del navegador con EME es opcional: si un navegador no admite contenido multimedia encriptado, no podrá reproducirlo, pero EME no es obligatorio para el cumplimiento de las especificaciones de HTML. Según la especificación de EME:
Las implementaciones de EME usan los siguientes componentes externos:
- Sistema de claves: Es un mecanismo de protección de contenido (DRM). EME no define Key Systems en sí, además de Clear Key (más información sobre esto a continuación).
- Módulo de desencriptación de contenido (CDM): Es un mecanismo de software o hardware del cliente que permite la reproducción de contenido multimedia encriptado. Al igual que con los sistemas de claves, EME no define ningún CDM, pero proporciona una interfaz para que las aplicaciones interactúen con los CDM disponibles.
- Servidor de licencias (claves): Interactúa con un CDM para proporcionar claves que desencripten el contenido multimedia. La negociación con el servidor de licencias es responsabilidad de la aplicación.
- Servicio de empaquetado: Codifica y encripta el contenido multimedia para su distribución o consumo.
Ten en cuenta que una aplicación que usa EME interactúa con un servidor de licencias para obtener claves que permitan la desencriptación, pero la identidad y la autenticación del usuario no forman parte de EME. La recuperación de claves para habilitar la reproducción de contenido multimedia se produce después de autenticar (opcionalmente) a un usuario. Los servicios como Netflix deben autenticar a los usuarios dentro de su aplicación web: cuando un usuario accede a la aplicación, esta determina su identidad y privilegios.
¿Cómo funciona la EME?
A continuación, se muestra cómo interactúan los componentes de EME, que corresponden al siguiente ejemplo de código:
- Una aplicación web intenta reproducir audio o video que tiene uno o más transmisiones encriptadas.
- El navegador reconoce que el contenido multimedia está encriptado (consulta el cuadro a continuación para saber cómo sucede esto) y activa un evento
encrypted
con metadatos (initData
) obtenidos del contenido multimedia sobre la encriptación. - La aplicación controla el evento
encrypted
:- Si no se asoció ningún objeto
MediaKeys
con el elemento multimedia, primero selecciona un sistema de claves disponible connavigator.requestMediaKeySystemAccess()
para verificar qué sistemas de claves están disponibles y, luego, crea un objetoMediaKeys
para un sistema de claves disponible a través de un objetoMediaKeySystemAccess
. Ten en cuenta que la inicialización del objeto MediaKeys debe ocurrir antes del primer eventoencrypted
. La app realiza la obtención de una URL de servidor de licencias independientemente de seleccionar un sistema de claves disponible. Un objetoMediaKeys
representa todas las claves disponibles para desencriptar el contenido multimedia de un elemento de audio o video. Representa una instancia de CDM y proporciona acceso a ella, específicamente para crear sesiones de claves, que se usan para obtener claves de un servidor de licencias. - Una vez creado el objeto
MediaKeys
, asígnale el elemento multimedia:setMediaKeys()
asocia el objetoMediaKeys
con un HTMLMediaElement, de modo que sus claves se puedan usar durante la reproducción, es decir, durante la decodificación.
- Si no se asoció ningún objeto
- La app crea un
MediaKeySession
llamando acreateSession()
enMediaKeys
. Esto crea unMediaKeySession
, que representa la vida útil de una licencia y sus claves. - Para generar una solicitud de licencia, la app pasa al CDM los datos multimedia obtenidos en el controlador
encrypted
llamando agenerateRequest()
en elMediaKeySession
. - El CDM activa un evento
message
: una solicitud para adquirir una clave de un servidor de licencias. - El objeto
MediaKeySession
recibe el eventomessage
y la aplicación envía un mensaje al servidor de licencias (por ejemplo, a través de XHR). - La aplicación recibe una respuesta del servidor de licencias y pasa los datos al CDM con el método
update()
deMediaKeySession
. - El CDM desencripta los medios usando las claves de la licencia. Se puede usar una clave válida, desde cualquier sesión dentro de los
MediaKey
asociados con el elemento multimedia. El CDM accederá a la clave y la política, indexadas por el ID de clave. - Se reanuda la reproducción de contenido multimedia.
¡Uf!
Ten en cuenta que puede haber varios mensajes entre el CDM y el servidor de licencias, y que toda la comunicación en este proceso es opaca para el navegador y la aplicación: solo el CDM y el servidor de licencias entienden los mensajes, aunque la capa de la app puede ver qué tipo de mensaje envía el CDM. La solicitud de licencia contiene un comprobante de la validez de la CDM (y la relación de confianza), así como una clave para usar cuando se encriptan las claves de contenido en la licencia resultante.
…pero ¿qué hacen realmente los CDM?
Una implementación de EME no proporciona por sí misma una forma de desencriptar medios: simplemente proporciona una API para que una aplicación web interactúe con módulos de desencriptación de contenido.
La especificación de EME no define lo que realmente hacen los CDM, y un CDM puede controlar la decodificación (descompresión) del contenido multimedia, así como la desencriptación. Existen varias opciones posibles para la funcionalidad de la CDM, desde la menos a la más sólida:
- Solo desencriptación, lo que habilita la reproducción con la canalización de contenido multimedia normal, por ejemplo, a través de un elemento
<video>
. - Desencriptación y decodificación, y pasar fotogramas de video al navegador para renderizarlos
- Desencriptación y decodificación, renderización directamente en el hardware (por ejemplo, la GPU)
Existen varias formas de hacer que un CDM esté disponible para una app web:
- Combina un CDM con el navegador.
- Distribuye un CDM por separado.
- Compila un CDM en el sistema operativo.
- Incluye un CDM en el firmware.
- Incorporar un CDM en el hardware
La especificación de EME no define cómo se pone a disposición un CDM, pero, en todos los casos, el navegador es responsable de verificar y exponer el CDM.
EME no exige un sistema de claves en particular. Entre los navegadores para computadoras de escritorio y dispositivos móviles actuales, Chrome admite Widevine y IE11 admite PlayReady.
Cómo obtener una clave de un servidor de licencias
En el uso comercial típico, el contenido se encripta y codifica con un servicio o una herramienta de empaquetado. Una vez que el contenido multimedia encriptado está disponible en línea, un cliente web puede obtener una clave (contenida en una licencia) de un servidor de licencias y usarla para habilitar la desencriptación y la reproducción del contenido.
En el siguiente código (adaptado de los ejemplos de especificaciones), se muestra cómo una aplicación puede seleccionar un sistema de claves adecuado y obtener una clave de un servidor de licencias.
var video = document.querySelector('video');
var config = [{initDataTypes: ['webm'],
videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}]}];
if (!video.mediaKeys) {
navigator.requestMediaKeySystemAccess('org.w3.clearkey',
config).then(
function(keySystemAccess) {
var promise = keySystemAccess.createMediaKeys();
promise.catch(
console.error.bind(console, 'Unable to create MediaKeys')
);
promise.then(
function(createdMediaKeys) {
return video.setMediaKeys(createdMediaKeys);
}
).catch(
console.error.bind(console, 'Unable to set MediaKeys')
);
promise.then(
function(createdMediaKeys) {
var initData = new Uint8Array([...]);
var keySession = createdMediaKeys.createSession();
keySession.addEventListener('message', handleMessage,
false);
return keySession.generateRequest('webm', initData);
}
).catch(
console.error.bind(console,
'Unable to create or initialize key session')
);
}
);
}
function handleMessage(event) {
var keySession = event.target;
var license = new Uint8Array([...]);
keySession.update(license).catch(
console.error.bind(console, 'update() failed')
);
}
Encriptación común
Las soluciones de encriptación común permiten a los proveedores de contenido encriptar y empaquetar su contenido una vez por contenedor o códec y usarlo con una variedad de sistemas de claves, CDM y clientes, es decir, cualquier CDM que admita la encriptación común. Por ejemplo, un video empaquetado con PlayReady se puede reproducir en un navegador con un CDM de Widevine que obtiene una clave de un servidor de licencias de Widevine.
Esto contrasta con las soluciones heredadas que solo funcionarían con una pila vertical completa, incluido un solo cliente que, a menudo, también incluía un entorno de ejecución de la aplicación.
La encriptación común (CENC) es un estándar ISO que define un esquema de protección para ISO BMFF. Se aplica un concepto similar a WebM.
Borrar clave
Aunque EME no define la funcionalidad de DRM, la especificación actualmente exige que todos los navegadores que admiten EME implementen la clave Clear Key. Con este sistema, el contenido multimedia se puede encriptar con una clave y luego reproducirse proporcionando esa clave. Clear Key se puede incorporar en el navegador: no requiere el uso de un módulo de desencriptación independiente.
Si bien es probable que no se use para muchos tipos de contenido comercial, Clear Key es totalmente interoperable en todos los navegadores que admiten EME. También es útil para probar implementaciones de EME y aplicaciones que usan EME sin necesidad de solicitar una clave de contenido a un servidor de licencias. Hay un ejemplo simple de Clear Key en simpl.info/ck. A continuación, se muestra una explicación del código, que es similar a los pasos descritos anteriormente, aunque sin interacción con el servidor de licencias.
// Define a key: hardcoded in this example
// – this corresponds to the key used for encryption
var KEY = new Uint8Array([
0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c
]);
var config = [{
initDataTypes: ['webm'],
videoCapabilities: [{
contentType: 'video/webm; codecs="vp8"'
}]
}];
var video = document.querySelector('video');
video.addEventListener('encrypted', handleEncrypted, false);
navigator.requestMediaKeySystemAccess('org.w3.clearkey', config).then(
function(keySystemAccess) {
return keySystemAccess.createMediaKeys();
}
).then(
function(createdMediaKeys) {
return video.setMediaKeys(createdMediaKeys);
}
).catch(
function(error) {
console.error('Failed to set up MediaKeys', error);
}
);
function handleEncrypted(event) {
var session = video.mediaKeys.createSession();
session.addEventListener('message', handleMessage, false);
session.generateRequest(event.initDataType, event.initData).catch(
function(error) {
console.error('Failed to generate a license request', error);
}
);
}
function handleMessage(event) {
// If you had a license server, you would make an asynchronous XMLHttpRequest
// with event.message as the body. The response from the server, as a
// Uint8Array, would then be passed to session.update().
// Instead, we will generate the license synchronously on the client, using
// the hard-coded KEY at the top.
var license = generateLicense(event.message);
var session = event.target;
session.update(license).catch(
function(error) {
console.error('Failed to update the session', error);
}
);
}
// Convert Uint8Array into base64 using base64url alphabet, without padding.
function toBase64(u8arr) {
return btoa(String.fromCharCode.apply(null, u8arr)).
replace(/\+/g, '-').replace(/\//g, '_').replace(/=*$/, '');
}
// This takes the place of a license server.
// kids is an array of base64-encoded key IDs
// keys is an array of base64-encoded keys
function generateLicense(message) {
// Parse the clearkey license request.
var request = JSON.parse(new TextDecoder().decode(message));
// We only know one key, so there should only be one key ID.
// A real license server could easily serve multiple keys.
console.assert(request.kids.length === 1);
var keyObj = {
kty: 'oct',
alg: 'A128KW',
kid: request.kids[0],
k: toBase64(KEY)
};
return new TextEncoder().encode(JSON.stringify({
keys: [keyObj]
}));
}
Para probar este código, necesitas un video encriptado para reproducirlo. La encriptación de un video para usarlo con Clear Key se puede realizar en WebM según las instrucciones de webm_crypt. También hay servicios comerciales disponibles (al menos para ISO BMFF/MP4) y se están desarrollando otras soluciones.
Tecnología relacionada n.° 1
Extensiones de origen de medios (MSE)
El HTMLMediaElement es una criatura de simple belleza.
Podemos cargar, decodificar y reproducir contenido multimedia simplemente proporcionando una URL de src:
<video src='foo.webm'></video>
La API de Media Source es una extensión de HTMLMediaElement que permite un control más detallado sobre la fuente de contenido multimedia, ya que permite que JavaScript cree transmisiones para la reproducción a partir de "fragmentos" de video. Esto, a su vez, permite técnicas como la transmisión adaptativa y el cambio de tiempo.
¿Por qué el EME es importante para EME? Esto se debe a que, además de distribuir contenido protegido, los proveedores de contenido comercial deben poder adaptar la entrega de contenido a las condiciones de la red y a otros requisitos. Netflix, por ejemplo, cambia de forma dinámica la tasa de bits de transmisión a medida que cambian las condiciones de la red. EME funciona con la reproducción de transmisiones de contenido multimedia que proporciona una implementación de MSE, al igual que lo haría con el contenido multimedia proporcionado a través de un atributo src
.
¿Cómo fragmentar y reproducir contenido multimedia codificado a distintas tasas de bits? Consulta la sección DASH a continuación.
Puedes ver MSE en acción en simpl.info/mse. A los efectos de este ejemplo, un video WebM se divide en cinco segmentos con las APIs de File. En una aplicación de producción, los fragmentos de video se recuperarían a través de Ajax.
Primero, se crea un SourceBuffer:
var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"');
Luego, se “transmite” toda la película a un elemento de video agregando cada fragmento con el método appendBuffer():
reader.onload = function (e) {
sourceBuffer.appendBuffer(new Uint8Array(e.target.result));
if (i === NUM_CHUNKS - 1) {
mediaSource.endOfStream();
} else {
if (video.paused) {
// start playing after first chunk is appended
video.play();
}
readChunk_(++i);
}
};
Obtenga más información sobre ECM en el artículo de HTML5 Rocks.
Tecnología relacionada n.° 2
Transmisión adaptable y dinámica a través de HTTP (DASH)
Ya sea multidispositivo, multiplataforma o móvil, la Web suele experimentarse en condiciones de conectividad cambiante. La entrega dinámica y adaptable es fundamental para lidiar con las restricciones y la variabilidad del ancho de banda en un mundo de múltiples dispositivos.
DASH (también conocido como MPEG-DASH) está diseñado para permitir la mejor entrega de contenido multimedia posible en un mundo inestable, tanto para la transmisión como para la descarga. Varias otras tecnologías hacen algo similar, como HTTP Live Streaming (HLS) de Apple y Smooth Streaming de Microsoft, pero DASH es el único método de transmisión de tasa de bits adaptable a través de HTTP que se basa en un estándar abierto. La DASH ya se utiliza en sitios como YouTube.
¿Qué tiene que ver esto con EME y MSE? Las implementaciones de DASH basadas en ECM pueden analizar un manifiesto, descargar segmentos de video a una tasa de bits adecuada y enviarlos a un elemento de video cuando tenga hambre mediante la infraestructura HTTP existente.
En otras palabras, el DASH permite a los proveedores de contenido comercial realizar transmisiones adaptables de contenido protegido.
La forma de comer DASH hace lo que dice la lata:
- Dinámico: Responde a las condiciones cambiantes.
- Adaptable: Se adapta para proporcionar una tasa de bits de audio o video adecuada.
- Transmisión: Permite la transmisión y la descarga.
- HTTP: Permite la entrega de contenido con la ventaja de HTTP, sin las desventajas de un servidor de transmisión tradicional.
La BBC comenzó a proporcionar transmisiones de prueba con DASH:
En síntesis:
- El contenido multimedia se codifica con diferentes tasas de bits.
- Los diferentes archivos de tasa de bits están disponibles desde un servidor HTTP.
- Una app web cliente elige qué tasa de bits recuperar y reproducir con DASH.
Como parte del proceso de segmentación de videos, se compila de forma programática un manifiesto XML conocido como descripción de presentación multimedia (MPD). Aquí se describen los conjuntos de adaptación y las representaciones, con duraciones y URLs. Un MPD se ve de la siguiente manera:
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" mediaPresentationDuration="PT0H3M1.63S" minBufferTime="PT1.5S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011"
type="static">
<Period duration="PT0H3M1.63S" start="PT0S">
<AdaptationSet>
<ContentComponent contentType="video" id="1" />
<Representation bandwidth="4190760" codecs="avc1.640028" height="1080" id="1" mimeType="video/mp4" width="1920">
<BaseURL>car-20120827-89.mp4</BaseURL>
<SegmentBase indexRange="674-1149">
<Initialization range="0-673" />
</SegmentBase>
</Representation>
<Representation bandwidth="2073921" codecs="avc1.4d401f" height="720" id="2" mimeType="video/mp4" width="1280">
<BaseURL>car-20120827-88.mp4</BaseURL>
<SegmentBase indexRange="708-1183">
<Initialization range="0-707" />
</SegmentBase>
</Representation>
…
</AdaptationSet>
</Period>
</MPD>
(Este XML se toma de el archivo .mpd que se usa para el reproductor de demostración de DASH de YouTube).
Según la especificación de DASH, en teoría, un archivo MPD podría usarse como src
para un video. Sin embargo, para brindar más flexibilidad a los desarrolladores web, los proveedores de navegadores optaron por dejar la compatibilidad con DASH a las bibliotecas de JavaScript que usan MSE, como dash.js. La implementación de DASH en JavaScript permite que el algoritmo de adaptación evolucione sin requerir actualizaciones del navegador. El uso de MSE también permite experimentar con formatos de manifiesto y mecanismos de entrega alternativos sin necesidad de realizar cambios en el navegador. El reproductor Shaka de Google implementa un cliente DASH con compatibilidad con EME.
Mozilla Developer Network tiene instrucciones para usar herramientas de WebM y FFmpeg para segmentar videos y compilar un MPD.
Conclusión
El uso de la Web para publicar videos y audios pagados crece a un ritmo enorme. Al parecer, todos los dispositivos nuevos, ya sean tablets, consolas de juegos, TVs conectadas o decodificadores, pueden transmitir contenido multimedia de los principales proveedores de contenido a través de HTTP. Más del 85% de los navegadores para dispositivos móviles y computadoras de escritorio ahora admiten <video>
y <audio>
, y Cisco estima que los videos representarán entre el 80 y el 90% del tráfico global de Internet de los consumidores para 2017. En este contexto, es probable que la compatibilidad de los navegadores con la distribución de contenido protegido sea cada vez más importante, ya que los proveedores de navegadores restringen la compatibilidad con las APIs de las que dependen la mayoría de los complementos multimedia.
Lecturas adicionales
Especificaciones y estándares
- Especificación de EME: Versión más reciente del borrador del editor<
- Encriptación común (CENC)
- Extensiones de fuente de contenido multimedia
- Estándar DASH (sí, es un PDF)
- Acerca del estándar DASH
Artículos
- Seminario en línea de DTG (parcialmente obsoleto)
- What is EME?, de Henri Sivonen
- Artículo sobre las extensiones de fuente de medios de HTML5 Rocks
- Transmisiones de prueba de MPEG-DASH: entrada de blog de I+D de la BBC