O que é o EME?

O Encrypted Media Extensions fornece 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 os arquivos criptografados sejam usados em qualquer navegador, independente do sistema de proteção. O primeiro é possibilitado pelas APIs e pelo fluxo padronizados, enquanto o segundo é possibilitado pelo 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 EME é opcional: se um navegador não oferecer suporte a mídia criptografada, ele não poderá reproduzir mídia criptografada, mas o EME não será necessário para conformidade com as especificações HTML. Na especificação do EME (em inglês):

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

A API oferece suporte a casos de uso que variam de descriptografia simples de chave clara a vídeo de alto valor (dada uma implementação apropriada do user agent). A troca de licenças/chaves é controlada pelo app, facilitando o desenvolvimento de apps de reprodução robustos que ofereçam suporte a várias tecnologias de descriptografia e proteção de conteúdo.

Esta especificação não define uma proteção de conteúdo ou um sistema 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 estar em conformidade com essa especificação: somente o sistema Clear Key precisa ser implementado como um valor de referência comum.

A API comum é compatível com 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 presumir que a comunicação fora de banda entre o sistema de criptografia e uma licença ou outro servidor.

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

  • Sistema de chaves:um mecanismo de proteção de conteúdo (DRM). O EME não define Key Systems em si, além da Clear Key (mais sobre isso abaixo).
  • Módulo de descriptografia de conteúdo (CDM, na sigla em inglês): um software ou mecanismo de hardware do lado do cliente que permite a reprodução de mídia criptografada. Assim como no Key Systems, o EME não define CDMs, 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ças é de responsabilidade do aplicativo.
  • Serviço de empacotamento:codifica e criptografa a mídia para distribuição/consumo.

Um aplicativo que usa o EME interage com um servidor de licenças para conseguir chaves para 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 depois (opcionalmente) da 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, o aplicativo determina a identidade e os privilégios do usuário.

Como o EME funciona?

