Introduzione a Encrypted Media Extensions

Encrypted Media Extensions (EME) 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 è progettato per consentire di utilizzare le stesse app e gli stessi file criptati in qualsiasi browser, indipendentemente dal sistema di protezione sottostante. Il primo è reso possibile dalle API e dal flusso standardizzati, mentre il secondo è reso possibile dal concetto di crittografia comune.

EME è un'estensione della specifica HTMLMediaElement, da cui il nome. Poiché si tratta di un'estensione, il supporto di EME da parte del browser è facoltativo: se un browser non supporta i contenuti multimediali criptati, non potrà riprodurli, ma EME non è obbligatorio per la conformità alle specifiche HTML. Dalla specifica EME:

Le implementazioni EME utilizzano i seguenti componenti esterni:

  • Sistema chiave: un meccanismo di protezione dei contenuti (DRM). EME non definisce i sistemi di chiavi stessi, a parte Clear Key (di seguito sono riportate ulteriori informazioni).
  • CDM (Content Decryption Module): un meccanismo software o hardware lato client che consente la riproduzione di contenuti multimediali criptati. Come per Key Systems, EME non definisce alcun CDM, ma fornisce un'interfaccia per consentire alle applicazioni di interagire con i CDM disponibili.
  • Server delle licenze (chiavi): interagisce con un CDM per fornire le chiavi per decriptare i contenuti multimediali. La negoziazione con il server di licenza è 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 abilitare la decrittografia, ma l'identità e l'autenticazione dell'utente non fanno parte di EME. Il recupero delle chiavi per abilitare la riproduzione dei contenuti multimediali avviene dopo (facoltativamente) l'autenticazione di un utente. Servizi come Netflix devono autenticare gli utenti all'interno della loro applicazione web: quando un utente accede all'applicazione, questa determina la sua identità e i suoi privilegi.

Come funziona EME?

Ecco come interagiscono i componenti di EME, che corrispondono al seguente esempio di codice:

  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 di seguito per sapere come avviene) e attiva un evento encrypted con i metadati (initData) ottenuti dai contenuti multimediali in merito alla crittografia.
  3. L'applicazione gestisce l'evento encrypted:
    1. Se nessun oggetto MediaKeys è stato associato all'elemento multimediale, seleziona prima un sistema di chiavi disponibile utilizzando navigator.requestMediaKeySystemAccess() per verificare quali sistemi di chiavi sono disponibili, quindi crea un oggetto MediaKeys per un sistema di chiavi disponibile tramite un oggetto MediaKeySystemAccess. Tieni presente che l'inizializzazione dell'oggetto MediaKeys deve avvenire prima del primo evento encrypted. L'app genera l'URL di un server licenze, indipendentemente dalla selezione di un sistema chiavi disponibile. Un oggetto MediaKeys rappresenta tutte le chiavi disponibili per decriptare i contenuti multimediali per un elemento audio o video. Rappresenta un'istanza CDM e fornisce l'accesso al CDM, in particolare per la creazione di sessioni di chiavi, che vengono utilizzate per ottenere le chiavi da un server delle licenze.
    2. Dopo aver 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() sul 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 nell'handler encrypted al CDM chiamando generateRequest() su MediaKeySession.
  6. Il CDM attiva un evento message: una richiesta di acquisizione di una chiave da un server 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 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 dall'ID chiave.
  10. La riproduzione dei contenuti multimediali riprende.

Fiuuu…

Tieni presente che potrebbero esserci più messaggi tra il CDM e il server delle licenze e che tutte le comunicazioni in questo processo sono opache per il browser e l'applicazione: i messaggi sono compresi solo dal 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 la prova della validità del CDM (e della relazione di attendibilità) nonché una chiave da utilizzare per criptare le chiavi dei contenuti nella licenza risultante.

…ma cosa fanno esattamente i CDM?

Un'implementazione EME non fornisce in sé un modo per decriptare i contenuti multimediali: fornisce semplicemente un'API per consentire a un'applicazione web di interagire con i moduli di decrittografia dei contenuti.

Ciò che fanno effettivamente i CDM non è definito dalle specifiche EME e un CDM può gestire la decodifica (decompressione) dei contenuti multimediali così come la decrittografia. Dalla meno alla più efficace, 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, con passaggio dei 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 a un'app web:

  • Raggruppa un CDM con il browser.
  • Distribuire un CDM separatamente.
  • Crea un CDM nel sistema operativo.
  • Includi un CDM nel firmware.
  • Incorpora un CDM nell'hardware.

La modalità di messa a disposizione di un CDM non è definita dalla specifica EME, ma in tutti i casi è il browser a essere responsabile del controllo e dell'esposizione del CDM.

EME non impone un determinato Key System; tra i browser desktop e mobile attuali, Chrome supporta Widevine e IE11 supporta PlayReady.

Recupero di una chiave da un server licenze

