DRM-X 6.0 dove dan logo cabang zaitunDRM-X 6.0Dokumentasi
◎ Bahasa Indonesia

Android SDK dan Media3 native

Unduh pemutar Android dengan daftar putar yang dapat diurutkan dengan menyeret, pengaturan acak dan ulang yang tersimpan, serta fitur melanjutkan pemutaran offline. Integrasikan pemutaran terlindungi menggunakan DRM-X atau Media3 native.

Lihat MarkdownContoh langsungDiperbarui 2026-10-26

Untuk penyesuaian UI, nilai bawaan parameter, dan contoh siklus hidup, baca PLAYER-UI-CONFIGURATION.md. Panel pop-up bawaan pemutar tidak transparan; opasitas teks dan watermark diatur secara terpisah.

Coba demo PHP dengan mengikuti DEMO-QUICKSTART.md di dalam SDK ZIP. Untuk konten Anda sendiri, integrasikan situs web dan backend terlebih dahulu, lalu ikuti MOBILE-AND-TV-APPS.md untuk menghubungkan aplikasi ke sistem login dan aturan akses menonton yang sama.

Unduh Android SDK dan aplikasi contoh dari Console → Developer Tools → Device SDKs dengan langganan Professional atau Enterprise aktif.
Choose your player, keep one license service. Use the DRM-X 6.0 Android SDK for a guided integration, or keep your native Media3 player. Both connect to your authenticated backend and Widevine. The Android SDK supports streaming on compatible API 23+ devices. Persistent offline VOD is supported for phones and tablets; OTT devices use streaming only.

Coba Android pemutar #

Unduh, instal, tekan Play. Aplikasi evaluasi gratis menggunakan video demo publik kami dan masuk secara otomatis. Anda tidak perlu membuat APK atau memasukkan kredensial DRM-X untuk mencobanya.

Unduh Android sampel APK

1.0.0-beta.11 · 12.15 MB · Android 6.0+ (API 23+) · DRM-X 6.0 Player

  1. Buka halaman ini pada telepon Android Anda dan unduh APK.
  2. Buka berkas yang diunduh. Jika Android bertanya, ijinkan instalasi dari peramban atau manajer berkas yang Anda gunakan, lalu tekan Pasang .
  3. Buka DRM-X 6.0 Pemutar , tetap terhubung, dan tekan Putar tombol. Aplikasi meminta lisensi streaming ketika Anda menekan Play.
  4. Buka Pengaturan untuk memilih kualitas, audio, kapsi, atau kecepatan putar. Putar telepon untuk melanjutkan dalam layar penuh.
  5. Buka menu tiga garis pada video lalu pilih Download. Aktifkan Downloaded videos untuk Offline Mode; nonaktifkan untuk Online Mode. Dalam Offline Mode, pilih Delete dari menu video tersebut lalu konfirmasikan. Perpanjangan lisensi memerlukan koneksi dan otorisasi yang masih berlaku; jika terjadi kesalahan offline, Anda dapat tetap offline atau beralih online.

Diunduh media yang sudah diantrian terus di latar belakang. Jika Android menghentikan aplikasi ketika menyiapkan lisensi atau trek, membuka kembali saat tersambung untuk menyelesaikan menyiapkan butir-butir yang tersisa. Unduhan yang tersimpan. Pemutaran offline hanya menggunakan media yang cached; tidak lengkap atau unduhan kadaluarsa memerlukan pelengkapan atau pembaharuan.

Sampel APK adalah publik dan ditandatangani untuk evaluasi. AAR dan penuh SDK di bawah ini tetap tersedia melalui akun Konsol yang memenuhi syarat. Jauhkan kunci penandatanganan aplikasi sendiri dan login backend terpisah dari demo ini.

APK checksum dan USB instalasi

SHA-256:

b96ba527ddf9abae5bff2f8b85a39a12b59c934aaacde1961c8096aa32524c54

Dengan Android platform -tools dan sebuah otorisasi USB perangkat:

adb install -r drmx-android-player-1.0.0-beta.11.apk

Instal APK ini melalui contoh resmi yang sudah ada tanpa uninstall itu. Kunci penandatanganan dan aplikasi yang sama dengan ID pertahankan pengunduhan, lisensi offline, posisi dan preferensi tersimpan. Jika membangun Anda sendiri menggunakan kunci penandatanganan lain, gunakan aplikasi terpisah ID untuk menjaga kedua aplikasi dan mempertahankan lisensi luring sampel.

Download controls and language names in preview.40#

Connect to the internet before starting a download. Choose the permitted video quality and audio track in the download dialog; available subtitles are included. Audio and subtitle menus display each language in its own language, such as English and 中文. Customer configuration and device DRM support determine the available quality ceiling.

Dalam Offline Mode, buka menu tiga garis video lalu pilih Delete. Konfirmasikan untuk menghapus hanya media terenkripsi dan referensi lisensi lokal video itu; Cancel mempertahankannya. Sambungkan kembali untuk mengunduhnya lagi.

New downloads require an internet connection. A queued transfer can pause when connectivity disappears and resume after reconnection. Completed downloads play offline while their device-bound licenses remain valid. Playlist posters and seek-preview thumbnails remain cached as described below.

Offline artwork and downloads in preview.39#

Playlist posters and seek-preview thumbnails are saved on the device with their artwork metadata. They remain available after closing and reopening the app without a network connection. After upgrading, open the offline demonstration library once while connected and allow the images to load; existing encrypted videos do not need to be downloaded again.

Wait for each video to finish downloading, then switch off both Wi-Fi and mobile data and choose Play offline. Test the downloaded playlist, selected audio and included captions. Reconnect before using Renew when a stored license needs renewal. Install this update over the existing sample to retain its downloads and settings.

Publisher images must be available while connected before they can appear offline. Images use a bounded 96 MiB app-private store, separate from encrypted media and licenses; older artwork can be evicted when this store fills. Missing images do not bypass DRM or prevent an otherwise valid downloaded video from playing.

Live DRM in preview.13#

Dirilis 17 September 2026. Aplikasi telepon dan pelanggan SDK menyertakan Tes 4K Live DRM. Pemutaran langsung menggunakan manifes baru dan tidak dapat diunduh. Acara uji coba hanya tersedia selama siaran aktif. Ikuti AWS dan OBS panduan pengaturan untuk membuat acara Anda sendiri.

Apa yang baru dalam Android pemutar preview] 12 #

Dirilis 14 September 2026. Perbaikan download pemutaran membuat permintaan lisensi yang tak terduga ketika Widevine lisensi kadaluwarsa atau memiliki satu menit atau kurang. Ini bisa muncul sebagai Media3 galat 6004, sebuah HTTP error ketika tersambung, atau DNS error ketika terputus.

