Apa itu EME?

Ekstensi Media Terenkripsi menyediakan API yang memungkinkan aplikasi web berinteraksi dengan sistem perlindungan konten untuk memungkinkan pemutaran audio dan video terenkripsi.

EME dirancang untuk memungkinkan aplikasi dan file terenkripsi yang sama digunakan di browser apa pun, terlepas dari sistem perlindungan yang mendasarinya. Yang pertama dimungkinkan oleh API dan flow yang terstandardisasi, sedangkan yang kedua dimungkinkan oleh konsep Common Encryption.

EME adalah ekstensi untuk spesifikasi HTMLMediaElement — sehingga namanya. Menjadi 'ekstensi' berarti dukungan browser untuk EME bersifat opsional: jika browser tidak mendukung media terenkripsi, browser tidak akan dapat memutar media terenkripsi, tetapi EME tidak diperlukan untuk kepatuhan spesifikasi HTML. Dari spesifikasi EME:

Proposal ini memperluas HTMLMediaElement yang menyediakan API untuk mengontrol pemutaran konten yang dilindungi.

API ini mendukung kasus penggunaan mulai dari dekripsi kunci yang jelas dan sederhana hingga video bernilai tinggi (dengan implementasi agen pengguna yang sesuai). Pertukaran lisensi/kunci dikontrol oleh aplikasi, yang memfasilitasi pengembangan aplikasi pemutaran yang andal yang mendukung berbagai teknologi perlindungan dan dekripsi konten.

Spesifikasi ini tidak menentukan perlindungan konten atau sistem Pengelolaan Hak Digital. Sebaliknya, API ini menentukan API umum yang dapat digunakan untuk menemukan, memilih, dan berinteraksi dengan sistem tersebut serta dengan sistem enkripsi konten yang lebih sederhana. Penerapan Pengelolaan Hak Digital tidak diperlukan untuk mematuhi spesifikasi ini: hanya sistem Clear Key yang diperlukan untuk diterapkan sebagai dasar pengukuran umum.

API umum mendukung serangkaian kemampuan enkripsi konten sederhana, sehingga fungsi aplikasi seperti autentikasi dan otorisasi diserahkan kepada penulis halaman. Hal ini dicapai dengan mewajibkan pesan khusus sistem perlindungan konten untuk dimediasi oleh halaman, bukan mengasumsikan komunikasi out-of-band antara sistem enkripsi dan lisensi atau server lainnya.

Implementasi EME menggunakan komponen eksternal berikut:

  • Key System: Mekanisme perlindungan konten (DRM). EME tidak menentukan Sistem Kunci itu sendiri, selain Clear Key (selengkapnya di bawah).
  • Content Decryption Module (CDM): Mekanisme software atau hardware sisi klien yang memungkinkan pemutaran media terenkripsi. Seperti halnya Sistem Kunci, EME tidak menentukan CDM apa pun, tetapi menyediakan antarmuka bagi aplikasi untuk berinteraksi dengan CDM yang tersedia.
  • Server Lisensi (Kunci): Berinteraksi dengan CDM untuk memberikan kunci guna mendekripsi media. Negosiasi dengan server lisensi merupakan tanggung jawab aplikasi.
  • Layanan pengemasan: Mengenkode dan mengenkripsi media untuk distribusi/konsumsi.

Perhatikan bahwa aplikasi yang menggunakan EME berinteraksi dengan server lisensi untuk mendapatkan kunci guna mengaktifkan dekripsi, tetapi identitas dan autentikasi pengguna bukan bagian dari EME. Pengambilan kunci untuk mengaktifkan pemutaran media terjadi setelah (opsional) autentikasi pengguna. Layanan seperti Netflix harus mengautentikasi pengguna dalam aplikasi web mereka: saat pengguna login ke aplikasi, aplikasi akan menentukan identitas dan hak istimewa pengguna.

Bagaimana cara kerja EME?

Berikut adalah cara komponen EME berinteraksi, sesuai dengan contoh kode di bawah ini:

Jika ada beberapa format atau codec yang tersedia, MediaSource.isTypeSupported() atau HTMLMediaElement.canPlayType() dapat digunakan untuk memilih format atau codec yang tepat. Namun, CDM mungkin hanya mendukung subset dari apa yang didukung browser untuk konten yang tidak dienkripsi. Sebaiknya negosiasikan konfigurasi MediaKeys sebelum memilih format dan codec. Jika aplikasi menunggu peristiwa terenkripsi, tetapi MediaKeys menunjukkan bahwa aplikasi tidak dapat menangani format/codec yang dipilih, mungkin sudah terlambat untuk beralih tanpa mengganggu pemutaran.

Alur yang direkomendasikan adalah menegosiasikan MediaKeys terlebih dahulu, menggunakan MediaKeysSystemAccess.getConfiguration() untuk mengetahui konfigurasi yang dinegosiasikan.

Jika hanya ada satu format/codec yang dapat dipilih, Anda tidak perlu getConfiguration(). Namun, sebaiknya siapkan MediaKeys terlebih dahulu. Satu-satunya alasan untuk menunggu peristiwa terenkripsi adalah jika tidak ada cara untuk mengetahui apakah konten dienkripsi atau tidak, tetapi dalam praktiknya hal itu tidak mungkin.

  1. Aplikasi web mencoba memutar audio atau video yang memiliki satu atau beberapa streaming terenkripsi.
  2. Browser mengenali bahwa media dienkripsi (lihat kotak di bawah untuk mengetahui bagaimana proses itu terjadi) dan mengaktifkan peristiwa terenkripsi dengan metadata (initData) yang diperoleh dari media tentang enkripsi tersebut.
  3. Aplikasi menangani peristiwa terenkripsi:

    1. Jika tidak ada objek MediaKeys yang dikaitkan dengan elemen media, pertama-tama pilih Key System yang tersedia dengan menggunakan navigator.requestMediaKeySystemAccess() untuk memeriksa Sistem Kunci yang tersedia, lalu buat objek MediaKeys untuk Key System yang tersedia melalui objek MediaKeySystemAccess. Perlu diperhatikan bahwa inisialisasi objek MediaKeys harus terjadi sebelum peristiwa terenkripsi pertama. Mendapatkan URL server lisensi dilakukan oleh aplikasi secara terpisah dari memilih sistem kunci yang tersedia. Objek MediaKeys mewakili semua kunci yang tersedia untuk mendekripsi media untuk elemen audio atau video. Class ini mewakili instance CDM dan memberikan akses ke CDM, khususnya untuk membuat sesi kunci, yang digunakan untuk mendapatkan kunci dari server lisensi.

    2. Setelah objek MediaKeys dibuat, tetapkan ke elemen media: setMediaKeys() mengaitkan objek MediaKeys dengan HTMLMediaElement, sehingga kuncinya dapat digunakan selama pemutaran, yaitu selama decoding.

  4. Aplikasi ini membuat MediaKeySession dengan memanggil createSession() pada MediaKeys. Tindakan ini akan membuat MediaKeySession, yang menunjukkan masa berlaku lisensi dan kuncinya.

  5. Aplikasi membuat permintaan lisensi dengan meneruskan data media yang diperoleh di pengendali terenkripsi ke CDM, dengan memanggil generateRequest() di MediaKeySession.

  6. CDM memicu peristiwa pesan: permintaan untuk memperoleh kunci dari server lisensi.

  7. Objek MediaKeySession menerima peristiwa pesan dan aplikasi mengirim pesan ke server lisensi (misalnya melalui XHR).

  8. Aplikasi akan menerima respons dari server lisensi dan meneruskan data ke CDM menggunakan metode update() MediaKeySession.

  9. CDM mendekripsi media menggunakan kunci dalam lisensi. Kunci yang valid dapat digunakan, dari sesi mana pun dalam MediaKeys yang terkait dengan elemen media. CDM akan mengakses kunci dan kebijakan, yang diindeks oleh ID Kunci.

Pemutaran media dilanjutkan.

Bagaimana cara browser mengetahui bahwa media dienkripsi?