In genere, i contenuti vengono criptati e codificati utilizzando uno strumento o un servizio di pacchettizzazione. Una volta che i contenuti multimediali criptati sono stati resi disponibili online, un client web può ottenere una chiave (contenuta in una licenza) da un server di licenze e utilizzarla per attivare la decriptazione 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 delle 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 comune consentono ai fornitori di contenuti di criptare e pacchettizzare i propri contenuti una volta per contenitore/codec e di utilizzarli con una serie di sistemi di chiavi, CDM e client, ovvero qualsiasi CDM che supporti la crittografia comune. Ad esempio, un video pacchettizzato utilizzando PlayReady potrebbe essere riprodotto in un browser utilizzando un CDM Widevine che ottiene una chiave da un server di licenze Widevine.

Ciò è in contrasto con le soluzioni precedenti che funzionavano solo con uno stack verticale completo, incluso un singolo client che spesso includeva anche un runtime dell'applicazione.

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 a tutti i browser che supportano EME di implementare Clear Key. Con questo sistema, i contenuti multimediali possono essere criptati con una chiave e poi riprodotti semplicemente fornendo la chiave. Clear Key può essere integrato nel browser: non richiede l'uso di un modulo di decriptazione separato.

Sebbene non sia probabile che venga utilizzato per molti tipi di contenuti commerciali, Clear Key è completamente interoperabile su tutti i browser che supportano EME. È utile anche per testare le implementazioni di EME e le applicazioni che utilizzano EME, senza dover richiedere una chiave dei contenuti da un server delle licenze. Puoi trovare un semplice esempio di Clear Key all'indirizzo simpl.info/ck. Di seguito è riportato un walkthrough del codice, che è simile ai 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, devi riprodurre un video criptato. La crittografia di un video per l'utilizzo con Clear Key può essere eseguita per WebM secondo le istruzioni webm_crypt. Sono disponibili anche servizi commerciali (almeno per ISO BMFF/MP4) e sono in fase di sviluppo altre soluzioni.

Estensioni Media Source (MSE)

HTMLMediaElement è una creatura di rara bellezza.

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

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

L'API Media Source è un'estensione di HTMLMediaElement che consente un controllo più granulare dell'origine dei contenuti multimediali, consentendo a JavaScript di creare stream per la riproduzione da "chunk" di video. Ciò a sua volta consente tecniche come lo streaming adattivo e il time shifting.

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

Come suddividere e riprodurre contenuti multimediali codificati a 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 suddiviso in cinque chunk utilizzando le API File. In un'applicazione di produzione, i chunk di video verrebbero recuperati tramite Ajax.

Per prima cosa viene creato un SourceBuffer:

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

L'intero film viene quindi "trasmesso in streaming" a un elemento video aggiungendo ogni chunk utilizzando 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 HTML5 Rocks.

Dynamic Adaptive Streaming over HTTP (DASH)

Multi-dispositivo, multipiattaforma, mobile: a prescindere dal nome, il web viene spesso utilizzato in condizioni di connettività mutevole. L'erogazione dinamica e adattiva è fondamentale per far fronte alle limitazioni di larghezza di banda e alla variabilità nel mondo multi-dispositivo.

DASH (noto anche come MPEG-DASH) è progettato per consentire la migliore esperienza di caricamento dei contenuti multimediali in un mondo incerto, sia per lo streaming che per il download. Diverse altre tecnologie fanno qualcosa di simile, ad esempio HTTP Live Streaming (HLS) di Apple e Smooth Streaming di Microsoft, ma DASH è l'unico metodo di streaming a velocità in bit adattiva tramite HTTP basato su uno standard aperto. DASH è già utilizzato da siti come YouTube.

Che cosa c'entra con EME e MSE? Le implementazioni DASH basate su MSE possono analizzare un manifest, scaricare segmenti di video a una velocità in bit appropriata e inviarli a un elemento video quando necessario, 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 esattamente quello che promette:

  • Dinamico: risponde alle condizioni in evoluzione.
  • Adattiva: si adatta per fornire una velocità in bit audio o video appropriata.
  • Streaming: consente lo streaming e il download.
  • HTTP: consente la pubblicazione di contenuti con il vantaggio di 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 vengono codificati a 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 Media Presentation Description (MPD). Descrive i set di adattamento e le rappresentazioni, con durate e URL. Un file 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 demo YouTube DASH)

Secondo la specifica 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 di DASH alle librerie JavaScript che utilizzano MSE, come 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 caricamento alternativi senza richiedere modifiche al browser. Shaka Player di Google implementa un client DASH con supporto EME.

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

Conclusione

L'utilizzo del web per la pubblicazione di contenuti video e audio a pagamento è in crescita a un ritmo esponenziale. Sembra che ogni nuovo dispositivo, che si tratti di un tablet, una console per videogiochi, una TV connessa a internet o un set-top box, sia in grado di riprodurre in streaming i contenuti multimediali dei principali fornitori di contenuti tramite HTTP. Oltre l'85% dei browser mobile e desktop ora supporta <video> e <audio> e Cisco stima che i video rappresenteranno l'80-90% del traffico internet globale dei consumatori entro il 2017. In questo contesto, il supporto del browser per la distribuzione di contenuti protetti diventerà probabilmente sempre più importante, poiché i fornitori di browser ridurranno il supporto per le API su cui si basa la maggior parte dei plug-in multimediali.

Per approfondire

Specifiche e standard

Articoli