EME WTF?

Un'introduzione alle Encrypted Media Extensions

Encrypted Media Extensions fornisce un'API che consente alle applicazioni web di interagire con i sistemi di protezione dei contenuti, per consentire la riproduzione di audio e video criptati.

EME è progettata per consentire l'utilizzo della stessa app e degli stessi file criptati in qualsiasi browser, indipendentemente dal sistema di protezione sottostante. La prima è resa possibile dalle API e dal flusso standardizzati, mentre la seconda è resa possibile dal concetto di Common Encryption.

EME è un'estensione della specifica HTMLMediaElement, da cui deriva il nome. Essendo una "estensione", il supporto del browser per EME è facoltativo: se un browser non supporta i contenuti multimediali criptati non sarà possibile riprodurre contenuti multimediali criptati, ma la tecnologia EME non è necessaria per la conformità alle specifiche HTML. Dalle specifiche EME:

Le implementazioni EME utilizzano i seguenti componenti esterni:

  • Sistema chiavi: un meccanismo di protezione dei contenuti (DRM). EME non definisce direttamente i sistemi Key, a parte Clear Key (ulteriori informazioni in merito di seguito).
  • Content Decryption Module (CDM): un meccanismo software o hardware lato client che consente la riproduzione di contenuti multimediali criptati. Come per i sistemi chiave, la soluzione EME non definisce i CDM, ma fornisce un'interfaccia per consentire alle applicazioni di interagire con i CDM disponibili.
  • Server licenza (chiave): interagisce con un CDM per fornire le chiavi per decriptare i contenuti multimediali. La negoziazione con il server delle licenze è responsabilità dell'applicazione.
  • Servizio di imballaggio: codifica e cripta i contenuti multimediali per la distribuzione/il consumo.

Tieni presente che un'applicazione che utilizza EME interagisce con un server di licenze per ottenere le chiavi per attivare la decrittografia, ma l'identità e l'autenticazione utente non fanno parte di EME. Il recupero delle chiavi per abilitare la riproduzione di contenuti multimediali avviene dopo (facoltativamente) aver autenticato un utente. Servizi come Netflix devono autenticare gli utenti all'interno della loro applicazione web: quando un utente accede all'applicazione, l'applicazione determina l'identità e i privilegi dell'utente.

Come funziona EME?

Ecco come interagiscono i componenti di EME, corrispondente all'esempio di codice riportato di seguito:

  1. Un'applicazione web tenta di riprodurre audio o video con uno o più stream criptati.
  2. Il browser riconosce che i contenuti multimediali sono criptati (vedi la casella sotto per sapere come avviene) e attiva un evento encrypted con i metadati (initData) ottenuti dai contenuti multimediali relativi alla crittografia.
  3. L'applicazione gestisce l'evento encrypted:
    1. Se nessun oggetto MediaKeys è stato associato all'elemento multimediale, seleziona prima un sistema chiavi disponibile utilizzando navigator.requestMediaKeySystemAccess() per verificare quali sistemi di chiavi sono disponibili, quindi crea un oggetto MediaKeys per un sistema chiavi disponibile tramite un oggetto MediaKeySystemAccess. Tieni presente che l'inizializzazione dell'oggetto MediaKeys deve avvenire prima del primo evento encrypted. L'app ottiene l'URL di un server di licenze, indipendentemente dalla selezione di un sistema di chiavi disponibile. Un oggetto MediaKeys rappresenta tutte le chiavi disponibili per decriptare i contenuti multimediali di un elemento audio o video. Rappresenta un'istanza CDM e fornisce l'accesso a CDM, in particolare per creare sessioni chiave, utilizzate per ottenere le chiavi da un server di licenze.
    2. Una volta creato l'oggetto MediaKeys, assegnalo all'elemento multimediale: setMediaKeys() associa l'oggetto MediaKeys a un HTMLMediaElement, in modo che le relative chiavi possano essere utilizzate durante la riproduzione, ovvero durante la decodifica.
  4. L'app crea un MediaKeySession chiamando createSession() su MediaKeys. Viene creato un MediaKeySession, che rappresenta la durata di una licenza e delle relative chiavi.
  5. L'app genera una richiesta di licenza passando i dati multimediali ottenuti nel gestore encrypted al CDM chiamando generateRequest() su MediaKeySession.
  6. Il CDM attiva un evento message: una richiesta di acquisizione di una chiave da un server di licenze.
  7. L'oggetto MediaKeySession riceve l'evento message e l'applicazione invia un messaggio al server delle licenze (ad esempio tramite XHR).
  8. L'applicazione riceve una risposta dal server delle licenze e passa i dati al CDM utilizzando il metodo update() di MediaKeySession.
  9. Il CDM decripta i contenuti multimediali utilizzando le chiavi incluse nella licenza. È possibile utilizzare una chiave valida da qualsiasi sessione all'interno dei MediaKey associati all'elemento multimediale. Il CDM accederà alla chiave e al criterio, indicizzati in base all'ID chiave.
  10. La riproduzione dei contenuti multimediali riprende.