Informasi ini ada dalam metadata file penampung media, yang akan berformat seperti ISO BMFF atau WebM. Untuk ISO BMFF, ini berarti metadata header, yang disebut kotak informasi skema perlindungan. WebM menggunakan elemen Matroska ContentEncryption, dengan beberapa tambahan khusus WebM. Panduan disediakan untuk setiap penampung di registry khusus EME.

Perhatikan bahwa mungkin ada beberapa pesan antara CDM dan server lisensi, dan semua komunikasi dalam proses ini tidak jelas bagi browser dan aplikasi: pesan hanya dipahami oleh CDM dan server lisensi, meskipun lapisan aplikasi dapat melihat jenis pesan yang dikirim CDM. Permintaan lisensi berisi bukti validitas CDM (dan hubungan kepercayaan) serta kunci yang akan digunakan saat mengenkripsi kunci konten dalam lisensi yang dihasilkan.

Namun, apa yang sebenarnya dilakukan CDM?

Implementasi EME itu sendiri tidak menyediakan cara untuk mendekripsi media: implementasi ini hanya menyediakan API untuk aplikasi web agar dapat berinteraksi dengan Modul Dekripsi Konten.

Fungsi CDM yang sebenarnya tidak ditentukan oleh spesifikasi EME, dan CDM dapat menangani decoding (dekompresi) media serta dekripsi. Dari yang paling tidak hingga paling andal, ada beberapa opsi potensial untuk fungsi CDM:

  • Hanya dekripsi, yang memungkinkan pemutaran menggunakan pipeline media normal, misalnya melalui elemen <video>.
  • Dekripsi dan decoding, yang meneruskan frame video ke browser untuk dirender.
  • Dekripsi dan decoding, yang dirender secara langsung di hardware (misalnya, GPU).

Ada beberapa cara untuk menyediakan CDM ke aplikasi web:

  • Paketkan CDM dengan browser.
  • Mendistribusikan CDM secara terpisah.
  • Build CDM ke dalam sistem operasi.
  • Menyertakan CDM dalam firmware.
  • Menyematkan CDM dalam hardware.

Cara penyediaan CDM tidak ditentukan oleh spesifikasi EME, tetapi dalam semua kasus, browser bertanggung jawab untuk memeriksa dan mengekspos CDM.

EME tidak mewajibkan Sistem Kunci tertentu; di antara browser desktop dan seluler saat ini, Chrome mendukung Widevine dan IE11 mendukung PlayReady.

Mendapatkan kunci dari server lisensi

Dalam penggunaan komersial biasa, konten akan dienkripsi dan dienkode menggunakan layanan atau alat pengemasan. Setelah media terenkripsi tersedia secara online, klien web dapat memperoleh kunci (yang terdapat dalam lisensi) dari server lisensi dan menggunakan kunci tersebut untuk mengaktifkan dekripsi dan pemutaran konten.

Kode berikut (diadaptasi dari contoh spesifikasi) menunjukkan cara aplikasi memilih sistem kunci yang sesuai dan mendapatkan kunci dari server lisensi.

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

Enkripsi umum

Dengan solusi Enkripsi Umum, penyedia konten dapat mengenkripsi dan mengemas kontennya sekali per container/codec, serta menggunakannya dengan berbagai Sistem Kunci, CDM, dan klien: yaitu, CDM apa pun yang mendukung Enkripsi Umum. Misalnya, video yang dikemas menggunakan Playready dapat diputar di browser menggunakan CDM Widevine yang mendapatkan kunci dari server lisensi Widevine.

Hal ini berbeda dengan solusi lama yang hanya akan berfungsi dengan stack vertikal lengkap, termasuk satu klien yang sering kali juga menyertakan runtime aplikasi.

Common Encryption (CENC) adalah standar ISO yang menentukan skema perlindungan untuk ISO BMFF; konsep serupa berlaku untuk WebM.

Hapus Kunci

Meskipun EME tidak menentukan fungsi DRM, spesifikasi saat ini mewajibkan semua browser yang mendukung EME harus menerapkan Clear Key. Dengan sistem ini, media dapat dienkripsi dengan kunci, lalu diputar kembali hanya dengan memberikan kunci tersebut. Clear Key dapat disertakan ke dalam browser: tidak memerlukan penggunaan modul dekripsi terpisah.

