¿Qué es EME?

Las extensiones de medios encriptados 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.

El EME está diseñado para permitir que se usen la misma app y los mismos archivos encriptados en cualquier navegador, sin importar el sistema de protección subyacente. La primera es posible gracias al flujo y las APIs estandarizadas, mientras que la última es posible gracias al concepto de encriptación común.

EME es una extensión de la especificación HTMLMediaElement, de ahí su nombre. Si el navegador incluye una "extensión", significa que la compatibilidad del navegador con EME es opcional. Si el navegador no admite contenido multimedia encriptado, no podrá reproducir ese tipo de contenido, pero el EME no es obligatorio para cumplir con las especificaciones de HTML. A partir de la especificación de EME:

Esta propuesta extiende HTMLMediaElement, lo que proporciona APIs para controlar la reproducción de contenido protegido.

La API admite casos de uso que van desde una simple desencriptación de clave clara hasta videos de alto valor (con una implementación de usuario-agente adecuada). La aplicación controla el intercambio de licencia/clave, lo que facilita el desarrollo de aplicaciones de reproducción sólidas que admiten una variedad de tecnologías de desencriptación de contenido y protección.

Esta especificación no define una protección de contenido ni un sistema de administración de derechos digitales. En su lugar, define una API común que se puede usar para descubrir esos sistemas, seleccionarlos e interactuar con ellos, así como con sistemas de encriptación de contenido más simples. No se requiere la implementación de la administración de derechos digitales para cumplir con esta especificación: solo se debe implementar el sistema de claves claras como modelo de referencia común.