Veja 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() poderão ser usados para selecionar o formato correto. No entanto, o CDM pode aceitar apenas um subconjunto do que o navegador aceita para conteúdo não criptografado. É melhor negociar uma configuração do MediaKeys antes de selecionar um formato e um codec. Se o aplicativo aguardar o evento criptografado, mas o MediaKeys mostrar que não consegue processar o formato/codec escolhido, poderá ser tarde demais para alternar sem interromper a reprodução.

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

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

  1. Um aplicativo da Web tenta reproduzir áudio ou vídeo que tenha um ou mais streams criptografados.
  2. O navegador reconhece que a mídia está criptografada (consulte a caixa abaixo para saber o que 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 Key Systems estão disponíveis e, em seguida, crie um objeto MediaKeys para um Key System disponível usando um objeto MediaKeySystemAccess. A inicialização do objeto MediaKeys precisa acontecer antes do primeiro evento criptografado. A coleta de um URL do servidor de licença é feita pelo app, independentemente 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 dele 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 o ciclo de vida de uma licença e as chaves dela.

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

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

  7. O objeto MediaKeySession recebe o evento da mensagem, e o app envia uma mensagem ao servidor de licenças (via XHR, por exemplo).

  8. O app 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 da licença. Uma chave válida pode ser usada em 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 é 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ão em um formato como ISO BMFF ou WebM. No ISO BMFF, isso significa metadados de cabeçalho, chamados de caixa de informações do esquema de proteção. O WebM usa o elemento Matroska ContentEncryption, com algumas adições específicas dele. As diretrizes são fornecidas para cada contêiner em um registro específico da EME.

Observe que 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ó são compreendidas 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 prova da validade e da relação de confiança do CDM, bem como uma chave a ser usada ao criptografar as chaves de conteúdo na licença resultante.

Mas o que os CDMs realmente fazem?

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

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

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

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

  • Agrupar um CDM com o navegador.
  • Distribua um CDM separadamente.
  • Crie um CDM no sistema operacional.
  • Inclua um CDM no firmware.
  • Incorporar um CDM ao hardware.

A especificação do EME não define como um CDM é disponibilizado. No entanto, em todos os casos, o navegador é responsável por verificar e expor o CDM.

O EME não exige um sistema de chaves específico. Entre os navegadores atuais para computadores e dispositivos móveis, o Chrome é compatível com o Widevine e o IE11 tem suporte para o PlayReady.

Como obter uma chave de um servidor de licença

Em 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ças e usá-la para ativar a descriptografia e a reprodução do conteúdo.

O código a seguir (adaptado dos exemplos de especificação, em inglês) mostra como um aplicativo 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

Com as soluções de criptografia comuns, os provedores de conteúdo podem criptografar e empacotar o conteúdo uma vez por contêiner/codec e usá-lo com vários sistemas de chaves, CDMs e clientes, ou seja, qualquer CDM compatível com criptografia comum. Por exemplo, um vídeo empacotado com Playready pode ser reproduzido em um navegador usando um Widevine CDM obtendo uma chave de um servidor de licenças Widevine.

Isso é diferente em soluções legadas que só funcionam com uma pilha vertical completa, incluindo um único cliente que geralmente também incluía um ambiente de execução de aplicativo.

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

Limpar chave

Embora o EME não defina a funcionalidade DRM, a especificação atualmente determina que todos os navegadores compatíveis com o EME precisam implementar a tecla Clear Key. Com esse sistema, a mídia pode ser criptografada com uma chave e reproduzida com o simples fornecimento dela. A Clear Key pode ser integrada ao navegador: não requer o uso de um módulo de descriptografia separado.

Embora não seja provável que seja usada para muitos tipos de conteúdo comercial, a Clear Key é totalmente interoperável em todos os navegadores compatíveis com o EME. Ele também é útil para testar implementações e apps que usam o EME, sem precisar solicitar uma chave de conteúdo de um servidor de licença. Há um exemplo simples de "Limpar chave" em simpl.info/ck. Veja abaixo um tutorial do código, que é paralela à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 este código, você precisa de um vídeo criptografado para reproduzir. É possível criptografar um vídeo para uso com Clear Key no WebM de acordo com as instruções de 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.

Para carregar, decodificar e reproduzir mídia, basta fornecer um URL 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 streams para reprodução de "blocos" de vídeo. Isso, por sua vez, permite técnicas como streaming adaptável e mudança de tempo.

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

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

Veja o MSE em ação em simpl.info/mse. Para os fins deste 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 é então "transmitido" para um elemento de vídeo, anexando cada fragmento usando o método anexarBuffer():

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 o MSE na introdução sobre MSE.

Vários dispositivos, plataformas e dispositivos móveis. Seja qual for o nome do app, a Web frequentemente passa por condições de conectividade variável. A entrega dinâmica e adaptável é crucial para lidar com as restrições e a variabilidade de largura de banda no mundo com vários dispositivos.

O DASH (também conhecido como MPEG-DASH) foi projetado para permitir a melhor entrega 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 via HTTP baseado em um padrão aberto. O DASH já está em uso por sites como o YouTube.

Qual é a relação com o EME e o MSE? As implementações DASH com base no MSE podem analisar um manifesto, fazer o download de segmentos de vídeo com uma taxa de bits adequada e alimentá-los a um elemento de vídeo quando ele ficar com fome, usando a infraestrutura HTTP existente.

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

O DASH faz o que diz:

  • Dinâmico:responde a mudanças de condições.
  • Adaptável:adapta-se 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 disponibilizar transmissões de teste usando o DASH:

A mídia é codificada diversas vezes em diferentes taxas de bits. Cada codificação é chamada de representação. Eles são divididos em diversos segmentos de mídia. O cliente executa um programa solicitando segmentos, em ordem, de uma representação em HTTP. As representações podem ser agrupadas em conjuntos de adaptação de representações com conteúdo equivalente. Se o cliente quiser mudar a taxa de bits, poderá escolher uma alternativa no conjunto de adaptação atual e começar a solicitar segmentos dessa representação. O conteúdo é codificado de modo a facilitar a 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 como um cabeçalho, contendo informações sobre a codificação, os tamanhos dos frames etc. Um cliente precisa conseguir isso 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 a partir de um servidor HTTP.
  3. Um app da Web cliente escolhe qual taxa de bits recuperar e reproduzir com 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 de forma programática. Isso descreve conjuntos de adaptação e representações, com durações e URLs. Uma MPD tem a seguinte 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>

Esse XML é retirado do arquivo .mpd usado para o player de demonstração do YouTube DASH.

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

A Mozilla Developer Network tem instruções (em inglês) sobre como usar ferramentas WebM e FFmpeg para segmentar vídeos e criar uma MPD.

Conclusão

O uso da Web para oferecer vídeo e áudio pago está crescendo a uma enorme taxa. Parece que todos os dispositivos novos, sejam tablets, consoles de jogos, smart TVs ou conversores, são capazes de 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 o vídeo será de 80 a 90% do tráfego global da Internet do consumidor até 2017. Nesse contexto, o suporte dos navegadores à distribuição de conteúdo protegido provavelmente será cada vez mais significativo, já que os fornecedores de navegadores oferecem suporte a APIs que a maioria dos plug-ins de mídia depende.

Sugestões de leitura

Especificações e padrões

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

Artigos

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

Demonstrações

Demonstração de chave clara: simpl.info/ck Demonstração de extensões de origem de mídia (MSE) O Shaka Player do Google implementa um cliente DASH com suporte para EME.

Feedback