EME WTF?

Rozszerzenia zaszyfrowanych multimediów

Rozszerzenia zaszyfrowanych multimediów udostępniają interfejs API, który umożliwia aplikacjom internetowym interakcję z systemami ochrony treści w celu odtwarzania zaszyfrowanych plików audio i wideo.

Narzędzie EME zostało zaprojektowane tak, aby umożliwić korzystanie z tej samej aplikacji i zaszyfrowanych plików w dowolnej przeglądarce, niezależnie od używanego systemu ochrony. Pierwszy z nich jest możliwa dzięki standardowym interfejsom API i przepływowi, natomiast drugi przebieg jest możliwa dzięki koncepcji Common Encryption.

EME jest rozszerzeniem specyfikacji HTMLMediaElement – stąd ta nazwa. „Rozszerzenie” oznacza, że obsługa EME przez przeglądarkę jest opcjonalna: jeśli przeglądarka nie obsługuje zaszyfrowanych multimediów, nie będzie mogła odtwarzać zaszyfrowanych multimediów. Zgodność ze specyfikacją HTML nie jest jednak wymagane. Na podstawie specyfikacji EME:

Implementacje EME wykorzystują te komponenty zewnętrzne:

  • System kluczy: mechanizm ochrony treści (DRM). EME nie definiuje samych systemów kluczy z wyjątkiem Clear Key (więcej informacji na ten temat znajdziesz poniżej).
  • Moduł odszyfrowywania treści (CDM): program lub mechanizm sprzętowy działający po stronie klienta, który umożliwia odtwarzanie zaszyfrowanych multimediów. Podobnie jak w przypadku systemów kluczy, EME nie definiuje żadnych systemów CDM i udostępnia interfejs umożliwiający aplikacjom interakcję z dostępnymi systemami CDM.
  • Serwer licencji (klucz): współpracuje z CDM, aby dostarczać klucze do odszyfrowywania multimediów. Za negocjacje z serwerem licencji odpowiada aplikacja.
  • Usługa pakowania: koduje i szyfruje multimedia na potrzeby dystrybucji i używania.

Pamiętaj, że aplikacja korzystająca z EME współpracuje z serwerem licencji, aby uzyskać klucze umożliwiające odszyfrowanie, ale tożsamość i uwierzytelnianie użytkowników nie są częścią EME. Pobranie klawiszy umożliwiających odtwarzanie multimediów ma miejsce po (opcjonalnie) uwierzytelnieniu użytkownika. Usługi takie jak Netflix muszą uwierzytelniać użytkowników w ramach aplikacji internetowej: gdy użytkownik loguje się w aplikacji, aplikacja określa jego tożsamość i uprawnienia.

Jak działa EME?

Oto jak współdziałają komponenty EME, zgodnie z przykładem kodu poniżej:

  1. Aplikacja internetowa próbuje odtworzyć dźwięk lub film, który ma co najmniej 1 zaszyfrowany strumień.
  2. Przeglądarka rozpoznaje, że multimedia są zaszyfrowane (szczegóły znajdziesz w polu poniżej) i uruchamia zdarzenie encrypted z metadanymi (initData) uzyskanymi z multimediów dotyczących tego szyfrowania.
  3. Aplikacja obsługuje zdarzenie encrypted:
    1. Jeśli z elementem multimedialnym nie został powiązany żaden obiekt MediaKeys, najpierw wybierz dostępny system kluczy, korzystając z navigator.requestMediaKeySystemAccess(), aby sprawdzić dostępne systemy kluczy, a następnie utwórz obiekt MediaKeys dla dostępnego systemu kluczy za pomocą obiektu MediaKeySystemAccess. Pamiętaj, że inicjowanie obiektu MediaKeys powinno nastąpić przed pierwszym zdarzeniem encrypted. Aplikacja uzyskuje adres URL serwera licencji niezależnie od wyboru dostępnego systemu kluczy. Obiekt MediaKeys reprezentuje wszystkie klucze dostępne do odszyfrowywania multimediów dla elementu audio lub wideo. Reprezentuje instancję CDM i zapewnia dostęp do CDM, w szczególności na potrzeby tworzenia sesji kluczy, które służą do uzyskiwania kluczy z serwera licencji.
    2. Po utworzeniu obiektu MediaKeys przypisz go do elementu medialnego: setMediaKeys() wiąże obiekt MediaKeys z obiektem HTMLMediaElement, dzięki czemu jego klucze mogą być używane podczas odtwarzania, tj. podczas dekodowania.
  4. Aplikacja tworzy MediaKeySession, wywołując createSession() w MediaKeys. Spowoduje to utworzenie identyfikatora MediaKeySession, który reprezentuje okres obowiązywania licencji i jej kluczy.
  5. Aplikacja generuje żądanie licencji, przekazując do CDM dane multimedialne uzyskane w module obsługi encrypted, wywołując metodę generateRequest() w interfejsie MediaKeySession.
  6. CDM uruchamia zdarzenie message, czyli żądanie uzyskania klucza z serwera licencji.
  7. Obiekt MediaKeySession odbiera zdarzenie message, a aplikacja wysyła komunikat do serwera licencji (np. przez XHR).
  8. Aplikacja otrzymuje odpowiedź od serwera licencji i przekazuje dane do CDM przy użyciu metody update() interfejsu MediaKeySession.
  9. CDM odszyfrowuje multimedia przy użyciu kluczy zawartych w licencji. Można użyć prawidłowego klucza z dowolnej sesji w obrębie zasobów MediaKey powiązanej z elementem multimedialnym. CDM uzyska dostęp do klucza i zasady, które są indeksowane według identyfikatora klucza.
  10. Odtwarzanie multimediów zostanie wznowione.