pemutar sekarang restore lisensi yang diunduh secara lokal. Lisensi yang sah mempertahankan sisa waktu pemutaran mereka; lisensi yang kadaluwarsa menampilkan Hubungkan dan tekan Renew . Peraturan keabsahan dan perlindungan Lisensi tetap dipaksakan oleh perangkat. Diagnostik juga termasuk status numerik HTTP ketika operasi jaringan gagal, tanpa mengekspos header permintaan, URL, atau lisensi tanggapan.

  1. Instal preview-12 melalui aplikasi resmi yang ada. Simpan data aplikasi dan unduh saat ini.
  2. Jika WayMaker atau video lain yang diunduh perlu diperbaharui, menyambung ke internet, pilih video itu dan tekan Renew .
  3. Tunggu Lisensi diperbarui. Siap untuk bermain offline. , lalu pilih Putar offline . Video terenkripsi masih diunduh.

Rilis ini mempertahankan kontrol dan fitur playlist preview.11. Aplikasi evaluasi publik dan pemutar bawaannya menampilkan versi preview.12. Pada saat rilis tersebut, Console SDK yang didistribusikan terpisah adalah preview.10 / bundel 20260910.8.

Apa yang baru dalam Android pemutar preview} 11 #

Aplikasi evaluasi ini termasuk kontrol terbaru pemutar dan perbaikan playlist, dengan gaya interaksi konsisten di seluruh Android dan Apple pemutar.

  • Kontrol di dalam video: tengah Play / Pause, Sebelumnya dan Selanjutnya untuk multi- video playlist, CC, Pengaturan dan tombol layar penuh. Kontrol menyembunyikan setelah dua detik sambil bermain; tekan video untuk mengungkapkan mereka lagi.
  • Captions and playing settings: gunakan CC untuk menampilkan atau menyembunyikan subtitle yang tersedia, dan Pengaturan untuk memilih bahasa, audio, kecepatan putar dan kualitas yang diijinkan. Pilihan offline hanya gunakan trek yang disertakan dalam unduhan.
  • Seret untuk memesan ulang: seret suatu butir playlist untuk mengubah posisinya. Perintah tersimpan bertahan dari sebuah aplikasi restart dan tercermin dalam daftar putar yang diunduh. Menghidupkan ulang menjaga video saat ini diputar. Sentuh penanganan untuk akses Pindah ke atas / Pindahkan ke bawah aksi.
  • Shuffle tersimpan dan ulangi: mode yang dipilih tetap disorot dan bertahan dalam pemutaran online dan offline. Dengan mengocok dan mengulang baik, video mengikuti urutan yang disimpan dan berhenti setelah item final. Menu playlist "Play next" aksi telah dihapus.
  • Lanjutkan di mana Anda berhenti: setiap video disimpan VOD posisi dikembalikan ketika Anda kembali ke pemutaran streaming atau diunduh. Kemajuan disimpan di perangkat ini untuk penampil saat ini; ini tidak sinkronisasi antar perangkat.

Atur daftar putar, simpan urutannya, dan lanjutkan pemutaran. Menonton offline memerlukan unduhan yang selesai serta lisensi offline yang dapat digunakan dan belum kedaluwarsa.

Rilis preview.11 memperbarui APK evaluasi publik. Console SDK saat ini adalah 1.0.0-beta.14, bundel 20261028.1; petunjuk integrasi berikut menggunakan bundel tersebut.

1. Pilih jalur integrasi #

Unduh melalui Developer Tools → Device SDKs. Penggunaan SDK memerlukan akses Professional atau Enterprise aktif, termasuk evaluasi Professional yang disetujui. Standard tetap menyediakan APIs native untuk pemutar Anda sendiri. Akses SDK diperiksa saat pembuatan sesi dan perpanjangan lisensi; lisensi offline berakhir paling lambat pada akhir masa layanan akun.

Pratinjau saat ini: 1.0.0-beta.14 bundel pelanggan 20261028.1 · Media3 1.11.0. ZIP berisi sebuah repositori Maven lokal, Gradle wrapper, jengkel README, telepon /TV/ sampel asli, sebuah sampel telepon terinstall APK in apks/, dan catatan integrasi offline. Ini tidak berisi pelanggan kredensial atau film yang dilindungi.

2. Siapkan isi dan backend #

  1. Paket dan menerbitkan Widevine -kompatibel dengan terenkripsi DASH judul dalam DRM-X Lingkungan. Salin yang tepat Diterbitkan Content ID .
  2. Mengkonfigurasi server Anda Environment ID, Site Key dan Access KeyGunakan yang dapat diunduh PHP integrasi untuk memulai pekerjaan.
  3. Instal cek log masuk dan hak pengguna Anda. Aplikasi Anda memanggil backend Anda dengan token pengguna yang kurang terpakai; backend Anda memilih isi, kebijakan, dan URL terpercaya.
  4. Jalankan pemutaran daring pertama pada perangkat yang mampu melakukan fisik Widevine. Tambahkan akuisisi luring hanya setelah kontrak streaming bekerja.

Video terenkripsi dan lisensi DRM dikirim melalui jalur yang berbeda

CDN mengirimkan video terenkripsi. Backend Anda memeriksa akses, dan layanan lisensi DRM mengizinkan dekripsi pada perangkat yang kompatibel.

  1. Penyimpanan / CDN

    Mengirimkan media terenkripsi ke pemutar.

  2. Backend Anda

    Memeriksa hak menonton pengguna dan meminta otorisasi pemutaran dari DRM-X.

  3. DRM-X

    Memvalidasi otorisasi pemutaran dan memproses permintaan lisensi DRM.

  4. Pemutar + perangkat

    Menggunakan sistem DRM perangkat untuk memperoleh lisensi dan memutar media terenkripsi.

Pengiriman media dan lisensi terpisah. Hanya backend pelanggan yang menyimpan DRM-X server kredensial.
Ubah nilai iniDi mana ia beradaArtinya
sessionEndpointAndroidEndpoint-sesi putar HTTPS Anda, bukan server lisensi mentah URL.
offlineEndpointAndroid, hanya luringAnda memperoleh / memperbaharui / rilis otorisasi titik akhir.
contentIdAndroid permintaan; backend validatesPelihat yang diproteksi Content ID berhak untuk diakses.
token akses pelangganApp memori setelah loginToken pengguna Anda yang singkat, tidak pernah sebuah Site Key atau Access Key.
Environment ID, Site Key, Access KeyHanya konfigurasi rahasia serverDRM-X akun kredensial. Jangan pernah mengirim mereka dalam sebuah APK, HTML atau sumber kontrol.
Kebijakan dan muatan terpercaya URLBackend AndaPemetaan hak bisnis dan paket. Jangan terima ini dari aplikasi.

3. Pasang dan jalankan SDK #

Buka yang diekstrak ZIP's root folder dalam Android Studio. JDK 17, Android SDK 36 dan Android Gradle Plugin 9.2.0; wrapper yang disertakan menggunakan Gradle 9.4.1. Perangkat minimal API adalah 23. Ketergantungan pembangunan memerlukan akses internet. Pilih samples.phone dan Jalankan. Nilai awalnya menggunakan backend demo publik dan judul; ikuti tandanya dan streaming kontrol sebelum beralih ke backend Anda sendiri.

# Windows
gradlew.bat :samples:phone:assembleDebug
# macOS / Linux
sh gradlew :samples:phone:assembleDebug

Untuk aplikasi yang sudah ada, salin ZIP 's maven/ folder ke dalam root projek Anda. Dalam settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        maven { url = uri(rootDir.resolve("maven")) }
        google()
        mavenCentral()
    }
}