Fiuuu...

Tieni presente che potrebbero esserci più messaggi tra CDM e il server delle licenze e tutte le comunicazioni in questo processo sono opache per il browser e l'applicazione: i messaggi vengono interpretati solo da CDM e dal server delle licenze, anche se il livello dell'app può vedere il tipo di messaggio inviato dal CDM. La richiesta di licenza contiene una prova della validità (e della relazione di fiducia) del CDM, nonché una chiave da utilizzare per criptare le chiavi dei contenuti nella licenza risultante.

... ma in realtà cosa fanno i CDM?

L'implementazione EME non offre di per sé un modo per decriptare i contenuti multimediali: fornisce semplicemente un'API affinché un'applicazione web possa interagire con i moduli di decrittografia dei contenuti.

Ciò che in realtà fanno i CDM non è definito dalle specifiche EME e un CDM potrebbe gestire la decodifica (decompressione) dei contenuti multimediali e la decrittografia. A partire dalla più affidabile, esistono diverse potenziali opzioni per la funzionalità CDM:

  • Solo decrittografia, che consente la riproduzione utilizzando la normale pipeline multimediale, ad esempio tramite un elemento <video>.
  • Decrittografia e decodifica, passaggio di frame video al browser per il rendering.
  • Decrittografia e decodifica, rendering direttamente nell'hardware (ad esempio, nella GPU).

Esistono diversi modi per rendere disponibile un CDM in un'app web:

  • Raggruppa un CDM con il browser.
  • Distribuisci un CDM separatamente.
  • di incorporare un CDM nel sistema operativo.
  • Includi un CDM nel firmware.
  • Incorporare un CDM nell'hardware.

Il modo in cui un CDM viene reso disponibile non è definito dalle specifiche EME, ma in tutti i casi il browser è responsabile del controllo e dell'esposizione del CDM.

EME non impone un particolare sistema chiavi; tra gli attuali browser desktop e mobile, Chrome supporta Widevine, mentre IE11 supporta PlayReady.

Recupero di una chiave da un server licenze

Nel tipico uso commerciale, i contenuti saranno criptati e codificati utilizzando un servizio o uno strumento di pacchettizzazione. Una volta che i contenuti multimediali criptati sono resi disponibili online, un client web può ottenere una chiave (contenuta in una licenza) da un server di licenze e utilizzarla per abilitare la decrittografia e la riproduzione dei contenuti.

Il seguente codice (adattato dagli esempi di specifiche) mostra come un'applicazione può selezionare un sistema di chiavi appropriato e ottenere una chiave da un server di licenze.

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

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

if (!video.mediaKeys) {
  navigator.requestMediaKeySystemAccess('org.w3.clearkey',
      config).then(
    function(keySystemAccess) {
      var promise = keySystemAccess.createMediaKeys();
      promise.catch(
        console.error.bind(console, 'Unable to create MediaKeys')
      );
      promise.then(
        function(createdMediaKeys) {
          return video.setMediaKeys(createdMediaKeys);
        }
      ).catch(
        console.error.bind(console, 'Unable to set MediaKeys')
      );
      promise.then(
        function(createdMediaKeys) {
          var initData = new Uint8Array([...]);
          var keySession = createdMediaKeys.createSession();
          keySession.addEventListener('message', handleMessage,
              false);
          return keySession.generateRequest('webm', initData);
        }
      ).catch(
        console.error.bind(console,
          'Unable to create or initialize key session')
      );
    }
  );
}