Uff...

Pamiętaj, że między CDM a serwerem licencji może być wymienianych wiele wiadomości, a cała komunikacja w tym procesie jest nieprzejrzysta do przeglądarki i aplikacji: wiadomości są rozumiene tylko przez CDM i serwer licencji, jednak w warstwie aplikacji można zobaczyć typ wiadomości wysyłanej przez CDM. Prośba o licencję zawiera potwierdzenie ważności (i relacji zaufania) CDM, a także klucz do szyfrowania kluczy treści w licencji.

...ale do czego służą strategie zarządzania relacjami z klientami (CDM)?

Implementacja EME nie zapewnia sposobu odszyfrowywania multimediów – udostępnia tylko interfejs API dla aplikacji internetowej, który umożliwia interakcję z modułami odszyfrowywania treści.

Specyfikacja EME nie definiuje funkcji CDM. Może ono obsługiwać dekodowanie (dekompresję) multimediów oraz odszyfrowywanie. Istnieje kilka potencjalnych opcji zarządzania relacjami z klientami (CDM):

  • Tylko odszyfrowywanie, co umożliwia odtwarzanie przy użyciu zwykłego potoku multimediów, na przykład za pomocą elementu <video>.
  • Odszyfrowywanie i dekodowanie, czyli przekazywanie klatek wideo do przeglądarki w celu renderowania.
  • Odszyfrowywanie i dekodowanie, renderowanie bezpośrednio na sprzęcie (np. w GPU).

Możesz udostępnić CDM dla aplikacji internetowej na kilka sposobów:

  • Połącz CDM z przeglądarką.
  • Rozpowszechniaj CDM oddzielnie.
  • Skompilowanie CDM w systemie operacyjnym.
  • Dołącz CDM do oprogramowania układowego.
  • umieszczanie CDM na sprzęcie;

Sposób udostępniania CDM nie jest zdefiniowany w specyfikacji EME, ale we wszystkich przypadkach za sprawdzenie i udostępnienie CDM odpowiada przeglądarka.

EME nie wymaga określonego systemu kluczy. Wśród obecnych przeglądarek na komputerach i urządzeniach mobilnych Chrome obsługuje Widevine, a IE11 – PlayReady.

Uzyskiwanie klucza z serwera licencji

W typowych zastosowaniach komercyjnych treści są szyfrowane i kodowane przy użyciu usługi opakowania lub narzędzia. Po udostępnieniu zaszyfrowanych multimediów online klient internetowy może uzyskać klucz (zawarty w licencji) z serwera licencji i użyć go do odszyfrowania i odtworzenia treści.

Poniższy kod (dostosowany na podstawie przykładów specyfikacji) pokazuje, jak aplikacja może wybrać odpowiedni system kluczy i uzyskać klucz z serwera licencji.

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

Popularne szyfrowanie