Meskipun tidak mungkin digunakan untuk banyak jenis konten komersial, Clear Key dapat dioperasikan secara penuh di semua browser yang mendukung EME. Hal ini juga berguna untuk menguji penerapan EME, dan aplikasi yang menggunakan EME, tanpa perlu meminta kunci konten dari server lisensi. Ada contoh Clear Key sederhana di simpl.info/ck. Berikut adalah panduan kode, yang sejajar dengan langkah-langkah yang dijelaskan di atas, meskipun tanpa interaksi server lisensi.

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

Untuk menguji kode ini, Anda memerlukan video terenkripsi untuk diputar. Mengenkripsi video untuk digunakan dengan Clear Key dapat dilakukan untuk WebM sesuai petunjuk webm_crypt. Layanan komersial juga tersedia (setidaknya untuk ISO BMFF/MP4) dan solusi lainnya sedang dikembangkan.

HTMLMediaElement adalah makhluk yang indah dan sederhana.

Kita dapat memuat, mendekode, dan memutar media cukup dengan memberikan URL src:

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

Media Source API adalah ekstensi untuk HTMLMediaElement yang memungkinkan kontrol yang lebih terperinci terhadap sumber media, dengan mengizinkan JavaScript membuat streaming untuk pemutaran dari 'bagian' video. Hal ini pada akhirnya memungkinkan teknik seperti streaming adaptif dan pergeseran waktu.

Mengapa MSE penting bagi EME? Karena selain mendistribusikan konten yang dilindungi, penyedia konten komersial harus dapat menyesuaikan pengiriman konten dengan kondisi jaringan dan persyaratan lainnya. Netflix, misalnya, secara dinamis mengubah kecepatan bit streaming saat kondisi jaringan berubah. EME berfungsi dengan pemutaran streaming media yang disediakan oleh implementasi MSE, seperti halnya media yang disediakan melalui atribut src.

Bagaimana cara memotong dan memutar media yang dienkode pada kecepatan bit berbeda? Lihat bagian DASH di bawah.

Anda dapat melihat cara kerja MSE di simpl.info/mse; untuk tujuan contoh ini, video WebM dibagi menjadi lima bagian menggunakan File API. Dalam aplikasi produksi, potongan video akan diambil melalui AJAX.

Pertama, SourceBuffer dibuat:

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

Seluruh film kemudian 'di-streaming' ke elemen video dengan menambahkan setiap bagian menggunakan metode 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);
  }
};

Cari tahu MSE lebih lanjut di primer MSE.

Multiperangkat, multiplatform, seluler — apa pun namanya, web sering kali digunakan dalam kondisi konektivitas yang dapat berubah. Pengiriman dinamis dan adaptif sangat penting untuk mengatasi batasan bandwidth dan variabilitas di dunia multiperangkat.

DASH (alias MPEG-DASH) dirancang untuk memungkinkan pengiriman media terbaik di dunia yang tidak stabil, baik untuk streaming maupun download. Beberapa teknologi lain melakukan hal yang serupa — seperti HTTP Live Streaming (HLS) Apple dan Smooth Streaming Microsoft — tetapi DASH adalah satu-satunya metode streaming kecepatan bit adaptif melalui HTTP yang didasarkan pada standar terbuka. DASH sudah digunakan oleh situs seperti YouTube.

Apa hubungannya dengan EME dan MSE? Implementasi DASH berbasis MSE dapat menguraikan manifes, mendownload segmen video dengan kecepatan bit yang sesuai, dan menyalurkannya ke elemen video saat elemen tersebut membutuhkan data — menggunakan infrastruktur HTTP yang ada.

Dengan kata lain, DASH memungkinkan penyedia konten komersial untuk melakukan streaming adaptif konten terlindungi.

DASH memang seperti namanya:

  • Dinamis: merespons kondisi yang berubah.
  • Adaptif: beradaptasi untuk memberikan kecepatan bit audio atau video yang sesuai.
  • Streaming: memungkinkan streaming serta download.
  • HTTP: memungkinkan pengiriman konten dengan memanfaatkan HTTP, tanpa kekurangan server streaming tradisional.

