EME-WTF?

Einführung in verschlüsselte Medienerweiterungen

Encrypted Media Extensions stellt eine API bereit, die Webanwendungen die Interaktion mit Content-Schutzsystemen ermöglicht, um die Wiedergabe von verschlüsselten Audio- und Videoinhalten zu ermöglichen.

EME wurde entwickelt, um die Verwendung derselben Anwendung und verschlüsselten Dateien in jedem Browser unabhängig vom zugrunde liegenden Schutzsystem zu ermöglichen. Ersteres wird durch standardisierte APIs und Abläufe ermöglicht, während Letzteres durch das Konzept der gemeinsamen Verschlüsselung ermöglicht wird.

EME ist eine Erweiterung der HTMLMediaElement-Spezifikation – daher der Name. Eine „Erweiterung“ bedeutet, dass die Browserunterstützung für EME optional ist: Wenn ein Browser keine verschlüsselten Medien unterstützt, kann er keine verschlüsselten Medien wiedergeben, für die Einhaltung der HTML-Spezifikationen ist EME jedoch nicht erforderlich. Aus der EME-Spezifikation:

EME-Implementierungen verwenden die folgenden externen Komponenten:

  • Schlüsselsystem:Ein Mechanismus zum Schutz von Inhalten (DRM-Mechanismus). Abgesehen von „Clear Key“ werden Schlüsselsysteme von EME nicht definiert (mehr dazu weiter unten).
  • Content Decryption Module (CDM): Ein clientseitiger Software- oder Hardwaremechanismus, der die Wiedergabe verschlüsselter Medien ermöglicht. Wie bei Key Systems definiert auch EME keine CDMs, sondern bietet eine Schnittstelle für Anwendungen zur Interaktion mit verfügbaren CDMs.
  • Lizenzserver (Schlüssel):interagiert mit einem CDM, um Schlüssel zum Entschlüsseln von Medien bereitzustellen. Die Verhandlung mit dem Lizenzserver liegt in der Verantwortung der Anwendung.
  • Verpackungsdienst:Codiert und verschlüsselt Medien für die Verteilung/Nutzung.

Eine Anwendung, die EME verwendet, interagiert mit einem Lizenzserver, um Schlüssel für die Entschlüsselung abzurufen. Die Nutzeridentität und -authentifizierung sind jedoch nicht Teil von EME. Das Abrufen von Schlüsseln zum Aktivieren der Medienwiedergabe erfolgt (optional) nach der Authentifizierung eines Nutzers. Dienste wie Netflix müssen Nutzer innerhalb ihrer Webanwendung authentifizieren: Wenn sich ein Nutzer in der Anwendung anmeldet, bestimmt die Anwendung die Identität und die Berechtigungen des Nutzers.

Wie funktioniert EME?

So interagieren die Komponenten von EME entsprechend dem folgenden Codebeispiel:

  1. Eine Webanwendung versucht, Audio- oder Videoinhalte mit einem oder mehreren verschlüsselten Streams wiederzugeben.
  2. Der Browser erkennt, dass die Medien verschlüsselt sind (siehe unten), und löst ein encrypted-Ereignis mit Metadaten (initData) aus, die von den Medien zur Verschlüsselung abgerufen wurden.
  3. Die Anwendung verarbeitet das Ereignis encrypted:
    1. Wenn dem Medienelement kein MediaKeys-Objekt zugeordnet wurde, wählen Sie zuerst mit navigator.requestMediaKeySystemAccess() ein verfügbares Schlüsselsystem aus, um zu prüfen, welche Schlüsselsysteme verfügbar sind. Erstellen Sie dann über ein MediaKeySystemAccess-Objekt ein MediaKeys-Objekt für ein verfügbares Schlüsselsystem. Die Initialisierung des MediaKeys-Objekts sollte vor dem ersten encrypted-Ereignis erfolgen. Die Lizenzserver-URL wird von der App unabhängig von der Auswahl eines verfügbaren Schlüsselsystems abgerufen. Ein MediaKeys-Objekt steht für alle Schlüssel, die zum Entschlüsseln der Medien für ein Audio- oder Videoelement verfügbar sind. Es stellt eine CDM-Instanz dar und bietet Zugriff auf das CDM, insbesondere zum Erstellen von Schlüsselsitzungen, mit denen Schlüssel von einem Lizenzserver abgerufen werden.
    2. Nachdem das MediaKeys-Objekt erstellt wurde, weisen Sie es dem Medienelement zu: setMediaKeys() verknüpft das MediaKeys-Objekt mit einem HTMLMediaElement, sodass seine Schlüssel während der Wiedergabe, d.h. während der Decodierung, verwendet werden können.
  4. Die App erstellt ein MediaKeySession durch Aufrufen von createSession() im MediaKeys. Dadurch wird eine MediaKeySession erstellt, die die Lebensdauer einer Lizenz und ihrer Schlüssel darstellt.
  5. Die App generiert eine Lizenzanfrage, indem die im encrypted-Handler abgerufenen Mediendaten an das CDM übergeben werden. Dazu wird generateRequest() für die MediaKeySession aufgerufen.
  6. Das CDM löst ein message-Ereignis aus, also eine Anfrage zum Abrufen eines Schlüssels von einem Lizenzserver.
  7. Das MediaKeySession-Objekt empfängt das message-Ereignis und die Anwendung sendet eine Nachricht an den Lizenzserver (z. B. über XHR).
  8. Die Anwendung empfängt eine Antwort vom Lizenzserver und übergibt die Daten mit der Methode update() des MediaKeySession an das CDM.
  9. Das CDM entschlüsselt die Medien mithilfe der Schlüssel in der Lizenz. Ein gültiger Schlüssel kann in jeder Sitzung innerhalb der MediaKeys verwendet werden, die mit dem Medienelement verknüpft sind. Das CDM greift auf den Schlüssel und die Richtlinie zu, die nach Schlüssel-ID indexiert sind.
  10. Die Medienwiedergabe wird fortgesetzt.