Popularne rozwiązania w zakresie szyfrowania pozwalają dostawcom treści zaszyfrować i zapakować zawartość raz na kontener/kodek oraz używać jej z różnymi systemami kluczy, CDM i klientami, czyli z dowolnym CDM obsługującym Common Encryption. Na przykład film spakowany przy użyciu Playready można odtworzyć w przeglądarce za pomocą urządzenia Widevine CDM, który uzyska klucz z serwera licencji Widevine.

W przeciwieństwie do starszych rozwiązań, które działałyby tylko z pełnym stosem branżowym, w tym z pojedynczym klientem, który często zawierał również środowisko wykonawcze aplikacji.

Common Encryption (CENC) to standard ISO określający schemat zabezpieczeń dla standardu ISO BMFF. Podobna koncepcja dotyczy WebM.

Wyczyść klucz

Chociaż EME nie definiuje funkcji DRM, obecnie zgodnie z specyfikacją wszystkie przeglądarki obsługujące EME muszą wdrożyć klarowny klucz. Ten system umożliwia szyfrowanie multimediów za pomocą klucza, a następnie odtwarzanie ich za pomocą samego klucza. Klucz Clear Key można wbudować w przeglądarkę – nie wymaga użycia osobnego modułu odszyfrowywania.

Chociaż klucz Clear Key nie jest używany w przypadku wielu typów treści komercyjnych, jest w pełni kompatybilny ze wszystkimi przeglądarkami obsługującymi EME. Jest on również przydatny do testowania implementacji EME i aplikacji używających EME bez konieczności żądania klucza treści z serwera licencji. Na stronie simpl.info/ck znajdziesz prosty przykład klucza Clear Key. Poniżej znajduje się przewodnik po kodzie, który jest równolegle z krokami opisanymi powyżej, ale nie wymaga interakcji z serwerem licencji.

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

Aby przetestować ten kod, musisz mieć zaszyfrowany film do odtworzenia. Film zaszyfrowany na potrzeby Clear Key można zaszyfrować w WebM zgodnie z instrukcjami webm_crypt. Dostępne są również usługi komercyjne (co najmniej dla formatu ISO BMFF/MP4) i opracowywane są inne rozwiązania.

Rozszerzenia źródła multimediów (MSE)

HTMLMediaElement to istota prostego piękna.

Możemy ładować, dekodować i odtwarzać multimedia, podając tylko adres URL źródła:

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

Interfejs API Media Source jest rozszerzeniem HTMLMediaElementu, które umożliwia dokładniejszą kontrolę nad źródłem multimediów, umożliwiając JavaScript tworzenie strumieni do odtwarzania z „fragmentów” filmu. To z kolei umożliwia użycie takich technik jak adaptacyjne strumieniowe przesyłanie danych i przesuwanie czasu.

Dlaczego MSE jest ważne dla EME? Ponieważ oprócz dystrybucji treści chronionych należy mieć możliwość dostosowania dostawy treści do warunków sieciowych i innych wymagań. Na przykład Netflix dynamicznie zmienia szybkość transmisji bitów wraz ze zmianami warunków sieciowych. EME obsługuje odtwarzanie strumieni multimediów dostarczanych przez implementację MSE, tak jak w przypadku multimediów dostarczanych za pomocą atrybutu src.

Jak podzielić na fragmenty i odtworzyć multimedia zakodowane z różnymi szybkościami transmisji bitów? Więcej informacji znajdziesz w sekcji DASH poniżej.

Działanie MSE można zobaczyć na stronie simpl.info/mse. Na potrzeby tego przykładu film WebM jest podzielony na 5 fragmentów przy użyciu interfejsów API plików. W aplikacji produkcyjnej fragmenty filmów są pobierane za pomocą technologii Ajax.

Najpierw tworzony jest element SourceBuffer:

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

Cały film jest następnie przesyłany strumieniowo do elementu wideo przez dołączanie każdego fragmentu za pomocą metody addBuffer():

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

Więcej informacji o MSE znajdziesz w artykule o HTML5 Rocks.

Dynamiczne adaptacyjne strumieniowe przesyłanie danych przez HTTP (DASH)

Wiele urządzeń, wiele platform, mobilne – jak to się nazywa, internet często funkcjonuje w zmiennych warunkach połączenia. Dynamiczne, adaptacyjne dostarczanie ma kluczowe znaczenie dla radzenia sobie z ograniczeniami i zmiennością przepustowości w świecie, w którym korzysta się z wielu urządzeń.

