O que é o EME?

As extensões de mídia criptografada oferecem uma API que permite que aplicativos da Web interajam com sistemas de proteção de conteúdo para permitir a reprodução de áudio e vídeo criptografados.

O EME foi projetado para permitir que o mesmo app e arquivos criptografados sejam usados em qualquer navegador, independentemente do sistema de proteção. O primeiro é possível com as APIs e o fluxo padronizados, enquanto o segundo é possível com o conceito de Criptografia comum.

O EME é uma extensão da especificação HTMLMediaElement, daí o nome. Ser uma "extensão" significa que o suporte do navegador para o EME é opcional: se um navegador não oferece suporte a mídia criptografada, ele não poderá reproduzir mídia criptografada, mas o EME não é necessário para conformidade com as especificações HTML. Na especificação da EME (em inglês):

Essa proposta estende o HTMLMediaElement oferecendo APIs para controlar a reprodução de conteúdo protegido.

A API oferece suporte a casos de uso que variam de descriptografia de chave clara simples a vídeo de alto valor (considerando uma implementação de user agent adequada). A troca de licença/chave é controlada pelo aplicativo, facilitando o desenvolvimento de aplicativos de reprodução robustos que oferecem suporte a várias tecnologias de descriptografia e proteção de conteúdo.

Esta especificação não define um sistema de proteção de conteúdo ou de Gerenciamento de direitos digitais. Em vez disso, ele define uma API comum que pode ser usada para descobrir, selecionar e interagir com esses sistemas, bem como com sistemas de criptografia de conteúdo mais simples. A implementação do gerenciamento de direitos digitais não é necessária para a conformidade com essa especificação: apenas o sistema Clear Key é necessário para ser implementado como uma base comum.

A API comum oferece suporte a um conjunto simples de recursos de criptografia de conteúdo, deixando as funções do aplicativo, como autenticação e autorização, para os autores da página. Isso é possível exigindo que as mensagens específicas do sistema de proteção de conteúdo sejam mediadas pela página, em vez de assumir a comunicação fora da banda entre o sistema de criptografia e uma licença ou outro servidor.

As implementações de EME usam os seguintes componentes externos:

  • Sistema de chaves:um mecanismo de proteção de conteúdo (DRM, na sigla em inglês). O EME não define os sistemas de chaves, exceto a chave clara (mais informações abaixo).
  • Módulo de descriptografia de conteúdo (CDM, na sigla em inglês): um mecanismo de software ou hardware do lado do cliente que permite a reprodução de mídia criptografada. Assim como os sistemas de chaves, o EME não define nenhum CDM, mas fornece uma interface para que os aplicativos interajam com os CDMs disponíveis.
  • Servidor de licença (chave):interage com um CDM para fornecer chaves para descriptografar mídia. A negociação com o servidor de licença é responsabilidade do aplicativo.
  • Serviço de empacotamento:codifica e criptografa mídia para distribuição/consumo.

Um aplicativo que usa EME interage com um servidor de licença para receber chaves e ativar a descriptografia, mas a identidade e a autenticação do usuário não fazem parte do EME. A recuperação de chaves para ativar a reprodução de mídia acontece após (opcionalmente) a autenticação de um usuário. Serviços como a Netflix precisam autenticar os usuários no aplicativo da Web. Quando um usuário faz login no aplicativo, ele determina a identidade e os privilégios do usuário.

Como funciona o EME?

Confira como os componentes do EME interagem, correspondendo ao exemplo de código abaixo:

Se vários formatos ou codecs estiverem disponíveis, MediaSource.isTypeSupported() ou HTMLMediaElement.canPlayType() pode ser usado para selecionar o correto. No entanto, o CDM pode oferecer suporte apenas a um subconjunto do que o navegador oferece suporte para conteúdo não criptografado. É melhor negociar uma configuração do MediaKeys antes de selecionar um formato e codec. Se o aplicativo esperar pelo evento criptografado e o MediaKeys mostrar que não pode processar o formato/codec escolhido, talvez seja tarde demais para mudar sem interromper a reprodução.

O fluxo recomendado é negociar primeiro as MediaKeys, usando MediaKeysSystemAccess.getConfiguration() para descobrir a configuração negociada.