Dalam modul aplikasi Anda build.gradle.kts:

dependencies {
    implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14")
}
Instal standalone AAR saja

Masukan AAR dalam modul aplikasi Anda libs/. Sebuah flat AAR tidak memiliki alternatif otomatis dependensi resolusi, jadi tambahkan tiga pencocokan Media3 ketergantungan sendiri. Jangan instal kedua bentuk.

implementation(files("libs/drmx-universal-player-1.0.0-beta.14.aar"))
implementation("androidx.media3:media3-exoplayer:1.11.0")
implementation("androidx.media3:media3-exoplayer-dash:1.11.0")
implementation("androidx.media3:media3-ui:1.11.0")

Dalam AndroidManifest.xml, izinkan akses internet dan menonaktifkan backup untuk sampel minimal ini. Dalam aplikasi produksi yang membutuhkan backup, khususnya mengecualikan device- terikat metadata lisensi offline.

<uses-permission android:name="android.permission.INTERNET" />
<application android:allowBackup="false" android:usesCleartextTraffic="false" ... >

Cari contoh telepon MainActivity.kt untuk drmx.sessionEndpoint, drmx.offlineEndpoint, drmx.contentId dan customer.loginEndpoint. Ubah pengaturan fallback yang ditandai, atau lewatkan Intent extra dari aplikasi setelah log masuk. Pass customer.accessToken di runtime. ZIP README menjelaskan setiap parameter dan demo kait untuk diganti.

Bangun suatu tes APK memakai AAR #

Contoh ini menggunakan proyek telepon yang dapat dijalankan dari SDK ZIP dan secara eksplisit menghubungkan standar AAR. Pendekatan yang sama bekerja untuk aplikasi yang ada Android.

  1. Unduh dan ekstrak penuh Android SDK ZIP . Buka folder root di Android Studio.
  2. Simpan libs/drmx-universal-player-1.0.0-beta.14.aar dalam akar yang diekstrak. Gunakan JDK 17, Android SDK 36 dan yang disertakan Gradle wrapper.
  3. Dalam samples/phone/build.gradle.kts, ganti DRM-X Maven ketergantungan dengan entri berikut di dalam dependencies. Simpan sisa berkas build.
implementation(files(rootProject.file("libs/drmx-universal-player-1.0.0-beta.14.aar")))
implementation("androidx.media3:media3-exoplayer:1.11.0")
implementation("androidx.media3:media3-exoplayer-dash:1.11.0")
implementation("androidx.media3:media3-ui:1.11.0")

Hapus implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14") sehingga sama SDK tidak disertakan dua kali. AAR membutuhkan tiga Media3 ketergantungan yang ditampilkan di atas.

  1. Untuk menjaga demo yang diunduh bersama dengan membangun pengujian Anda sendiri, ubah modul telepon applicationId ke com.example.drmxtest. Biarkan namespace nya tidak berubah bagi sampel yang diberikan ini.
  2. Bangun APK, lalu pasang pada USB- tersambung Android perangkat dengan USB debugging yang diijinkan.
# Windows, from the extracted SDK root
gradlew.bat :samples:phone:assembleDebug
adb install -r samples/phone/build/outputs/apk/debug/phone-debug.apk

# macOS / Linux
sh gradlew :samples:phone:assembleDebug
adb install -r samples/phone/build/outputs/apk/debug/phone-debug.apk

Buka aplikasi tes dan tap Play. Sumber telepon yang diberikan menggunakan demo publik yang sama awalnya. Untuk menguji judul Anda sendiri, ganti sesi yang ditandai / offline / login titik akhir dan Published Content ID in MainActivity.kt, dan memasok otentikasi singkat milik pengguna anda sendiri pada waktu berjalan. Server anda menyimpan Site Key dan Access Key.

Kontrolnya berasal dari... DrmXPlayerView di AARAktivitas yang diberikan menunjukkan Play- to-otorisasi, rotasi responsif, kepemilikan download, pemutaran offline dan pembaharuan. Lihat. contoh integrasi kontrol di bawah ketika menambahkan tampilan ini ke aplikasi anda sendiri.

4. Permintaan server dan kebijakan baku #

Aplikasi Android meminta backend Anda untuk sesi putar. Backend Anda mengkonfirmasi pengguna, verifikasi hak, peta Content ID untuk menerbitkan media dan panggilan DRM-X. Lihat Sesi Playback API untuk kontrak streaming lengkap.

{
  "contentId": "YOUR-PUBLISHED-CONTENT-ID",
  "contentType": "vod",
  "drmSystem": "widevine",
  "playbackMode": "streaming",
  "platformCapabilities": {
    "clientPlatform": "android",
    "drmSystems": ["widevine"], "manifestTypes": ["dash"],
    "maximumHeight": 1080, "secureDecoder": false, "persistentState": false
  }
}

Kemampuan konservatif di atas jangan menegaskan Widevine L1. Gunakan SDK 's kapabilitas pembantu, atau penilaian perangkat Anda sendiri, untuk perangkat aktual. Kemampuan klien membantu seleksi kompabilitas; mereka tidak pernah memberikan kualitas hak.

Sampel PHP bawaan bagi multi-tier-standard untuk putar daring. Pilih kebijakan tunggal yang didokumentasikan ketika paket Anda menggunakan satu kunci. Kebijakan Anda harus cocok dengan model kunci paket dan hak-hak penampil. Ubah konfigurasi server atau pengaturan lingkungannya DRMX_LICENSE_POLICY_TEMPLATE, bukan parameter permintaan dalam pemutar. Lihat lisensi kebijakan .

Offline memerlukan kebijakan persistennya sendiri. Tetapkan playback_policy.persistent ke true dalam kebijakan yang dikeluarkan server dan pilih durasi lisensi, sewa, pemutaran, dan perpanjangan. Referensinya masing-masing menggunakan 7 hari / 3 hari / 48 jam / 7 hari; ini adalah contoh aturan bisnis, bukan persetujuan studio atau standar seluruh platform. Kembalikan metadata yang cocok. Lisensi Widevine tetap resmi.

5. Path A: putar melalui DrmXPlayer #

Di dalam Activity, buat PlayerView, gunakan secure window, lalu panggil prepare setelah login. Wrapper meminta sesi, mengatur Widevine, menerapkan batas tinggi video dari respons, dan melepaskan reservasi streaming saat dihentikan. Impor berasal dari com.haihaisoft.drmx.player dan AndroidX Media3.

val view = PlayerView(this)
setContentView(view)
DrmXPlayer.protectWindow(window)
val player = DrmXPlayer(this, view)
val capabilities = WidevineCapabilities.playbackCapabilities(
    clientPlatform = "android",
    displayMetrics = resources.displayMetrics,
    applicationVersion = "1.0",
)
player.prepare(
    endpoint = "https://customer.example/api/drmx/playback-session",
    contentId = "YOUR-PUBLISHED-CONTENT-ID",
    contentType = "vod", // use live only for a published live title
    capabilities = capabilities,
    customerAuthenticationHeaders = mapOf("Authorization" to "Bearer $userToken"),
)
// Keep player as an Activity/Fragment-owned field in your actual application.
// Stop when your playback lifecycle ends; close when the owner is destroyed.
// player.stop()
// player.close()