BBC telah mulai menyediakan streaming pengujian menggunakan DASH:

Media dienkode beberapa kali dengan kecepatan bit yang berbeda. Setiap encoding disebut Representasi. Segmen ini dibagi menjadi beberapa Segmen Media. Klien memutar program dengan meminta segmen secara berurutan dari representasi melalui HTTP. Representasi dapat dikelompokkan ke dalam Kumpulan Adaptasi representasi yang berisi konten yang setara. Jika klien ingin mengubah kecepatan bit, klien dapat memilih alternatif dari kumpulan adaptasi saat ini dan mulai meminta segmen dari representasi tersebut. Konten dienkode sedemikian rupa untuk memudahkan klien melakukan pengalihan ini. Selain sejumlah segmen media, representasi umumnya juga memiliki Segmen Inisialisasi. Hal ini dapat dianggap sebagai header, yang berisi informasi tentang encoding, ukuran frame, dll. Klien perlu mendapatkan ini untuk representasi tertentu sebelum memakai segmen media dari representasi tersebut.

Ringkasnya:

  1. Media dienkode dengan kecepatan bit yang berbeda.
  2. File dengan berbagai kecepatan bit disediakan dari server HTTP.
  3. Aplikasi web klien memilih kecepatan bit yang akan diambil dan diputar dengan DASH.

Sebagai bagian dari proses segmentasi video, manifes XML yang dikenal sebagai Media Presentation Description (MPD) dibuat secara terprogram. Ini menjelaskan Set dan Representasi Adaptasi, dengan durasi dan URL. MPD terlihat seperti ini:

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

(XML ini diambil dari file .mpd yang digunakan untuk pemutar demo DASH YouTube.)

Menurut spesifikasi DASH, file MPD secara teori dapat digunakan sebagai src untuk video. Namun, untuk memberikan fleksibilitas lebih kepada developer web, vendor browser telah memilih untuk menyerahkan dukungan DASH ke library JavaScript yang menggunakan MSE seperti dash.js. Dengan menerapkan DASH di JavaScript, algoritma adaptasi dapat berkembang tanpa memerlukan update browser. Penggunaan MSE juga memungkinkan eksperimen dengan format manifes dan mekanisme pengiriman alternatif tanpa memerlukan perubahan browser. Shaka Player dari Google mengimplementasikan klien DASH dengan dukungan EME.

Mozilla Developer Network memiliki petunjuk tentang cara menggunakan alat WebM dan FFmpeg untuk menyegmentasi video dan membuat MPD.

Kesimpulan

Penggunaan web untuk menayangkan video dan audio berbayar berkembang dengan kecepatan yang sangat tinggi. Sepertinya setiap perangkat baru, baik tablet, konsol game, TV yang terhubung, atau dekoder, dapat melakukan streaming media dari penyedia konten utama melalui HTTP. Lebih dari 85% browser seluler dan desktop kini mendukung <video> dan <audio>, dan Cisco memperkirakan bahwa video akan menjadi 80 hingga 90 persen traffic internet konsumen global pada tahun 2017. Dalam konteks ini, dukungan browser untuk distribusi konten yang dilindungi kemungkinan akan semakin signifikan, karena vendor browser mengurangi dukungan untuk API yang digunakan sebagian besar plugin media.

Bacaan lebih lanjut

Spesifikasi dan standar

Spesifikasi EME: Draf Editor terbaru Enkripsi Umum (CENC) Ekstensi Sumber Media: Draf Editor terbaru Standar DASH (ya, ini adalah PDF) Ringkasan standar DASH

Artikel

Webinar DTG (sebagian sudah tidak berlaku) What is EME?, oleh Henri Sivonen Media Source Extensions primer MPEG-DASH Test Streams: BBC R&D blog post

Demo

Demo Clear Key: simpl.info/ck Demo Media Source Extensions (MSE) Shaka Player Google menerapkan klien DASH dengan dukungan EME

Masukan