La API común admite un conjunto simple de capacidades de encriptación de contenido, lo que deja a los autores de páginas funciones como la autenticación y la autorización. Para lograr esto, se debe solicitar que la página actúe como intermediario de los mensajes específicos del sistema de protección de contenido en lugar de asumir la comunicación fuera de banda entre el sistema de encriptación y una licencia o algún otro servidor.

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í, a excepción de Clear Key (obtén más información sobre este tema 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 Key Systems, EME no define ningún CDM, pero proporciona una interfaz para que las aplicaciones interactúen con los CDM que están disponibles.
  • Servidor de licencias (clave): Interactúa con un CDM a fin de proporcionar claves para desencriptar el contenido multimedia. La negociación con el servidor de licencias es responsabilidad de la aplicación.
  • Servicio de empaquetado: Codifica y encripta 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 para habilitar la desencriptación, pero la identidad del usuario y la autenticación no son parte de EME. La recuperación de claves para habilitar la reproducción de contenido multimedia ocurre después de que se autentica a un usuario (de manera opcional). 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 la identidad y los privilegios del usuario.

¿Cómo funciona EME?

A continuación, se muestra cómo interactúan los componentes de EME, lo que corresponde al ejemplo de código a continuación:

Si hay varios formatos o códecs disponibles, se pueden usar MediaSource.isTypeSupported() o HTMLMediaElement.canPlayType() para seleccionar el correcto. Sin embargo, es posible que el CDM solo admita un subconjunto de lo que el navegador admite para el contenido sin encriptar. Es mejor negociar una configuración de MediaKeys antes de seleccionar un formato y un códec. Si la aplicación espera el evento encriptado, pero MediaKeys muestra que no puede controlar el formato o códec elegido, es posible que sea demasiado tarde para realizar el cambio sin interrumpir la reproducción.

El flujo recomendado es negociar MediaKeys primero mediante MediaKeysSystemAccess.getConfiguration() para descubrir la configuración negociada.

Si solo hay un formato o códec para elegir, no es necesario usar getConfiguration(). Sin embargo, es preferible configurar MediaKeys primero. La única razón para esperar el evento encriptado es que no hay forma de saber si el contenido está encriptado o no, pero en la práctica es poco probable.

  1. Una aplicación web intenta reproducir audio o video que tiene una o más transmisiones encriptadas.
  2. El navegador reconoce que el contenido multimedia está encriptado (consulta el cuadro que aparece a continuación para saber cómo sucede) y activa un evento encriptado con metadatos (initData) obtenidos de los medios sobre la encriptación.
  3. La aplicación controla el evento encriptado:

    1. Si no se asoció ningún objeto MediaKeys con el elemento multimedia, primero selecciona un sistema de claves disponible con navigator.requestMediaKeySystemAccess() para verificar qué sistemas de claves están disponibles y, luego, crea un objeto MediaKeys para un sistema de claves disponible a través de un objeto MediaKeySystemAccess. Ten en cuenta que la inicialización del objeto MediaKeys debe ocurrir antes del primer evento encriptado. La app realiza la obtención de una URL del servidor de licencias, independientemente de la selección de un sistema de claves disponible. Un objeto MediaKeys 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 este, en particular para crear sesiones de claves que se usan con el objetivo de obtener claves de un servidor de licencias.

    2. Una vez que se haya creado el objeto MediaKeys, asígnalo al elemento multimedia: setMediaKeys() asocia el objeto MediaKeys con un HTMLMediaElement, de modo que sus claves se puedan usar durante la reproducción, es decir, durante la decodificación.

  4. La app crea una MediaKeySession llamando a createSession() en MediaKeys. Esto crea una MediaKeySession, que representa la vida útil de una licencia y sus claves.

  5. Para generar una solicitud de licencia, la app pasa los datos multimedia obtenidos en el controlador encriptado al CDM llamando a generateRequest() en MediaKeySession.

  6. El CDM activa un evento de mensaje: una solicitud para adquirir una clave de un servidor de licencias.

  7. El objeto MediaKeySession recibe el evento de mensaje y la aplicación envía un mensaje al servidor de licencias (por ejemplo, a través de XHR).

  8. La aplicación recibe una respuesta del servidor de licencias y pasa los datos al CDM con el método update() de MediaKeySession.

  9. El CDM desencripta el contenido multimedia usando las claves de la licencia. Se puede usar una clave válida de cualquier sesión dentro de las MediaKeys asociadas con el elemento multimedia. El CDM accederá a la clave y la política, indexadas por ID de clave.

Se reanudará la reproducción de contenido multimedia.

¿Cómo sabe el navegador que el contenido multimedia está encriptado?

Esta información se encuentra en los metadatos del archivo del contenedor multimedia, que tendrá un formato como ISO BMFF o WebM. Para ISO BMFF, significa metadatos de encabezado, llamados cuadro de información del esquema de protección. WebM usa el elemento Matroska ContentEncryption, con algunas adiciones específicas de WebM. Se proporcionan lineamientos para cada contenedor en un registro específico de EME.

Ten en cuenta que puede haber varios mensajes entre el CDM y el servidor de licencias, y que todas las comunicaciones de este proceso son opacas para el navegador y la aplicación: solo el CDM y el servidor de licencias pueden entender los mensajes, aunque la capa de la app puede ver qué tipo de mensaje envía el CDM. La solicitud de licencia contiene una prueba de la validez del CDM (y la relación de confianza), así como una clave para usar cuando se encriptan las claves de contenido de la licencia resultante.

Pero ¿qué hacen realmente los CDM?

Una implementación de EME no proporciona por sí misma una forma de desencriptar contenido multimedia: simplemente proporciona una API para que una aplicación web interactúe con los módulos de desencriptación de contenido.

La especificación de EME no define lo que realmente hacen los CDM, y un CDM puede manejar la decodificación (descompresión) de contenido multimedia y la desencriptación. De la menos a la más sólida, existen varias opciones posibles para la funcionalidad de CDM:

  • Solo desencriptación, habilita la reproducción mediante la canalización de contenido multimedia normal, por ejemplo, a través de un elemento de <video>.
  • Desencriptación y decodificación, que pasa fotogramas del video al navegador para su renderización.
  • Desencriptación y decodificación, que se renderizan directamente en el hardware (por ejemplo, la GPU)

Existen varias formas de hacer que un CDM esté disponible para una app web:

  • Empaqueta un CDM con el navegador.
  • Distribuye un CDM por separado.
  • Compilar 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 la forma en que un CDM se pone a disposición, pero en todos los casos el navegador es responsable de examinar y exponer el CDM.

EME no exige un sistema de claves específico. Entre los navegadores actuales para computadoras de escritorio y dispositivos móviles, Chrome es compatible con Widevine y, además, IE11 es compatible con PlayReady.

Obtén una clave de un servidor de licencias

En el uso comercial típico, el contenido se encriptará y se codificará con un servicio o una herramienta de empaquetado. Una vez que el medio 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 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 apropiado y obtener una clave de un servidor de licencias.

    var video = document.querySelector('video');

    var config = [{initDataTypes: ['webm'],
      videoCapabilities: [{contentType: 'video/webm; codecs="vp09.00.10.08"'}]}];

    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 comunes permiten a los proveedores de contenido encriptar y empaquetar su contenido una vez por contenedor/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 podría reproducirse en un navegador con un CDM de Widevine que obtenga 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 único 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, actualmente, la especificación exige que todos los navegadores compatibles con EME implementen la función Borrar clave. Con este sistema, el contenido multimedia se puede encriptar con una clave y, luego, reproducirlo con solo proporcionar esa clave. Clear Key se puede integrar en el navegador: no requiere el uso de un módulo de desencriptación independiente.

Si bien es poco probable que se use para muchos tipos de contenido comercial, Clear Key es completamente interoperable en todos los navegadores compatibles con EME. También es útil para probar implementaciones de EME y aplicaciones que usan EME, sin la necesidad de solicitar una clave de contenido de 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 paralela a los pasos descritos antes, aunque sin interacción del 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],
    }),
  );
}

Si quieres probar este código, necesitas un video encriptado para reproducirlo. Para WebM, se puede encriptar un video para usarlo con Clear Key según las instrucciones de webm_crypt. También están disponibles los servicios comerciales (al menos para ISO BMFF/MP4) y otras soluciones en desarrollo.

El HTMLMediaElement es un elemento de belleza simple.

Podemos cargar, decodificar y reproducir contenido multimedia con solo proporcionar una URL src:

<video src="foo.webm"></video>

La API de fuente de medios es una extensión de HTMLMediaElement que permite un control más detallado sobre la fuente del contenido multimedia, ya que permite que JavaScript cree transmisiones para reproducir a partir de "fragmentos" de video. Esto, a su vez, permite técnicas como la transmisión adaptable y el cambio de tiempo.

¿Por qué el EME es importante para EME? Porque, 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 otros requisitos. Por ejemplo, Netflix cambia de forma dinámica la tasa de bits de la transmisión a medida que cambian las condiciones de la red. El EME funciona con la reproducción de transmisiones de contenido multimedia proporcionadas por una implementación de EME, al igual que 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 de DASH que figura a continuación.

Puedes ver ECM en acción en simpl.info/mse. Para los fines de este ejemplo, un video de WebM se divide en cinco partes con las APIs de File. En una aplicación de producción, los fragmentos de video se recuperan a través de AJAX.

Primero, se crea un SourceBuffer:

var sourceBuffer = mediaSource.addSourceBuffer(
  'video/webm; codecs="vorbis,vp8"',
);

Luego, la película completa se "transmite" a un elemento de video agregando cada fragmento con el método attachBuffer():

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);
  }
};

Obtén más información sobre ECM en el manual de MSE.

Multidispositivo, multiplataforma, móvil: sin importar cómo lo llames, la Web a menudo se experimenta en condiciones de conectividad cambiante. La entrega dinámica y adaptable es crucial para hacer frente a 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 las descargas. 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. Algunos sitios como YouTube ya utilizan la forma de comer DASH.

¿Qué tiene que ver esto con EME y EME? Las implementaciones de DASH basadas en MSE pueden analizar un manifiesto, descargar segmentos de video a una tasa de bits adecuada y enviarlos a un elemento de video cuando le resulte necesario hacerlo, mediante la infraestructura HTTP existente.

En otras palabras, DASH permite a los proveedores de contenido comercial realizar una transmisión adaptable de contenido protegido.

La forma de comer DASH hace lo que dice en 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 transmitir y descargar.
  • HTTP: Habilita 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 mediante DASH:

El contenido multimedia se codifica varias veces con distintas tasas de bits. Cada codificación se denomina Representación. que se dividen en varios segmentos de medios. El cliente juega un programa solicitando segmentos, en orden, de una representación a través de HTTP. Las representaciones se pueden agrupar en conjuntos de adaptación de representaciones que tengan contenido equivalente. Si el cliente desea cambiar la tasa de bits, puede elegir una alternativa del conjunto de adaptación actual y comenzar a solicitar segmentos desde esa representación. El contenido está codificado de tal manera que el cambio sea sencillo para el cliente. Además de algunos segmentos multimedia, una representación suele tener un segmento de inicialización. Se puede considerar como un encabezado que contiene información sobre la codificación, los tamaños de los fotogramas, etc. Un cliente necesita obtener esto para una representación dada antes de consumir los segmentos de contenido multimedia de esa representación.

En síntesis:

  1. El contenido multimedia está codificado con distintas tasas de bits.
  2. Los diferentes archivos de tasa de bits están disponibles desde un servidor HTTP.
  3. 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 manera programática un manifiesto XML conocido como descripción de presentación multimedia (MPD). Esto describe conjuntos de adaptación y representaciones, con duraciones y URLs. Una 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 del archivo .mpd que se usa para el reproductor de demostración de YouTube DASH).

Según la especificación de DASH, en teoría se podría usar un archivo MPD como src de un video. Sin embargo, para brindar más flexibilidad a los desarrolladores web, los proveedores de navegadores eligieron dejar la compatibilidad de DASH en las bibliotecas de JavaScript con ECM, 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 ECM también permite experimentar con formatos de manifiesto alternativos y mecanismos de entrega sin necesidad de realizar cambios en el navegador. El Shaka Player de Google implementa un cliente de DASH compatible con EME.

Mozilla Developer Network tiene instrucciones sobre cómo usar las herramientas de WebM y FFmpeg para segmentar videos y compilar una MPD.

Conclusión

El uso de la Web para entregar videos y audio pagos está creciendo a un gran ritmo. Parece que cada dispositivo nuevo, ya sea una tablet, una consola de juegos, una TV conectada o un decodificador, puede 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 del consumidor para el 2017. En este contexto, es probable que la compatibilidad de los navegadores con la distribución de contenido protegido sea cada vez más significativa, 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: Última versión del Editor Encriptación común (CENC) Extensiones de fuente de medios: Estándar DASH del borrador más reciente del editor (sí, es un PDF) Descripción general del estándar DASH

Artículos

Seminario en línea de DTG (parcialmente obsoleto) ¿Qué es EME?, de Henri Sivonen, Media Source Extensions primer MPEG-DASH Test Streams: Entrada de blog de BBC R&D

Demostraciones

Demostración de clave clara: simpl.info/ck Demostración de extensiones de fuente de medios (MSE) Shaka Player de Google implementa un cliente de DASH compatible con EME

Comentarios