Register DrmXPlayerListener untuk otorisasi, siap, bermain, dan error UI. Tampilkan kode kesalahan yang aman daripada membuang tanda-tanda lisensi atau respon tubuh. Gunakan sampel telepon yang disertakan untuk lifecycle lengkap, kontrol layar penuh dan daftar unduh.

Kontrol pemutar dan rotasi tak terganggu #

Sentuh Putar pada video untuk memulai otorisasi. Membuka layar atau Pengaturan tidak memperoleh lisensi streaming. Jeda / lanjutkan, mengubah pengaturan dan memutar telepon mempertahankan sesi saat ini.

Tangkapan layar pengaturan Samsung Android saat ini. Perlindungan tangkapan layar menyembunyikan tampilan utama pemutar.
Tangkapan layar pengaturan Samsung Android saat ini. Perlindungan tangkapan layar menyembunyikan tampilan utama pemutar.
KendaliApa yang bisa dilakukan pelanggan
KualitasPilih Auto atau didukung, resolusi berlisensi dari manifes. Pilihan tidak tersedia menjelaskan lisensi atau perangkat / DRM pembatasan. Auto mematuhi pembatasan yang sama.
Trek audioPilih bahasa audio apapun yang didukung yang disediakan oleh paket.
Subtitles / CCPilih bahasa judul atau mati. Tombol CC muncul ketika judul didukung ada dan menjungkirkannya secara langsung.
Penampilan judulSesuaikan fonta, warna teks dan ukuran, warna latar belakang / jendela dan opasitas. Preferensi disimpan pada perangkat.
Waktu tidur / kecepatan putarPutar di 0.25-2 × kecepatan atau jeda secara otomatis setelah interval yang dipilih.
Seek / layar lengkapScrub the timeline, seek 10 detik, or double-tap either side. Putar ke lansekap tanpa memulai ulang memutar.

Setiap menu video berisi Move up / Move down serta Download saat online atau Delete saat offline. Download dinonaktifkan setelah unduhan selesai. Sakelar Downloaded videos memilih pemutaran offline; hanya trek yang tersimpan dalam cache yang tersedia. Tidak ada tombol terpisah untuk mengunduh, menghapus, atau mengunduh seluruh daftar putar.

Salin log di samping informasi footer salinan kesalahan baru-baru ini sebagai teks, sehingga Anda dapat mengirim dukungan rincian tanpa cuplikan layar. Laporan berisi laporan ID, operasi, SDK/Android/ versi perangkat, numerik CDM/Media3 error, frame stack yang dipilih, koneksi / download / keadaan putar dan sebuah jumlah pengulangan. Mereka tidak termasuk kredensial, URL media, kunci, kunci-set ID, data lisensi dan penuh CDM dump.

Sampel tersebut menyimpan hingga 32 laporan lokal (64 KB) selama lima jam. Ketika tersambung dan ditandatangani masuk, itu mengunggah kesalahan yang tertunda ke backend pelanggan yang terkonfigurasi, paling sekali per 30 detik. Server demo publik mempertahankan peringatan / kesalahan selama lima jam, kombinasi kejadian berulang, tutup berkas di 512 KB dan memangkas setiap menit. Kemajuan rutin dan pemutaran sukses tidak log. Integrasi suai harus secara eksplisit memasok asal mereka sendiri drmx.diagnosticsEndpoint; Kesalahan diagnostik tidak pernah mengganggu pemutaran.

Lisensi offline sudah digunakan? Preview.5 memperbaiki kasus perangkat Samsung Android yang melaporkan CDM 368 dengan “Lisensi dimuat di sesi berbeda.” Menekan Play offline lagi akan melanjutkan pemutar yang ada. Renew pertama-tama merilis sesi DRM-nya, lalu memperbarui lisensi yang disimpan. Unduhan dan lisensi tetap dipertahankan; tidak diperlukan pengaturan ulang data aplikasi.
val player = DrmXPlayer(this)
val controls = DrmXPlayerView(this)
controls.bind(player) // Does not acquire a session or license.
controls.onPlayRequested = {
    player.prepare(endpoint = sessionEndpoint, contentId = contentId,
        capabilities = capabilities, customerAuthenticationHeaders = userHeaders)
}
controls.onFullScreenRequested = { /* resize the existing player view */ }
player.listener = object : DrmXPlayerListener {
    override fun onAuthorizing(contentId: String) { controls.setPreparing(true) }
    override fun onReady(info: DrmXPlaybackInfo) { controls.setPreparing(false) }
    override fun onError(error: PlaybackSessionException) { controls.setPreparing(false) }
}
// Keep protectWindow(window) and the secure video surface.
// At final owner destruction: controls.unbind(); player.close()

Daftar muatan telepon yang diberikan menangani orientasi dan perubahan ukuran layar; itu onConfigurationChanged mengubah pandangan yang sama dan pemutar. Simpan kedua bagian saat menyesuaikannya. Host yang menciptakan Aktivitas harus mempertahankan pemilik pemutaran dan mengatur ulang tampilan. Kematian proses berbeda dari rotasi dan perlu pemulihan keadaan normal. Pilihan 4K / 8K memerlukan isi yang cocok, lisensi hak akses dan dukungan perangkat / DRM; dukungan CDM tetap berlaku.

Untuk gubahan pemutar, stop and release the active offline DRM sesi sebelum memeriksa, memperbaharui atau melepaskan lisensi gigih yang sama. SDK's player.stopForOfflineLicenseOperation { /* call manager here */ } menunggu Media3 antrian putar untuk merilis media. Polos stop() antrian yang bekerja asynchronously. checkLicense() ketika lisensi yang sama sedang diputar; putar sendiri memvalidasi nya CDM negara sampel yang diberikan menunjukkan handoff lengkap dan menonaktifkan tindakan tumpang tindih selama pembaharuan.

6. Cari lisensi dan unduh media #

Siapkan pemutaran offline

Unduhan harus lengkap dan lisensi yang terikat pada perangkat harus masih berlaku.

  1. Unduh media terenkripsi

    Saat online, unduh video serta trek audio dan subtitel yang diperlukan.

  2. Dapatkan lisensi persisten

    Saat online, dapatkan lisensi Widevine yang terikat pada perangkat. Simpan Key-set ID yang bersifat opaque secara privat.

  3. Periksa kesiapan

    Pastikan unduhan sudah lengkap dan lisensinya masih berlaku.

  4. Putar secara offline

    Baca media dari cache dan pulihkan lisensi lokal. Perpanjangan dan pelepasan lisensi memerlukan koneksi internet.