Se houver apenas um formato/codec para escolher, não será necessário usar getConfiguration(). No entanto, ainda é preferível configurar o MediaKeys primeiro. O único motivo para aguardar o evento criptografado é se não há como saber se o conteúdo está criptografado ou não, mas isso não é provável, na prática.

  1. Um aplicativo da Web tenta reproduzir áudio ou vídeo com um ou mais streams criptografados.
  2. O navegador reconhece que a mídia está criptografada (confira a caixa abaixo para saber como isso acontece) e dispara um evento criptografado com metadados (initData) recebidos da mídia sobre a criptografia.
  3. O aplicativo processa o evento criptografado:

    1. Se nenhum objeto MediaKeys tiver sido associado ao elemento de mídia, primeiro selecione um sistema de chaves disponível usando navigator.requestMediaKeySystemAccess() para verificar quais sistemas de chaves estão disponíveis e, em seguida, crie um objeto MediaKeys para um sistema de chaves disponível usando um objeto MediaKeySystemAccess. A inicialização do objeto MediaKeys precisa acontecer antes do primeiro evento criptografado. A obtenção de um URL do servidor de licença é feita pelo app independente da seleção de um sistema de chaves disponível. Um objeto MediaKeys representa todas as chaves disponíveis para descriptografar a mídia de um elemento de áudio ou vídeo. Ele representa uma instância do CDM e fornece acesso ao CDM, especificamente para criar sessões de chave, que são usadas para receber chaves de um servidor de licenças.

    2. Depois que o objeto MediaKeys for criado, atribua-o ao elemento de mídia: setMediaKeys() associa o objeto MediaKeys a um HTMLMediaElement, para que as chaves possam ser usadas durante a reprodução, ou seja, durante a decodificação.

  4. O app cria uma MediaKeySession chamando createSession() no MediaKeys. Isso cria uma MediaKeySession, que representa a duração de uma licença e as chaves dela.

  5. O app gera uma solicitação de licença transmitindo os dados de mídia recebidos no gerenciador criptografado para o CDM, chamando generateRequest() na MediaKeySession.

  6. O CDM dispara um evento de mensagem: uma solicitação para adquirir uma chave de um servidor de licença.

  7. O objeto MediaKeySession recebe o evento de mensagem, e o aplicativo envia uma mensagem para o servidor de licença (via XHR, por exemplo).

  8. O aplicativo recebe uma resposta do servidor de licença e transmite os dados ao CDM usando o método update() da MediaKeySession.

  9. O CDM descriptografa a mídia usando as chaves na licença. Uma chave válida pode ser usada, de qualquer sessão nas MediaKeys associadas ao elemento de mídia. O CDM vai acessar a chave e a política, indexadas pelo ID da chave.

A reprodução de mídia será retomada.

Como o navegador sabe que a mídia está criptografada?

Essas informações estão nos metadados do arquivo do contêiner de mídia, que estará em um formato como ISO BMFF ou WebM. Para o ISO BMFF, isso significa metadados do cabeçalho, chamado de caixa de informações do esquema de proteção. O WebM usa o elemento ContentEncryption do Matroska, com algumas adições específicas do WebM. As diretrizes são fornecidas para cada contêiner em um registro específico do EME.

Pode haver várias mensagens entre o CDM e o servidor de licença, e toda a comunicação nesse processo é opaca para o navegador e o aplicativo: as mensagens são entendidas apenas pelo CDM e pelo servidor de licença, embora a camada do app possa ver que tipo de mensagem o CDM está enviando. A solicitação de licença contém a prova da validade (e da relação de confiança) do CDM, bem como uma chave para usar ao criptografar as chaves de conteúdo na licença resultante.

Mas o que os CDMs realmente fazem?

Uma implementação de EME, por si só, não oferece uma maneira de descriptografar mídia: ela simplesmente fornece uma API para um aplicativo da Web interagir com os módulos de descriptografia de conteúdo.

O que os CDMs realmente fazem não é definido pela especificação EME, e um CDM pode processar a decodificação (descompressão) de mídia, bem como a descriptografia. Do menos ao mais robusto, há várias opções possíveis para a funcionalidade do CDM:

  • Apenas descriptografia, permitindo a reprodução usando o pipeline de mídia normal, por exemplo, por um elemento <video>.
  • Descriptografia e decodificação, transmitindo frames de vídeo para o navegador para renderização.
  • Descriptografia e decodificação, renderização diretamente no hardware (por exemplo, a GPU).

Há várias maneiras de disponibilizar um CDM para um app da Web:

  • Empacotar um CDM com o navegador.
  • Distribua um CDM separadamente.
  • Criar um CDM no sistema operacional.
  • Inclua um CDM no firmware.
  • Incorpore um CDM no hardware.

A forma como um CDM é disponibilizado não é definida pela especificação EME, mas, em todos os casos, o navegador é responsável por verificar e expor o CDM.

O EME não exige um sistema de chave específico. Entre os navegadores para computador e dispositivos móveis atuais, o Chrome oferece suporte ao Widevine, e o IE11 oferece suporte ao PlayReady.