Puh...

Beachten Sie, dass sich möglicherweise mehrere Nachrichten zwischen dem CDM und dem Lizenzserver befinden und die gesamte Kommunikation in diesem Prozess für den Browser und die Anwendung undurchsichtig ist: Nachrichten werden nur vom CDM und vom Lizenzserver gelesen, obwohl auf der Anwendungsebene angezeigt wird, welche Art von Nachricht vom CDM gesendet wird. Die Lizenzanforderung enthält einen Nachweis der Gültigkeit des CDM (und die Vertrauensstellung) sowie einen Schlüssel, der zum Verschlüsseln der Inhaltsschlüssel in der resultierenden Lizenz verwendet werden muss.

Aber was tun CDMs eigentlich?

Eine EME-Implementierung bietet an sich keine Möglichkeit zum Entschlüsseln von Medien. Sie bietet einer Webanwendung lediglich eine API zur Interaktion mit Modulen zur Inhaltsentschlüsselung.

Was CDMs tatsächlich tun, ist in der EME-Spezifikation nicht definiert. Ein CDM kann sowohl die Decodierung (Dekomprimierung) als auch die Entschlüsselung übernehmen. Von der am wenigsten robusten bis zur robustesten gibt es mehrere mögliche Optionen für die CDM-Funktionalität:

  • Nur Entschlüsselung, wodurch die Wiedergabe über die normale Medienpipeline ermöglicht wird, z. B. über ein <video>-Element.
  • Entschlüsselung und Decodierung, Weitergabe von Videoframes an den Browser zum Rendern
  • Entschlüsselung und Decodierung, Rendering direkt in der Hardware (z. B. GPU)

Es gibt mehrere Möglichkeiten, ein CDM für eine Webanwendung verfügbar zu machen:

  • Ein CDM mit dem Browser bündeln
  • Verteilen Sie ein CDM separat.
  • Erstellen Sie ein CDM in das Betriebssystem.
  • Fügen Sie ein CDM in die Firmware ein.
  • Betten Sie ein CDM in die Hardware ein.

Wie ein CDM zur Verfügung gestellt wird, wird in der EME-Spezifikation nicht definiert. In allen Fällen ist jedoch der Browser für die Überprüfung und Offenlegung des CDM verantwortlich.

Für EME ist kein bestimmtes Schlüsselsystem vorgeschrieben. Unter aktuellen Desktop- und mobilen Browsern unterstützt Chrome Widevine und Internet Explorer 11 PlayReady.

Schlüssel von einem Lizenzserver abrufen

Bei einer typischen kommerziellen Nutzung werden Inhalte mithilfe eines Paketdienstes oder Tools verschlüsselt und codiert. Sobald die verschlüsselten Medien online verfügbar gemacht werden, kann ein Webclient von einem Lizenzserver einen Schlüssel (in einer Lizenz enthalten) erhalten und ihn zur Entschlüsselung und Wiedergabe der Inhalte verwenden.