Ready offline = complete encrypted media + a valid device- bound license. Not one places the other.
val offlineManager = DrmXOfflineManager(applicationContext)
val capabilities = WidevineCapabilities.offlinePlaybackCapabilities(
    clientPlatform = "android", // android-tv or android-automotive where applicable
    displayMetrics = resources.displayMetrics,
    applicationVersion = "1.0",
)
offlineManager.acquireLicense(
    endpoint = offlineEndpoint,
    contentId = contentId,
    capabilities = capabilities,
    customerAuthenticationHeaders = customerHeaders,
) { result ->
    result.onSuccess { stored ->
        // Next: select tracks <= stored.maximumHeight and queue their download.
        // Acquisition alone does not download the video.
    }.onFailure { /* show a safe retry/error state */ }
}

The SDK Simpan yang buram Widevine kunci -set ID dan bukan metadata rahasia. CDM Dia tidak bertahan. DRM License Tokens, kunci mentah, tantangan atau bytes respon lisensi. Abaikan com.haihaisoft.drmx.offline.licenses.v1.xml dari backup.

Buat satu pelamar yang dimiliki Media3 SimpleCache, unduh manajer dan basis data. Gunakan DownloadHelper dengan MediItem berlisensi dan pilih video di dalam maximumHeightDalam Media3 1.11 callback siap adalah onPrepared(helper, tracksInfoUpdated)Sampel telepon enqueueDownload dan OfflineTestRuntime tampilkan implementasi yang dapat dijalankan.

// Inside DownloadHelper's prepared callback, after selecting licensed video:
helper.addAudioLanguagesToSelection("en", "zh-Hans", "zh")
helper.addTextLanguagesToSelection(true, "en", "zh-Hans", "zh")
val selected = helper.getDownloadRequest(offlineLicenseId, byteArrayOf())
val request = offlineManager.createDownloadRequest(contentId, selected.streamKeys)
downloadManager.addDownload(request)
helper.release()

Unduh setiap bahasa yang ingin Anda ekspos luring. Memanggil createDownloadRequest tanpa kunci stream mengunduh semua trek, yang umumnya diboros untuk video adaptif. Tampilkan kemajuan unduh dan tunggu untuk Download.STATE_COMPLETEDUntuk transfer latar belakang produksi, mengintegrasikan Media3 DownloadService, batas penyimpanan dan Android foreground- layanan / pemberitahuan persyaratan; antrian demo berjalan in- proses.

Konten offline multi-kunci: Media3 memulihkan satu set kunci ID untuk item pemutaran. Lisensi persistennya harus berisi setiap kunci yang diperlukan untuk video, audio, dan trek terenkripsi lainnya yang dipilih. Uji ini dengan kebijakan paket dan server Anda yang sebenarnya; mengaktifkan beberapa sesi tidak menggabungkan lisensi offline independen.

7. Path B: kembalikan dengan Anda yang ada Media3 pemutar #

Baca download selesai dan gunakan kunci stream yang tepat. Pengunduhan dan tumpukan putar harus berbagi lokasi singgahan dan pemetaan kunci yang sama. Contoh ini sengaja mematikan akses upstream untuk pemutaran luring yang ketat:

val cacheOnly = CacheDataSource.Factory()
    .setCache(downloadCache)
    .setUpstreamDataSourceFactory(null)
val mediaSourceFactory = DefaultMediaSourceFactory(this)
    .setDataSourceFactory(cacheOnly)
val nativePlayer = ExoPlayer.Builder(this)
    .setMediaSourceFactory(mediaSourceFactory)
    .build()
val completed = downloadIndex.getDownload(offlineLicenseId)
require(completed?.state == Download.STATE_COMPLETED)
val playback = offlineManager.createPlayback(contentId, streamKeys = completed!!.request.streamKeys)
nativePlayer.setMediaItem(playback.mediaItem)
nativePlayer.prepare()
nativePlayer.play()
// Release nativePlayer when the owner is destroyed.

Untuk menggunakan aliran offline yang sama dengan Path A, membangun DrmXPlayer dengan ini mediaSourceFactory dan memanggil player.prepareOffline(playback). Pemulihan biasa menggunakan lisensi lokal CDM tanpa meminta yang baru. Entri cache yang hilang seharusnya menghasilkan kesalahan selama penerimaan terputus, bukan diam-diam beralih ke internet.

8. Path C: native Media3, tidak ada DRM-X AAR # }

Anda tidak diharuskan menggunakan DrmXPlayer atau DrmXOfflineManager. Tambahkan tiga dependensi Media3 dari langkah 3 dan terapkan kontrak backend pelanggan yang sama. Gunakan session.manifestUrl (bukan session.manifest.url), lisensi yang dikembalikan URL dan header otorisasi berumur pendek. Terapkan ketinggian maksimum yang dikembalikan dan tangani pelepasan sesi streaming saat berhenti, penggantian, berakhir, kegagalan, dan kehancuran.

Respon lengkap di bawah ini dikompilasi sebagai samples:native bersama dengan rilis SDK. Ini menangani HTTPS, respon terbatas dibaca, otorisasi latar belakang, tanggapan basi dan rilis reservasi. Ganti titik akhir yang ditandai / Content ID dan menyediakan tanda masuk Anda sendiri. Contoh adalah titik awal; tambah penyegaran produksi Anda, refresh, akun-switch, dan app- navigasi lifecycle.

Aktivitas streaming Asli (Kotlin, tidak ada DRM-X ketergantungan)
package com.haihaisoft.drmx.samples.nativeplayer

import android.app.Activity
import android.os.Bundle
import android.view.WindowManager
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URI
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean

/** Complete native streaming example. No DRM-X AAR or server credentials. */
@UnstableApi
class MainActivity : Activity() {
    private lateinit var player: ExoPlayer
    private lateinit var status: TextView
    private val worker = Executors.newSingleThreadExecutor()
    private val closed = AtomicBoolean(false)
    private var activeSession: JSONObject? = null
    private var requesting = false
    private var generation = 0
    // CUSTOMIZE: use YOUR authenticated customer backend and Published Content ID.
    // The sample can receive these after your application's login via Intent extras.
    // Never replace the app login token with a DRM-X Site Key or Access Key.
    private val endpoint get() = intent.getStringExtra("drmx.sessionEndpoint")
        ?: "https://customer.example/api/drmx/playback-session"
    private val contentId get() = intent.getStringExtra("drmx.contentId") ?: "YOUR-PUBLISHED-CONTENT-ID"
    private val customerToken get() = intent.getStringExtra("customer.accessToken").orEmpty()

    override fun onCreate(state: Bundle?) {
        super.onCreate(state)
        window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
        val root = LinearLayout(this).apply { orientation = LinearLayout.VERTICAL; fitsSystemWindows = true; setPadding(24, 24, 24, 24) }
        status = TextView(this).apply { text = "Native Media3 · connect your backend, then Play"; textSize = 16f }
        val view = PlayerView(this)
        player = ExoPlayer.Builder(this).build(); view.player = player
        root.addView(status)
        root.addView(view, LinearLayout.LayoutParams(-1, 0, 1f))
        root.addView(Button(this).apply { text = "Play protected video"; setOnClickListener { startPlayback() } })
        root.addView(Button(this).apply { text = "Stop & release session"; setOnClickListener { stopPlayback() } })
        setContentView(root)
        player.addListener(object : Player.Listener {
            override fun onPlayerError(error: PlaybackException) { status.text = "Playback error: ${error.errorCodeName}"; releaseSession() }
            override fun onPlaybackStateChanged(state: Int) { if (state == Player.STATE_ENDED) releaseSession() }
        })
    }