function handleMessage(event) {
  var keySession = event.target;
  var license = new Uint8Array([...]);
  keySession.update(license).catch(
    console.error.bind(console, 'update() failed')
  );
}

Crittografia comune

Le soluzioni di crittografia comuni consentono ai fornitori di contenuti di criptare e pacchettizzare i loro contenuti una volta per container/codec e di utilizzarli con una varietà di sistemi chiavi, CDM e client, ovvero qualsiasi CDM che supporti la crittografia comune. Ad esempio, un video pacchettizzato con Playready potrebbe essere riprodotto in un browser utilizzando un Widevine CDM che ottiene una chiave da un server licenze Widevine.

Questo è in contrasto con le soluzioni legacy che avrebbero funzionato solo con uno stack verticale completo, incluso un singolo client che spesso includeva anche un runtime dell'applicazione.

La crittografia comune (CENC) è uno standard ISO che definisce uno schema di protezione per ISO BMFF. Un concetto simile si applica a WebM.

Cancella chiave

Sebbene EME non definisca la funzionalità DRM, la specifica attualmente impone che tutti i browser che supportano EME debbano implementare la funzionalità Clear Key. Questo sistema consente di criptare i contenuti multimediali con una chiave e quindi riprodurli semplicemente fornendo la chiave. La chiave Clear può essere integrata nel browser: non richiede l'uso di un modulo di decrittografia separato.

Sebbene non sia probabile che venga utilizzato per molti tipi di contenuti commerciali, Clear Key è completamente interoperabile tra tutti i browser che supportano EME. È utile anche per testare le implementazioni EME e le applicazioni che utilizzano EME, senza dover richiedere una chiave dei contenuti a un server di licenze. È disponibile un esempio semplice di Clear Key all'indirizzo simpl.info/ck. Di seguito è riportata una procedura dettagliata del codice, che rispecchia i passaggi descritti sopra, ma senza interazione con il server delle licenze.

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

Per testare questo codice, è necessario un video criptato da riprodurre. La crittografia di un video per l'utilizzo con Clear Key può essere eseguita per WebM come descritto nelle istruzioni di webm_crypt. Sono inoltre disponibili servizi commerciali (almeno per ISO BMFF/MP4) e altre soluzioni sono in fase di sviluppo.

MSE (Media Source Extensions)

HTMLMediaElement è una creatura di semplice bellezza.

Possiamo caricare, decodificare e riprodurre i contenuti multimediali semplicemente fornendo un URL src:

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

L'API Media Source è un'estensione di HTMLMediaElement che consente un controllo più granulare sull'origine dei contenuti multimediali, consentendo a JavaScript di creare stream per la riproduzione da "blocchi" di video. Questo, a sua volta, consente tecniche come lo streaming adattivo e lo spostamento temporale.

Perché la funzione MSE è importante per l'EME? Perché, oltre a distribuire contenuti protetti, i fornitori di contenuti commerciali devono essere in grado di adattare l'importazione dei contenuti alle condizioni della rete e ad altri requisiti. Netflix, ad esempio, modifica dinamicamente la velocità in bit dello stream al variare delle condizioni della rete. EME supporta la riproduzione di stream multimediali forniti da un'implementazione MSE, proprio come farebbe con i contenuti multimediali forniti tramite un attributo src.

Come suddividere e riprodurre i contenuti multimediali codificati con velocità in bit diverse? Consulta la sezione DASH di seguito.

Puoi vedere MSE in azione all'indirizzo simpl.info/mse. Ai fini di questo esempio, un video WebM viene diviso in cinque parti utilizzando le API file. In un'applicazione di produzione, i blocchi di video vengono recuperati tramite Ajax.

Innanzitutto viene creato un buffer di origine:

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

L'intero filmato viene quindi trasmesso in streaming a un elemento video aggiungendo ogni blocco con il metodo 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);
  }
};