Como receber uma chave de um servidor de licença

No uso comercial típico, o conteúdo é criptografado e codificado usando um serviço ou ferramenta de empacotamento. Depois que a mídia criptografada é disponibilizada on-line, um cliente da Web pode conseguir uma chave (contida em uma licença) de um servidor de licença e usá-la para ativar a descriptografia e a reprodução do conteúdo.

O código a seguir (adaptado dos exemplos de especificações) mostra como um app pode selecionar um sistema de chaves adequado e receber uma chave de um servidor de licenças.

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

Criptografia comum

As soluções de criptografia comum permitem que os provedores de conteúdo criptografem e empacotem o conteúdo uma vez por contêiner/codec e o usem com vários sistemas de chaves, CDMs e clientes, ou seja, qualquer CDM compatível com a criptografia comum. Por exemplo, um vídeo empacotado com o Playready pode ser reproduzido em um navegador usando um CDM do Widevine que recebe uma chave de um servidor de licença do Widevine.

Isso contrasta com as soluções legadas que só funcionam com uma pilha vertical completa, incluindo um único cliente que geralmente também inclui um ambiente de execução do aplicativo.

A Criptografia comum (CENC) é um padrão ISO que define um esquema de proteção para ISO BMFF. Um conceito semelhante se aplica ao WebM.

Remover chave

Embora o EME não defina a funcionalidade DRM, a especificação atualmente exige que todos os navegadores com suporte a EME implementem a chave clara. Com esse sistema, a mídia pode ser criptografada com uma chave e reproduzida simplesmente fornecendo essa chave. A chave clara pode ser integrada ao navegador: ela não exige o uso de um módulo de descriptografia separado.

Embora não seja provável que seja usado para muitos tipos de conteúdo comercial, o Clear Key é totalmente interoperável em todos os navegadores compatíveis com EME. Ele também é útil para testar implementações de EME e aplicativos que usam EME, sem a necessidade de solicitar uma chave de conteúdo de um servidor de licença. Há um exemplo simples de chave clara em simpl.info/ck. Confira abaixo um tutorial do código, que é semelhante às etapas descritas acima, mas sem interação com o servidor de licença.

// 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 testar esse código, é necessário ter um vídeo criptografado para assistir. É possível criptografar um vídeo para uso com a chave clara para WebM conforme as instruções do webm_crypt. Serviços comerciais também estão disponíveis (pelo menos para ISO BMFF/MP4), e outras soluções estão sendo desenvolvidas.

O HTMLMediaElement é uma criatura de beleza simples.

É possível carregar, decodificar e reproduzir mídias simplesmente fornecendo um URL de src:

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

A API Media Source é uma extensão do HTMLMediaElement que permite um controle mais refinado sobre a origem da mídia, permitindo que o JavaScript crie fluxos para reprodução de "pedaços" de vídeo. Isso, por sua vez, permite técnicas como streaming adaptativo e mudança de tempo.

Por que a MSE é importante para a EME? Porque, além de distribuir conteúdo protegido, os provedores de conteúdo comercial precisam adaptar a entrega de conteúdo às condições de rede e a outros requisitos. A Netflix, por exemplo, muda dinamicamente a taxa de bits do streaming conforme as condições da rede mudam. O EME funciona com a reprodução de streams de mídia fornecidos por uma implementação de MSE, assim como faria com mídia fornecida por um atributo src.

Como dividir e reproduzir mídia codificada em diferentes taxas de bits? Consulte a seção DASH abaixo.

Confira a MSE em ação em simpl.info/mse. Para este exemplo, um vídeo WebM é dividido em cinco partes usando as APIs File. Em um aplicativo de produção, blocos de vídeo seriam recuperados via AJAX.

Primeiro, um SourceBuffer é criado:

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

O filme inteiro é "streamado" para um elemento de vídeo anexando cada bloco usando o 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);
  }
};

Saiba mais sobre a MSE no documento introdutório sobre MSE.

Dispositivos e plataformas múltiplas, dispositivos móveis: seja qual for o nome, a Web é frequentemente utilizada em condições de conectividade mutável. A entrega dinâmica e adaptativa é essencial para lidar com restrições de largura de banda e variabilidade no mundo multidispositivo.

O DASH (também conhecido como MPEG-DASH) foi projetado para permitir o melhor envio de mídia possível em um mundo instável, tanto para streaming quanto para download. Várias outras tecnologias fazem algo semelhante, como o HTTP Live Streaming (HLS) da Apple e o Smooth Streaming da Microsoft, mas o DASH é o único método de streaming de taxa de bits adaptável por HTTP que é baseado em um padrão aberto. O DASH já está em uso em sites como o YouTube.