    private fun startPlayback() {
        if (requesting) return
        stopPlayback(); requesting = true; status.text = "Authorizing…"
        val attempt = generation
        val request = JSONObject().put("contentId", contentId).put("contentType", "vod")
            .put("drmSystem", "widevine").put("playbackMode", "streaming")
            .put("platformCapabilities", JSONObject().put("clientPlatform", "android")
                .put("drmSystems", org.json.JSONArray(listOf("widevine")))
                .put("manifestTypes", org.json.JSONArray(listOf("dash")))
                .put("maximumHeight", 1080).put("secureDecoder", false).put("persistentState", false))
        val headers = if (customerToken.isBlank()) emptyMap() else mapOf("Authorization" to "Bearer $customerToken")
        worker.execute {
            val result = runCatching { JSONObject(post(endpoint, request.toString(), headers)) }
            runOnUiThread {
                requesting = false
                result.onSuccess { session ->
                    if (closed.get() || attempt != generation) { workerRelease(session); return@onSuccess }
                    // Keep release information even if contract validation fails.
                    activeSession = session
                    runCatching {
                        require(session.getInt("contractVersion") == 1 && session.getString("contentId") == contentId)
                        require(session.getString("drmSystem") == "widevine" && session.getString("playbackMode") == "streaming")
                        val drm = session.getJSONObject("drm")
                        require(drm.getString("system") == "widevine")
                        val maximumHeight = session.getJSONObject("policy").getInt("maximumHeight")
                        require(maximumHeight in 1..4320)
                        val mediaItem = MediaItem.Builder().setUri(https(session.getString("manifestUrl")))
                            .setMimeType("application/dash+xml")
                            .setDrmConfiguration(MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
                                .setLicenseUri(https(drm.getString("licenseServerUrl")))
                                .setForceDefaultLicenseUri(true).setLicenseRequestHeaders(authorization(drm))
                                .setMultiSession(false).build()).build()
                        player.trackSelectionParameters = player.trackSelectionParameters.buildUpon()
                            .setMaxVideoSize(Int.MAX_VALUE, maximumHeight).setPreferredAudioLanguages("en")
                            .setTrackTypeDisabled(C.TRACK_TYPE_TEXT, true).build()
                        player.setMediaItem(mediaItem); player.prepare(); player.play()
                        status.text = "Native Media3 · protected playback"
                    }.onFailure { status.text = "Invalid session contract. Check your backend."; releaseSession() }
                }.onFailure { if (!closed.get()) status.text = "Authorization failed. Check login, Content ID, policy, and network." }
            }
        }
    }

    private fun releaseSession() { activeSession?.let(::workerRelease); activeSession = null }
    private fun workerRelease(session: JSONObject) {
        // A bounded release may finish after Activity destruction, including a
        // session response that arrived after the user left the screen.
        Thread({ runCatching {
            val release = session.getJSONObject("release")
            val headers = if (release.has("authorization")) authorization(release)
                else authorization(session.getJSONObject("drm"))
            post(release.getString("url"), "", headers)
        } }, "drmx-native-release").start()
    }
    private fun stopPlayback() { generation++; player.stop(); player.clearMediaItems(); releaseSession() }
    override fun onStop() { stopPlayback(); super.onStop() }
    override fun onDestroy() { closed.set(true); stopPlayback(); player.release(); worker.shutdown(); super.onDestroy() }

    companion object {
        fun https(url: String): String {
            val uri = URI(url); require(uri.scheme == "https" && uri.host != null && uri.userInfo == null)
            return url
        }
        fun authorization(drm: JSONObject): Map<String, String> {
            val auth = drm.optJSONObject("authorization")
            val name = auth?.getString("headerName") ?: "Authorization"
            val scheme = auth?.getString("scheme") ?: drm.optString("tokenType", "Bearer")
            val value = auth?.getString("value") ?: drm.getString("drmLicenseToken")
            require(name.matches(Regex("^[A-Za-z0-9-]{1,64}$")) && scheme.matches(Regex("^[A-Za-z][A-Za-z0-9._-]{0,31}$")))
            require(value.isNotBlank() && !value.contains('\r') && !value.contains('\n'))
            return mapOf(name to "$scheme $value")
        }
        fun post(url: String, body: String, headers: Map<String, String>): String {
            val connection = URI(https(url)).toURL().openConnection() as HttpURLConnection
            try {
                connection.requestMethod = "POST"; connection.instanceFollowRedirects = false
                connection.connectTimeout = 5000; connection.readTimeout = 15000; connection.doOutput = true
                connection.setRequestProperty("Content-Type", "application/json")
                headers.forEach { (name, value) -> connection.setRequestProperty(name, value) }
                connection.outputStream.use { it.write(body.toByteArray(Charsets.UTF_8)) }
                require(connection.responseCode in 200..299) { "Customer request rejected" }
                return connection.inputStream.use { stream ->
                    val output = java.io.ByteArrayOutputStream(); val buffer = ByteArray(8192)
                    while (true) {
                        val count = stream.read(buffer); if (count < 0) break
                        require(output.size() + count <= 1_048_576); output.write(buffer, 0, count)
                    }
                    output.toString("UTF-8")
                }
            } finally { connection.disconnect() }
        }
    }
}

Untuk lisensi offline asli, gunakan OfflineLicenseHelper.downloadLicense, bertahan tombol buram -set ID Pribadi dan kembalikan dengan MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).setKeySetId(id)pembantu terkompail berikut meliputi akuisisi, durasi pemeriksaan, pembaruan, rilis dan download / item putar. Ini adalah bukan sebuah aplikasi download lengkap: Anda masih memiliki cache, layanan download, kemajuan, metadata, pengikatan pengguna / perangkat dan terpercaya otorisasi operasi.

Pendukung lisensi asli (Kotlin, tidak ada DRM-X ketergantungan)
package com.haihaisoft.drmx.samples.nativeplayer

import android.content.Context
import android.util.Base64
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.StreamKey
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.exoplayer.dash.DashUtil
import androidx.media3.exoplayer.drm.DrmSessionEventListener
import androidx.media3.exoplayer.drm.OfflineLicenseHelper
import androidx.media3.exoplayer.offline.DownloadRequest
import org.json.JSONObject

/** Native Media3 alternative: no DRM-X AAR. Call blocking operations on a worker.
 * Application owns downloads/cache, user/device entitlement, progress and lifecycle.
 * Never persist the session, request headers, tokens, challenges or license bytes.
 */
@UnstableApi
class NativeOfflineLicense(context: Context) {
    private val storage = context.getSharedPreferences("native.offline.keysets", Context.MODE_PRIVATE)
    private val upstream = DefaultHttpDataSource.Factory().setConnectTimeoutMs(10000).setReadTimeoutMs(20000)