Der folgende Code, angepasst aus den Spezifikationsbeispielen, zeigt, wie eine Anwendung ein geeignetes Schlüsselsystem auswählen und einen Schlüssel von einem Lizenzserver abrufen kann.

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

Allgemeine Verschlüsselung

Mit gängigen Verschlüsselungslösungen können Contentanbieter ihre Inhalte einmal pro Container/Codec verschlüsseln und verpacken und mit einer Vielzahl von Schlüsselsystemen, CDMs und Clients verwenden, d. h. mit jedem CDM, das Common Encryption unterstützt. Beispiel: Ein mit Playready gepacktes Video könnte mit einem Widevine-CDM, das einen Schlüssel von einem Widevine-Lizenzserver abruft, in einem Browser abgespielt werden.

Im Gegensatz dazu funktionieren Legacy-Lösungen nur mit einem vollständigen vertikalen Stack, einschließlich eines einzelnen Clients, der oft auch eine Anwendungslaufzeit enthielt.

Die Common Encryption (CENC) ist ein ISO-Standard, mit dem ein Schutzschema für ISO BMFF definiert wird. Ein ähnliches Konzept gilt für WebM.

Schlüssel löschen

Obwohl EME keine DRM-Funktion definiert, schreibt die Spezifikation derzeit vor, dass alle Browser, die EME unterstützen, einen Klarschlüssel implementieren müssen. Mit diesem System können Medien mit einem Schlüssel verschlüsselt und dann einfach durch Angabe dieses Schlüssels wiedergegeben werden. Der Löschschlüssel kann in den Browser integriert werden: Es ist kein separates Entschlüsselungsmodul erforderlich.

Clear Key wird wahrscheinlich nicht für viele Arten von kommerziellen Inhalten verwendet, ist aber in allen Browsern, die EME unterstützen, vollständig interoperabel. Das ist auch hilfreich beim Testen von EME-Implementierungen und Anwendungen mit EME, ohne dass ein Inhaltsschlüssel von einem Lizenzserver angefordert werden muss. Unter simpl.info/ck finden Sie ein einfaches Beispiel für einen Löschschlüssel (Clear Key). Nachfolgend finden Sie eine Schritt-für-Schritt-Anleitung des Codes, der den oben beschriebenen Schritten entspricht, allerdings ohne Interaktion mit dem Lizenzserver.

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

Zum Testen dieses Codes benötigen Sie ein verschlüsseltes Video zur Wiedergabe. Das Video kann für WebM gemäß der Anleitung für webm_crypt verschlüsselt werden. Es sind auch kommerzielle Dienstleistungen verfügbar (mindestens für ISO BMFF/MP4). Weitere Lösungen werden derzeit entwickelt.

Media Source Extensions (MSE)

Das HTMLMediaElement ist einfach schön.

Medien können einfach geladen, decodiert und wiedergegeben werden, indem eine src-URL angegeben wird:

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

Die Media Source API ist eine Erweiterung von HTMLMediaElement, die eine genauere Kontrolle über die Medienquelle ermöglicht, indem JavaScript Streams für die Wiedergabe aus Videoblöcken erstellt. Dies ermöglicht wiederum Techniken wie adaptives Streaming und zeitversetztes Fernsehen.

Warum ist MSE für EME wichtig? Weil kommerzielle Contentanbieter nicht nur geschützte Inhalte verbreiten können, müssen in der Lage sein, die Inhaltsübermittlung an die Netzwerkbedingungen und andere Anforderungen anzupassen. Netflix beispielsweise ändert die Bitrate des Streams dynamisch, wenn sich die Netzwerkbedingungen ändern. EME funktioniert mit der Wiedergabe von Medienstreams, die über eine MSE-Implementierung bereitgestellt werden, genau wie bei Medien, die über ein src-Attribut bereitgestellt werden.

Wie kann ich Medien, die mit verschiedenen Bitraten codiert sind, aufschlüsseln und wiedergeben? Weitere Informationen finden Sie im Abschnitt zu DASH unten.

Sie können MSE unter simpl.info/mse in Aktion sehen. Für dieses Beispiel wird ein WebM-Video mithilfe der File APIs in fünf Blöcke unterteilt. In einer Produktionsanwendung werden Videoblöcke über Ajax abgerufen.

Zuerst wird ein SourceBuffer erstellt:

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

Der gesamte Film wird dann an ein Videoelement "gestreamt", indem jeder Block mit der Methode addBuffer() angefügt wird:

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

Weitere Informationen zu MSE finden Sie im HTML5 Rocks-Artikel.

Dynamisch adaptives Streaming über HTTP (DASH)