Qual é a relação disso com EME e MSE? As implementações do DASH baseadas em MSE podem analisar um manifesto, fazer o download de segmentos de vídeo com uma taxa de bits adequada e alimentar um elemento de vídeo quando ele ficar com fome, usando a infraestrutura HTTP atual.

Em outras palavras, o DASH permite que provedores de conteúdo comercial façam streaming adaptativo de conteúdo protegido.

O DASH faz o que diz:

  • Dinâmico: responde a condições variáveis.
  • Adaptável:se adapta para fornecer uma taxa de bits de áudio ou vídeo adequada.
  • Streaming: permite streaming e download.
  • HTTP:permite o envio de conteúdo com a vantagem do HTTP, sem as desvantagens de um servidor de streaming tradicional.

A BBC começou a fornecer fluxos de teste usando o DASH:

A mídia é codificada várias vezes em diferentes taxas de bits. Cada codificação é chamada de representação. Eles são divididos em vários segmentos de mídia. O cliente reproduz um programa solicitando segmentos, em ordem, de uma representação por HTTP. As representações podem ser agrupadas em conjuntos de adaptação de representações que contêm conteúdo equivalente. Se o cliente quiser mudar o bitrate, ele poderá escolher uma alternativa do conjunto de adaptação atual e começar a solicitar segmentos dessa representação. O conteúdo é codificado de modo a facilitar essa troca para o cliente. Além de vários segmentos de mídia, uma representação geralmente também tem um segmento de inicialização. Isso pode ser considerado um cabeçalho, contendo informações sobre a codificação, tamanhos de frame etc. Um cliente precisa receber essa informação para uma determinada representação antes de consumir segmentos de mídia dessa representação.

Para resumir:

  1. A mídia é codificada em diferentes taxas de bits.
  2. Os diferentes arquivos de taxa de bits são disponibilizados em um servidor HTTP.
  3. Um app da Web cliente escolhe qual taxa de bits recuperar e reproduzir com o DASH.

Como parte do processo de segmentação de vídeo, um manifesto XML conhecido como Descrição de apresentação de mídia (MPD, na sigla em inglês) é criado programaticamente. Isso descreve conjuntos de adaptação e representações, com durações e URLs. Um MPD tem esta aparência:

    <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 foi extraído do arquivo .mpd usado no player de demonstração do YouTube DASH.

De acordo com a especificação DASH, um arquivo MPD poderia, em teoria, ser usado como src para um vídeo. No entanto, para dar mais flexibilidade aos desenvolvedores Web, os fornecedores de navegadores optaram por deixar o suporte ao DASH para bibliotecas JavaScript que usam MSE, como dash.js. A implementação do DASH no JavaScript permite que o algoritmo de adaptação evolua sem exigir atualizações do navegador. O uso do MSE também permite a experimentação com formatos de manifesto alternativos e mecanismos de entrega sem exigir mudanças no navegador. O Shaka Player do Google implementa um cliente DASH compatível com EEM.

A Mozilla Developer Network tem instruções sobre como usar ferramentas WebM e FFmpeg para segmentar vídeos e criar um MPD.

Conclusão

O uso da Web para veicular áudio e vídeo pagos está crescendo a uma taxa enorme. Parece que todo novo dispositivo, seja um tablet, console de jogos, smart TV ou conversor, pode fazer streaming de mídia dos principais provedores de conteúdo por HTTP. Mais de 85% dos navegadores para dispositivos móveis e computadores agora são compatíveis com <video> e <audio>, e a Cisco estima que os vídeos serão de 80 a 90% do tráfego global de Internet dos consumidores até 2017. Nesse contexto, o suporte do navegador para a distribuição de conteúdo protegido provavelmente vai se tornar cada vez mais significativo, já que os fornecedores de navegador restringem o suporte para as APIs que a maioria dos plug-ins de mídia usa.

Leitura adicional

Especificações e padrões

Especificação EME: rascunho mais recente do editor Criptografia comum (CENC, na sigla em inglês) Extensões de origem de mídia: rascunho mais recente do editor Padrão DASH (sim, é um PDF) Visão geral do padrão DASH

Artigos

Webinar do DTG (parcialmente obsoleto) O que é EME?, por Henri Sivonen Introdução às extensões de origem de mídia Fluxos de teste MPEG-DASH: postagem do blog da BBC R&D

Demonstrações

Demonstração de tecla Clear: simpl.info/ck Demonstração de extensões de fonte de mídia (MSE, na sigla em inglês) O Shaka Player do Google implementa um cliente DASH com suporte a EME

Feedback