    // session is the validated response from YOUR offline-license-session endpoint.
    fun acquire(session: JSONObject): ByteArray {
        validate(session)
        val contentId = session.getString("contentId")
        require(!storage.contains(contentId)) { "Renew or release the stored license before acquiring again" }
        val source = upstream.createDataSource()
        val manifest = DashUtil.loadManifest(source, android.net.Uri.parse(MainActivity.https(session.getString("manifestUrl"))))
        val format = DashUtil.loadFormatWithDrmInitData(upstream.createDataSource(), manifest.getPeriod(0))
            ?: error("Missing Widevine initialization data")
        val helper = helper(session)
        try {
            val id = helper.downloadLicense(format)
            save(contentId, id)
            return id
        } finally { helper.release() }
    }

    fun renew(contentId: String, freshSession: JSONObject) {
        validate(freshSession); require(freshSession.getString("contentId") == contentId)
        val helper = helper(freshSession)
        try { save(contentId, helper.renewLicense(keySet(contentId))) } finally { helper.release() }
    }

    // Local CDM check only. No token or network license URI is used.
    fun remainingSeconds(contentId: String): Pair<Long, Long> {
        val config = MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).build()
        val helper = OfflineLicenseHelper.newWidevineInstance(config, upstream, DrmSessionEventListener.EventDispatcher())
        try {
            val remaining = helper.getLicenseDurationRemainingSec(keySet(contentId))
            return Pair(remaining.first, remaining.second)
        } finally { helper.release() }
    }

    fun release(contentId: String, freshSession: JSONObject) {
        validate(freshSession); require(freshSession.getString("contentId") == contentId)
        val helper = helper(freshSession)
        try {
            helper.releaseLicense(keySet(contentId))
            check(storage.edit().remove(contentId).commit())
        } finally { helper.release() }
    }

    fun downloadRequest(contentId: String, downloadId: String, manifestUrl: String, streamKeys: List<StreamKey>): DownloadRequest {
        require(streamKeys.isNotEmpty()) { "Select policy-compliant video, audio and subtitle tracks first" }
        return DownloadRequest.Builder(downloadId, android.net.Uri.parse(MainActivity.https(manifestUrl)))
            .setMimeType("application/dash+xml").setStreamKeys(streamKeys).setKeySetId(keySet(contentId)).build()
    }

    fun mediaItem(contentId: String, manifestUrl: String, streamKeys: List<StreamKey>): MediaItem = MediaItem.Builder()
        .setUri(MainActivity.https(manifestUrl)).setMimeType("application/dash+xml").setStreamKeys(streamKeys)
        .setDrmConfiguration(MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).setKeySetId(keySet(contentId)).build()).build()

    private fun helper(session: JSONObject): OfflineLicenseHelper {
        val drm = session.getJSONObject("drm")
        // Use HTTP license headers, NOT Widevine optional key-request parameters.
        val config = MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
            .setLicenseUri(MainActivity.https(drm.getString("licenseServerUrl")))
            .setForceDefaultLicenseUri(true).setLicenseRequestHeaders(MainActivity.authorization(drm)).build()
        return OfflineLicenseHelper.newWidevineInstance(config, upstream, DrmSessionEventListener.EventDispatcher())
    }
    private fun validate(session: JSONObject) {
        require(session.getInt("contractVersion") == 1 && session.getString("playbackMode") == "offline")
        require(session.getString("drmSystem") == "widevine" && session.getString("contentType") == "vod")
        require(session.getJSONObject("policy").getBoolean("persistent"))
    }
    private fun keySet(contentId: String): ByteArray = Base64.decode(storage.getString(contentId, null)
        ?: error("No device-bound license for this content"), Base64.NO_WRAP)
    private fun save(contentId: String, id: ByteArray) {
        check(storage.edit().putString(contentId, Base64.encodeToString(id, Base64.NO_WRAP)).commit())
    }
}

Sebelum memanggil pembantu tingkat rendah ini, validasi bahwa respon backend segar cocok dengan yang diminta Content ID, operasi, signed-in user / device dan otorisasi perioda validitas. Persist sebuah stabil licenseId dan terpercaya metadata manifest / track bersama kunci -set IDTidak pernah ada token, jalankan panggilan pembantu di benang pekerja. HTTP penggunaan header setLicenseRequestHeaders pada DRM konfigurasi; Widevine parameter opsional key-request adalah peta yang berbeda.

Integrasi baru harus digunakan AndroidX Media3 DRM dukunganLegacy com.google.android.exoplayer2 proyek harus mengikuti panduan migrasi resmiDan janganlah kamu campur adukkan harta dan anak-anak kamu. AndroidX kelas atau asumsikan sampel compiles tidak berubah terhadap versi yang lebih tua.

9. Periksa, perbarui, rilis dan tukar bahasa #

Ini adalah tindakan terpisah. Jalankan satu per satu setelah sesi DRM offline aktif dilepaskan: gunakan player.stopForOfflineLicenseOperation { /* one operation below */ } dengan DrmXPlayer, atau lepaskan pemutar native Media3 yang ada terlebih dahulu. Jangan memeriksa lisensi saat video sedang diputar. Pemeriksaan bersifat opsional untuk menampilkan durasi; pemeriksaan tidak boleh menghalangi perpanjangan lisensi yang telah kedaluwarsa.

offlineManager.checkLicense(contentId) { result ->
    result.onSuccess { license ->
        if (license.state == DrmXOfflineLicenseState.EXPIRED) {
            // Require renewal online before offering offline playback.
        }
    }
}
offlineManager.renewLicense(offlineEndpoint, contentId, capabilities, customerHeaders) {
    /* update duration and UI after success; show retry on failure */
}
// Only when the customer requests deletion/release:
offlineManager.releaseLicense(offlineEndpoint, contentId, capabilities, customerHeaders) {
    /* after success, remove the corresponding encrypted download */
}

getStoredLicense membaca metadata; checkLicense queries otoritas lokal CDM durasi. Renew / release membutuhkan konektivitas dan baru operasional -spesifik otorisasi dari backend Anda. Menghapus media tidak merilis lisensi. Ketika diputus, rilis antrian untuk koneksi berikutnya daripada melaporkan bahwa server telah mengkonfirmasi itu. Tutup manajer ketika pemiliknya hancur.

// Use player.media3Player for the DRM-X wrapper; nativePlayer for plain Media3.
nativePlayer.trackSelectionParameters = nativePlayer.trackSelectionParameters
    .buildUpon()
    .setPreferredAudioLanguages("en") // Chinese: "zh-Hans", "zh"
    .setPreferredTextLanguages("en")  // choose a packaged/downloaded language
    .setTrackTypeDisabled(C.TRACK_TYPE_TEXT, false) // true = subtitles Off
    .build()

Bangun pilihan dari trek yang tersedia dan matikan pilihan yang tak tersedia. Suatu preferensi tak dapat membuat trek bahasa Inggris absen dari paket. Pengguna Offline hanya dapat memilih bahasa yang diunduh. Pertahankan keadaan lisensi / cache di seluruh rotasi layar; layar penuh adalah tampilan / perubahan jendela, bukan alasan untuk memperoleh lisensi lain.

10. Mengimplementasikan titik akhir otorisasi luring #

