¿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.

EME está diseñado para permitir que se usen la misma app y los mismos archivos encriptados en cualquier navegador, independientemente del sistema de protección subyacente. El primero es posible gracias a las APIs y el flujo estandarizados, mientras que el segundo es posible gracias al concepto de Criptografía común.

EME es una extensión de la especificación HTMLMediaElement; de ahí su 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. De la especificación de EME:

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

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

Esta especificación no define un sistema de protección de contenido ni de administración de derechos digitales. En cambio, define una API común que se puede usar para descubrir, seleccionar e interactuar con esos sistemas, 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 Clear Key como un modelo de referencia común.

La API común admite un conjunto simple de capacidades de encriptación de contenido, lo que deja las funciones de la aplicación, como la autenticación y la autorización, a los autores de la página. Esto se logra mediante la exigencia de que la página medie los mensajes específicos del sistema de protección de contenido en lugar de suponer una 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í, además de Clear Key (obtén más información al respecto a continuación).
  • Módulo de desencriptación de contenido (CDM): Es un mecanismo de hardware o software del cliente que habilita la reproducción de contenido multimedia encriptado. Al igual que con los sistemas de claves, la 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 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 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 ejemplo de código que aparece 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 admite el navegador para el contenido no encriptado. 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 el códec elegido, es posible que sea demasiado tarde para cambiar sin interrumpir la reproducción.

El flujo recomendado es negociar MediaKeys primero, con MediaKeysSystemAccess.getConfiguration() para conocer la configuración negociada.

Si solo hay un formato o códec para elegir, no es necesario usar getConfiguration(). Sin embargo, sigue siendo preferible configurar MediaKeys primero. La única razón para esperar el evento encriptado es si no hay forma de saber si el contenido está encriptado o no, pero en la práctica eso 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 a continuación para ver cómo sucede esto) y activa un evento encriptado con metadatos (initData) obtenidos del contenido multimedia 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 obtiene una URL de 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 esta, específicamente para crear sesiones de claves, que se usan para obtener claves de un servidor de licencias.

    2. Una vez creado el objeto MediaKeys, asígnale el 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 un MediaKeySession, que representa la vida útil de una licencia y sus claves.

  5. La app genera una solicitud de licencia pasando 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 (a través de XHR, por ejemplo).

  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 con las claves de la licencia. Se puede usar una clave válida desde cualquier sesión dentro de MediaKeys asociada 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.

¿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 estará en un formato como ISO BMFF o WebM. Para ISO BMFF, esto significa metadatos de encabezado, que se denominan cuadro de información del esquema de protección. WebM usa el elemento Matroska ContentEncryption, con algunas incorporaciones 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 toda la comunicación en este proceso es opaca para el navegador y la aplicación: solo el CDM y el servidor de licencias pueden comprender los mensajes, aunque la capa de la aplicación puede ver qué tipo de mensaje envía el CDM. La solicitud de licencia contiene una prueba 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 en realidad los CDM?

Una implementación de EME no proporciona por sí misma una forma de desencriptar contenido multimedia; solo 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 controlar la decodificación (descompresión) del contenido multimedia, así como la desencriptación. De menor a mayor solidez, existen varias opciones posibles para la funcionalidad de CDM:

  • 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 una herramienta o un servicio de empaquetado. Una vez que los medios encriptados están disponibles en línea, un cliente web puede obtener una clave (incluida en una licencia) de un servidor de licencias y usar la clave 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 adecuado 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 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 funcionan con una pila vertical completa, incluido un cliente único que a menudo también incluye 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 Clear Key. Con este sistema, el contenido multimedia se puede encriptar con una clave y, luego, reproducirse con solo proporcionar esa clave. Borrar la clave se puede integrar 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 paralelo a los pasos descritos anteriormente, 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],
    }),
  );
}

Para probar este código, necesitas un video encriptado para reproducir. La encriptación de un video para su uso 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.

HTMLMediaElement es una criatura de belleza simple.

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 hora.

¿Por qué la MSE es importante para la 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 se fragmenta y reproduce contenido multimedia codificado con diferentes tasas de bits? Consulta la sección de DASH a continuación.

Puedes ver MSE en acción en simpl.info/mse. Para los fines 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);
  }
};

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

Ya sea multidispositivo, multiplataforma o para dispositivos móviles, la Web a menudo se experimenta en condiciones de conectividad cambiante. La entrega dinámica y adaptativa es fundamental para hacer frente a las limitaciones de ancho de banda y la variabilidad en el mundo multidispositivo.

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. Sitios como YouTube ya usan DASH.

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

En otras palabras, DASH permite que los proveedores de contenido comercial realicen transmisiones adaptativas de contenido protegido.

La forma de comer DASH hace lo que dice la lata:

  • Dinámico: Responde a las condiciones cambiantes.
  • Adaptativa: 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:

El contenido multimedia se codifica varias veces con diferentes tasas de bits. Cada codificación se llama representación. Estos se dividen en varios segmentos de contenido multimedia. El cliente reproduce un programa solicitando segmentos, en orden, desde una representación a través de HTTP. Las representaciones se pueden agrupar en conjuntos de adaptaciones de representaciones que contienen 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 de esa representación. El contenido está codificado de tal manera que el cliente pueda realizar este cambio con facilidad. Además de varios segmentos multimedia, una representación suele tener también un segmento de inicialización. Se puede considerar como un encabezado que contiene información sobre la codificación, los tamaños de fotogramas, etcétera. Un cliente debe obtenerlo para una representación determinada antes de consumir segmentos de contenido multimedia de esa representación.

En síntesis:

  1. El contenido multimedia se codifica con diferentes 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 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 se podría usar como src para un video. Sin embargo, para brindar más flexibilidad a los desarrolladores web, los proveedores de navegadores eligieron dejar la compatibilidad con DASH hasta las bibliotecas de JavaScript que usan 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 requerir cambios en el navegador. El reproductor Shaka de Google implementa un cliente DASH con compatibilidad con EME.

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

Conclusión

El uso de la Web para publicar contenido de audio y video con costo está creciendo 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 computadoras y dispositivos móviles ahora admiten <video> y <audio>, y Cisco estima que, en 2017, el video representará entre el 80 y el 90 por ciento del tráfico de Internet de los consumidores. En este contexto, es probable que la compatibilidad del navegador con la distribución de contenido protegido sea cada vez más importante, ya que los proveedores de navegadores recortan 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 Codificación común (CENC) Extensiones de fuente de medios: Versión más reciente del borrador del editor Estándar DASH (sí, es un PDF) Descripción general del estándar DASH

Artículos

Seminario en línea de DTG (parcialmente obsoleto) What is EME?, de Henri Sivonen Media Source Extensions primer MPEG-DASH Test Streams: entrada de blog de I+D de la BBC

Demostraciones

Demo de Clear Key: simpl.info/ck Demo de extensiones de fuente de medios (MSE) El reproductor Shaka de Google implementa un cliente DASH con compatibilidad con EME.

Comentarios