DASH (inaczej MPEG-DASH) to technologia, która umożliwia najlepsze przesyłanie multimediów w niestabilnym świecie zarówno przy strumieniowaniu, jak i pobieraniu. Podobne działanie wykonuje kilka innych technologii, np. HTTP Live Streaming (HLS) firmy Apple i Smooth Streaming firmy Microsoft. Jednak DASH to jedyna metoda przesyłania strumieniowego z adaptacyjną szybkością transmisji przez HTTP, która opiera się na otwartym standardzie. DASH jest już używany przez witryny takie jak YouTube.

Co ma to wspólnego z EME i MSE? Implementacje DASH oparte na MSE mogą analizować plik manifestu, pobierać segmenty wideo z odpowiednią szybkością transmisji bitów i przekazywać je do elementu wideo, gdy jest głodny, za pomocą istniejącej infrastruktury HTTP.

Inaczej mówiąc, DASH umożliwia dostawcom treści komercyjnych adaptacyjne strumieniowanie chronionych treści.

DASH działa zgodnie z opisem na puszce:

  • Dynamiczna: reaguje na zmieniające się warunki.
  • Adaptacyjna: dostosowuje szybkość transmisji dźwięku lub obrazu.
  • Przesyłanie strumieniowe: umożliwia zarówno strumieniowanie, jak i pobieranie.
  • HTTP: umożliwia dostarczanie treści za pomocą protokołu HTTP, ale bez wad w przypadku tradycyjnego serwera strumieniowego.

BBC zaczyna udostępniać strumienie testowe za pomocą DASH:

Podsumujmy:

  1. Multimedia są kodowane z różną szybkością transmisji bitów.
  2. Pliki o różnej szybkości transmisji bitów są udostępniane z serwera HTTP.
  3. Aplikacja internetowa klienta wybiera szybkość transmisji bitów do pobrania i odtworzenia za pomocą DASH.

W ramach procesu segmentacji wideo automatycznie powstaje plik manifestu XML, który nosi nazwę Media PresentationDescription (MPD). Opisuje zestawy i reprezentacje na potrzeby adaptacji wraz z czasami trwania i adresami URL. Opis prezentacji multimedialnej (MPD) wygląda tak:

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

(Ten plik XML pochodzi z pliku .mpd używanego w pokazowym odtwarzaczu DASH w YouTube).

Zgodnie ze specyfikacją DASH plik MPD teoretycznie można wykorzystać jako src w filmie. Aby zapewnić deweloperom więcej elastyczności, dostawcy przeglądarek zdecydowali się pozostawić obsługę DASH w zakresie bibliotek JavaScriptu, takich jak dash.js. Implementacja DASH w JavaScript umożliwia algorytm adaptacyjny ewoluowanie bez konieczności aktualizowania przeglądarki. Używanie MSE umożliwia też eksperymentowanie z alternatywnymi formatami pliku manifestu i mechanizmami przesyłania bez konieczności wprowadzania zmian w przeglądarce. Shaka Player Google implementuje klienta DASH z obsługą EME.

Mozilla Developer Network zawiera instrukcjedotyczące używania narzędzi WebM i FFmpeg do segmentowania filmów i tworzenia opisu prezentacji multimedialnej (MPD).

Podsumowanie

Wykorzystanie internetu do realizowania płatnych treści wideo i audio rośnie w ogromnym tempie. Wygląda na to, że każde nowe urządzenie – tablet, konsola do gier, telewizor z dostępem do internetu czy dekoder umożliwia strumieniowe przesyłanie treści multimedialnych od największych dostawców treści przez HTTP. Ponad 85% przeglądarek na urządzeniach mobilnych i komputerach obsługuje już <video> i <audio>. Według szacunków firmy Cisco do 2017 roku filmy będą stanowić 80–90% globalnego ruchu klientów w internecie. W tym kontekście obsługa dystrybucji treści chronionych przez przeglądarki staje się coraz ważniejsza, ponieważ dostawcy przeglądarek ograniczają obsługę interfejsów API, z których korzysta większość wtyczek multimedialnych.

Więcej informacji

Specyfikacje i standardy

Artykuły