Aplikasi memposting contentId, contentType: "vod", drmSystem: "widevine", playbackMode: "offline", operation: "acquire" | "renew" | "release", offlineLicenseId (kosong untuk akuisisi awal) dan jujur platformCapabilities. Verifikasi ID pembaruan milik pengguna, perangkat, dan isi. Pilih server- sisi kebijakan, keluarkan yang baru DRM License Token dan kembalikan kontrak yang cocok:

{
  "contractVersion": 1, "playbackMode": "offline",
  "offlineLicenseId": "YOUR-STABLE-USER-DEVICE-CONTENT-RECORD",
  "contentId": "YOUR-PUBLISHED-CONTENT-ID", "contentType": "vod",
  "drmSystem": "widevine",
  "manifestUrl": "https://media.customer.example/title/manifest.mpd",
  "authorizationExpiresAt": "YOUR-FRESH-UTC-EXPIRY",
  "drm": {
    "system": "widevine",
    "licenseServerUrl": "https://license6.drm-x.com/v1/licenses/widevine",
    "integrationMode": "direct-token",
    "drmLicenseToken": "SHORT-LIVED-OPERATION-AUTHORIZATION",
    "tokenType": "Bearer"
  },
  "policy": {
    "persistent": true, "renewAllowed": true, "maximumHeight": 1080,
    "allowedTrackTypes": ["AUDIO", "SD", "HD"],
    "licenseDurationSeconds": 604800, "rentalDurationSeconds": 259200,
    "playbackDurationSeconds": 172800, "renewalDurationSeconds": 604800
  }
}

Ini adalah placeholder yang jelas, bukan sebuah respon yang dapat dipakai kembali. Kembalikan nilai baru dari operasi yang sebenarnya. SDK juga mendukung integrationMode: "token-proxy" dengan kekurangan drm.authorization deskripsi. Kembalikan Cache-Control: no-store: tidak pernah log sukses badan otorisasi. Lihat proses yang dapat diunduh SDK 's OFFLINE_INTEGRATION.md dan PHP sampel titik luring untuk penanganan permintaan konkret.

#

Tes / gejalaDiharapkan pemeriksaan perilaku / berikutnya
SDK unduh kembali 403Pilih sebuah organisasi dengan berlangganan Profesional atau Enterprise yang aktif. Suatu peningkatan yang direncanakan setelah percobaan tidak membuka SDK.
Sesi 401 / 403Periksa log masuk, hak akses, Lingkungan, Terbitkan Content ID dan kebijakan. Jangan pernah lewati otentikasi.
Offline gagal tapi kerja onlinePeriksa CDM durasi, selesai download, lokasi cache, kunci stream dan semua kunci lisensi yang dibutuhkan.
Inggris / Cina hilangPeriksa metadata bahasa paket, trek aktual dan seleksi bahasa offline.
L1, HDCP atau gagal decoderPeriksa perangkat keras fisik, provisioning, codec dan output persyaratan. Sebuah string kapabilitas tidak sertifikasi.
Reboot, rotasi, upgradeSurat ijin dan cache yang bertahan dari perubahan lifecycle yang didukung tanpa akuisisi duplikasi. Backup tidak boleh bermigrasi lisensi ke perangkat baru.
Mode pesawat udaraNonaktifkan Wi-Fi dan data mobile juga; restart aplikasi, putar secara lokal dan ganti setiap bahasa yang dijanjikan.
Kadaluwarsa, tekanan penyimpanan, tombol akunTampilkan keadaan yang jelas tidak tersedia / memperbaharui, memulihkan media yang tidak lengkap, dan memaksakan akses pengguna / perangkat tanpa membocorkan katalog luring pengguna lain.

Kualifikasi setiap ponsel yang didukung, tablet, Android TV/Google TV, Fire TV atau tertanam Android model untuk Widevine, decoding aman, codec / resolusi, HDCP, eksternal display, clock changes and long disconnection. Tidak setiap Android perangkat memiliki sama DRM kemampuan isi Hollywood membutuhkan perlindungan pemegang hak cipta dan kebutuhan perangkat, tidak hanya sukses decoding.

Tidak ada peluang untuk memperoleh sebelum pemutusan? Itu adalah penerapan yang berbeda: layanan lisensi on-board yang memenuhi syarat mengeluarkan lisensi melalui jaringan lokal selama perjalanan. Ini tidak diaktifkan hanya dengan menggunakan SDK ini. DRM-X dapat bekerja sama dengan Anda dalam penerapan khusus yang mencakup izin penyedia, penyediaan, perlindungan kunci, dan uji penerimaan yang sepenuhnya terputus. Lihat di dalam pesawat DRM untuk pesawat, bus, kereta api, dan kapal.

Daftar putar dan kontrol bersama Universal Player#

Pemutar Android dan Apple menyediakan Previous/Play-Pause/Next, CC, Settings, dan kontrol layar penuh. Move up / Move down mempertahankan video saat ini dan menyimpan urutan daftar untuk pemutaran online maupun offline. Pilihan acak dan ulang tetap tersimpan setelah aplikasi dibuka kembali; jika keduanya mati, pemutaran berhenti setelah item terakhir.

Supply the compenent / viewer storageNamespace to that DrmXPlayer and playerView.storageNamespace before setting the playlist. VOD progress is save periocally and on pause / stop, then resume for that Content ID in streaming or download playing. Live playing does not use a saved VOD position. Selesai unduhan mempertahankan perilaku yang sama / resume tanpa sumber media jaringan. Keadaan lokal ini tidak mensinkronkan kemajuan.

Web dan Android berbagi panel gelap dan kontrol biru ringkas. Daftar mendukung berikut/sebelum, acak, ulang satu/semua, dan maju otomatis yang bisa dibatalkan. Setiap item memerlukan otorisasi baru. Bab dan transkrip berasal dari katalog.

Tersedia kecepatan, timer tidur, takarir, kualitas diizinkan, bahasa, penanda sesi, dan ulang A–B. Web menambah pintasan, lanjut lokal opsional per penonton, pencarian transkrip, serta PiP yang didukung. Android mempertahankan unduhan Widevine offline dan pembaruan lisensi. Casting dan progres lintas perangkat memerlukan integrasi aplikasi terpisah.

Pengaturan logo profesional dan Enterprise

Branding tetap berada di permukaan video. Buka Pengaturan Berkas Tentang pemutar untuk logo yang berwenang, informasi produk dan versi pemutar. Web juga menawarkan informasi ini pada right- klik. Demo telepon umum termasuk WayMaker, Kebaikan Allah, Minyak Baru dan 4K Tes Video; setiap pemilihan menerima otorisasi sendiri.

Atur playerBranding menjadi {"mode":"hidden"} atau {"mode":"custom","logoUrl":"https://your-site.example/logo.png","label":"Your brand"} di backend tepercaya. Platform memeriksa Professional atau Enterprise aktif. Paket lain memakai {"mode":"default"}; permintaan merek khusus/tersembunyi tanpa izin menghasilkan 403. Ini berbeda dari watermark keamanan.

Unduhan PHP memiliki daftar dan merek yang dapat disunting. ZIP Android menyediakan callback daftar. Otorisasi daftar tetap di backend; jangan tanam kredensial klien API pada pemutar mana pun.