Scopri di più su MSE nell'articolo su HTML5 Rocks.

Dynamic Adaptive Streaming over HTTP (DASH)

Multi-dispositivo, multipiattaforma e dispositivi mobili: come lo chiami, il web viene spesso vissuto in condizioni di connettività sempre variabili. La pubblicazione dinamica e adattiva è fondamentale per far fronte ai limiti e alla variabilità della larghezza di banda nel mondo multi-dispositivo.

DASH (noto anche come MPEG-DASH) è progettato per consentire la migliore pubblicazione di contenuti multimediali in un mondo instabili, sia per lo streaming che per il download. Molte altre tecnologie funzionano in modo simile, ad esempio HTTP Live Streaming (HLS) di Apple e Live Streaming di Microsoft, ma DASH è l'unico metodo di streaming a velocità in bit adattiva tramite HTTP e si basa su uno standard aperto. DASH è già utilizzato da siti come YouTube.

Che cosa c'entra tutto questo con EME e MSE? Le implementazioni DASH basate su MSE sono in grado di analizzare un manifest, scaricare segmenti di video a una velocità in bit appropriata e indirizzarli a un elemento video quando diventa affamato utilizzando l'infrastruttura HTTP esistente.

In altre parole, DASH consente ai fornitori di contenuti commerciali di eseguire lo streaming adattivo di contenuti protetti.

DASH fa quello che dice sulla scatola:

  • Dinamico: risponde a condizioni variabili.
  • Adattiva: si adatta per fornire una velocità in bit audio o video appropriata.
  • Streaming: consente sia lo streaming sia il download.
  • HTTP:consente la distribuzione dei contenuti con il vantaggio del protocollo HTTP, senza gli svantaggi di un server di streaming tradizionale.

La BBC ha iniziato a fornire stream di prova utilizzando DASH:

In sintesi:

  1. I contenuti multimediali sono codificati con velocità in bit diverse.
  2. I diversi file con velocità in bit vengono resi disponibili da un server HTTP.
  3. Un'app web client sceglie la velocità in bit da recuperare e riprodurre con DASH.

Nell'ambito del processo di segmentazione dei video, viene creato in modo programmatico un manifest XML noto come MPD (Media Presentation Description). Descrive gli insiemi di adattamento e le rappresentazioni, con durate e URL. Un MPD ha il seguente aspetto:

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

Questo XML è tratto dal file .mpd utilizzato per il player della demo DASH di YouTube.

Secondo le specifiche DASH, in teoria un file MPD potrebbe essere utilizzato come src per un video. Tuttavia, per offrire maggiore flessibilità agli sviluppatori web, i fornitori di browser hanno scelto di lasciare il supporto DASH fino alle librerie JavaScript che utilizzano MSE, ad esempio dash.js. L'implementazione di DASH in JavaScript consente all'algoritmo di adattamento di evolversi senza richiedere aggiornamenti del browser. L'utilizzo di MSE consente inoltre di sperimentare con formati manifest e meccanismi di pubblicazione alternativi senza richiedere modifiche al browser. Shaka Player di Google implementa un client DASH con supporto EME.

Mozilla Developer Network offre istruzioni su come utilizzare gli strumenti WebM e FFmpeg per segmentare i video e creare un MPD.

Conclusione

L'utilizzo del web per offrire video e audio a pagamento sta crescendo a una frequenza enorme. Sembra che ogni nuovo dispositivo, che si tratti di tablet, console per videogiochi, TV connessa a internet o decoder, sia in grado di riprodurre in streaming contenuti multimediali dai principali fornitori di contenuti tramite HTTP. Oltre l'85% dei browser mobile e desktop ora supportano <video> e <audio> e Cisco stima che i video costituiranno dall'80 al 90% del traffico internet dei consumatori a livello mondiale entro il 2017. In questo contesto, il supporto dei browser per la distribuzione di contenuti protetti diventerà probabilmente sempre più significativo, in quanto i fornitori di browser riducono il supporto per le API su cui fa affidamento la maggior parte dei plug-in multimediali.

Per approfondire

Specifiche e standard

Articoli