Multigeräte-, plattform-, mobil: Wie auch immer Sie es nennen, das Web wird oft unter den Bedingungen einer wechselbaren Konnektivität erlebt. Die dynamische, adaptive Bereitstellung ist entscheidend, um mit Bandbreitenbeschränkungen und Schwankungen in der Welt der vielen Geräte umgehen zu können.

DASH (oder MPEG-DASH) wurde entwickelt, um in einer unzuverlässigen Welt die bestmögliche Medienübermittlung sowohl für Streaming als auch für Downloads zu ermöglichen. Einige andere Technologien funktionieren ähnlich, z. B. HTTP Live Streaming (HLS) von Apple und Smooth Streaming von Microsoft. DASH ist jedoch die einzige Methode für das adaptive Bitrate-Streaming über HTTP, das auf einem offenen Standard basiert. DASH wird bereits von Websites wie YouTube verwendet.

Was hat das mit EME und MSE zu tun? MSE-basierte DASH-Implementierungen können mithilfe der vorhandenen HTTP-Infrastruktur ein Manifest parsen, Videosegmente mit geeigneter Bitrate herunterladen und bei Bedarf an ein Videoelement übertragen.

Mit anderen Worten: DASH ermöglicht Anbietern von kommerziellen Inhalten das adaptive Streaming von geschützten Inhalten.

DASH hält, was auf der Dose steht:

  • Dynamisch:reagiert auf sich ändernde Bedingungen
  • Adaptiv:Passt sich an die entsprechende Audio- oder Video-Bitrate an.
  • Streaming:Nutzer können Inhalte streamen und herunterladen.
  • HTTP:Ermöglicht die Inhaltsübermittlung mit dem Vorteil von HTTP ohne die Nachteile eines herkömmlichen Streamingservers.

Die BBC hat damit begonnen, Teststreams mit DASH anzubieten:

Zusammenfassung:

  1. Medien werden mit verschiedenen Bitraten codiert.
  2. Die Dateien mit den verschiedenen Bitraten werden von einem HTTP-Server zur Verfügung gestellt.
  3. Eine Client-Webanwendung wählt mithilfe von DASH die Bitrate aus, die abgerufen und wiedergegeben werden soll.

Im Rahmen des Videosegmentierungsprozesses wird programmatisch ein XML-Manifest erstellt, das als Media Presentation Description (MPD) bezeichnet wird. Dies beschreibt Anpassungssätze und Darstellungen mit Dauer und URLs. Eine MPD-Datei sieht so aus:

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

Die XML-Datei stammt aus der MPD-Datei, die für den DASH-Demo-Player von YouTube verwendet wird.

Laut DASH-Spezifikation könnte eine MPD-Datei theoretisch als src für ein Video verwendet werden. Um Webentwicklern mehr Flexibilität zu geben, haben Browseranbieter jedoch die DASH-Unterstützung für JavaScript-Bibliotheken mit MSE wie dash.js beibehalten. Durch die Implementierung von DASH in JavaScript kann der Anpassungsalgorithmus weiterentwickelt werden, ohne dass Browserupdates erforderlich sind. Mit MSE können außerdem alternative Manifestformate und Übermittlungsmechanismen getestet werden, ohne dass Browseränderungen erforderlich sind. Mit dem Shaka Player von Google wird ein DASH-Client mit EME-Unterstützung implementiert.

Im Mozilla Developer Network findest du eine Anleitung dazu, wie WebM-Tools und FFmpeg verwendet werden können, um Videos zu segmentieren und eine MPD-Datei zu erstellen.

Fazit

Die Nutzung des Internets zur Bereitstellung kostenpflichtiger Video- und Audioinhalte nimmt stark zu. Scheinbar können mit jedem neuen Gerät – ob Tablet, Spielekonsole, internetfähiger Fernseher oder Set-Top-Box – Medien der wichtigsten Contentanbieter über HTTP gestreamt werden. <video> und <audio> werden inzwischen von über 85 % der mobilen und Desktop-Browser unterstützt. Cisco schätzt, dass bis 2017 der Anteil von 80 bis 90 % des weltweiten Internettraffics von Verbraucherinnen und Verbrauchern schätzungsweise 80 bis 90 % betragen wird. In diesem Zusammenhang wird die Browserunterstützung für die Verbreitung geschützter Inhalte wahrscheinlich an Bedeutung gewinnen, da Browseranbieter die Unterstützung für APIs einschränken, auf die die meisten Medien-Plug-ins angewiesen sind.

Weitere Informationen

Spezifikationen und Standards

Artikel