DRM-X 6.0 logo bồ câu và nhánh ô liuDRM-X 6.0Tài liệu
◎ Tiếng Việt

Android SDK và Media3 gốc

Tải trình phát Android với danh sách phát có thể kéo để sắp xếp, cài đặt phát ngẫu nhiên và lặp lại được lưu, cùng khả năng tiếp tục phát ngoại tuyến. Tích hợp phát nội dung được bảo vệ bằng DRM-X hoặc Media3 gốc.

Xem MarkdownVí dụ chạy trực tiếpCập nhật ngày 2026-10-26

Để tùy chỉnh giao diện, xem giá trị mặc định của tham số và ví dụ về vòng đời, hãy đọc PLAYER-UI-CONFIGURATION.md. Các bảng bật lên mặc định của trình phát đều không trong suốt; độ mờ của phụ đề và hình mờ được đặt riêng.

Thử bản demo PHP theo DEMO-QUICKSTART.md trong SDK ZIP. Với nội dung của bạn, hãy tích hợp trang web và backend trước, rồi làm theo MOBILE-AND-TV-APPS.md để kết nối ứng dụng với cùng hệ thống đăng nhập và quy tắc cấp quyền xem.

Tải Android SDK và ứng dụng mẫu từ Console → Developer Tools → Device SDKs với gói Professional hoặc Enterprise đang hoạt động.
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.

Thử Android trình phát#

Tải xuống, cài đặt, nhấn Play. Ứng dụng đánh giá miễn phí sử dụng video demo công khai của chúng tôi và đăng nhập tự động. Bạn không cần phải tạo APK hoặc nhập thông tin đăng nhập DRM-X để dùng thử.

Tải về Android mẫu APK

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

  1. Mở trang này ra. Android điện thoại và tải về APK.
  2. Mở tập tin đã tải về. Android yêu cầu, cho phép cài đặt từ trình duyệt hay bộ quản lý tập tin bạn dùng, rồi vỗ chân Cài đặt.
  3. Mở DRM-X 6.0 Bộ phátHãy kết nối với nhau, và vỗ vào video Phát Nút. Ứng dụng yêu cầu giấy phép truyền tải khi bạn bấm phím Play.
  4. Mở Thiết lập để chọn chất lượng, âm thanh, chú thích hoặc tốc độ phát lại. Hãy quay điện thoại để tiếp tục trên màn hình đầy đủ.
  5. Mở menu ba dòng của video và chọn Download. Bật Downloaded videos để dùng Offline Mode; tắt để dùng Online Mode. Trong Offline Mode, chọn Delete từ menu của video đó rồi xác nhận. Gia hạn giấy phép cần kết nối mạng và quyền truy cập còn hiệu lực; khi có lỗi ngoại tuyến, bạn có thể tiếp tục ngoại tuyến hoặc chuyển sang trực tuyến.

Việc tải về phương tiện đã xếp hàng vẫn còn nằm trong nền. Android Dừng ứng dụng trong khi chuẩn bị giấy phép hay dấu vết, mở lại nó trong khi kết nối để chuẩn bị xong các mục còn lại. Giữ lại việc tải về có sẵn. Phát lại điện thoại ngoài chỉ dùng phương tiện đã lưu tạm; chưa hoàn thành hay đã hết hạn cần thiết hoàn tất hay mới lại.

Mẫu APK đã được công khai và đã ký để đánh giá. AAR đầy đủ SDK Bên dưới vẫn còn sẵn sàng thông qua tài khoản bàn điều khiển thích hợp. Giữ ứng dụng sản xuất riêng của bạn ký khóa và đăng nhập hậu phương riêng từ chứng minh này.

APK checksum và USB cài đặt

SHA-256:

b96ba527ddf9abae5bff2f8b85a39a12b59c934aaacde1961c8096aa32524c54

Với Android Công cụ nền tảng và quyền hạn USB thiết bị:

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

Cài đặt APK trên mẫu chính thức đã có mà không bỏ cài đặt nó. Cùng một phím ký và ứng dụng tương tự ID Bảo tồn việc tải về, giấy phép ngoại tuyến, lưu vị trí và sở thích riêng. Nếu nhà riêng của bạn sử dụng phím ký khác, hãy sử dụng ứng dụng riêng. ID để giữ cả hai ứng dụng và bảo tồn giấy phép ngoại tuyến của mẫu.

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.

Trong Offline Mode, mở menu ba dòng của video và chọn Delete. Xác nhận để chỉ xóa nội dung mã hóa và tham chiếu giấy phép cục bộ của video đó; Cancel sẽ giữ lại. Kết nối lại để tải video lần nữa.

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#

Phát hành ngày 17 tháng 9 năm 2026. Ứng dụng điện thoại và khách hàng SDK bao gồm Thử nghiệm 4K Live DRM. Tính năng phát lại trực tiếp sử dụng các bảng kê khai mới và không thể tải xuống được. Sự kiện thử nghiệm chỉ khả dụng trong thời gian phát sóng đang hoạt động. Làm theo hướng dẫn thiết lập AWS và OBS để tạo sự kiện của riêng bạn.

Có gì mới không? Android trình phát xem thử.12#

Phát hành ngày 14 tháng 9 năm 2026. Widevine Giấy phép đã hết hạn hoặc có một phút hoặc ít hơn còn lại. Media3 lỗi 6004, a HTTP Lỗi khi được kết nối, hoặc a DNS Lỗi khi bị ngắt kết nối.

Hạt trình phát Giấy phép hợp lệ giữ thời gian phát lại còn lại của họ; giấy phép hết hạn cho thấy Kết nối và khởi động lại. giấy phép xác thực và các quy tắc bảo vệ vẫn còn được thiết bị thực hiện. Chẩn đoán cũng bao gồm số điện thoại HTTP Trạng thái khi thao tác mạng bị lỗi, mà không phơi bày tiêu đề yêu cầu, địa chỉ URL hay câu trả lời cấp giấy phép.

  1. Cài đặt ô xem thử.12 trên ứng dụng chính thức đã có. Giữ dữ liệu ứng dụng hiện thời và tải về.
  2. Nếu WayMaker hay một video khác được tải về cần được thay đổi, kết nối đến internet, chọn video đó và tap Mới.
  3. Đợi Giấy phép mới, sẵn sàng chơi ngoài giờ.thì chọn đi. Chơi trò chơi nhập vaiĐoạn video được mã hóa vẫn được tải về.

This release retains preview.11's controls and playlist features. The public evaluation app and its embedded player identify preview.12; at that release, the separately distributed Console SDK was preview.10 / bundle 20260910.8.

Có gì mới không? Android trình phát xem thử.11#

Ứng dụng đánh giá này bao gồm những ứng dụng mới nhất trình phát Điều khiển và danh sách nhạc cải tiến, với một phong cách tương tác nhất định trên chúng tôi Android và táo trình phát.

  • Điều khiển bên trong video: Giữa Play/Puse, Back and next cho các danh sách nhạc đa công, CC, Thiết lập và nút toàn màn hình. Việc điều khiển ẩn sau hai giây đang bật; hãy gõ đoạn video để hiển thị lại chúng.
  • Thiết lập dây và phát lại: dùng CC để hiển thị hoặc ẩn phụ đề sẵn sàng, và Thiết lập để chọn ngôn ngữ phụ đề, âm thanh, tốc độ phát lại và chất lượng cho phép. Các lựa chọn ngoại tuyến chỉ dùng các đường dẫn trong việc tải về.
  • Kéo để sắp xếp lại: kéo thanh xử lý của mục nhập để thay đổi vị trí của nó. Thứ tự đã lưu sống sót sau khi khởi chạy lại ứng dụng và được phản ánh trong danh sách nhạc đã tải về. Sắp xếp lại để giữ cho video được bật. Hãy gõ vào điều khiển để có thể truy cập lên / di chuyển xuống hành động.
  • Lưu xáo trộn và lặp lại: Chế độ đã chọn sẽ được tô sáng và sống sót khi khởi chạy lại bằng phát lại trực tuyến và tắt. Với xáo trộn và lặp lại cả hai, các video theo thứ tự đã lưu và dừng lại sau mục cuối cùng. Trình đơn danh sách nhạc « Chơi hành động tiếp theo đã bị gỡ bỏ.
  • Tiếp tục ở nơi bạn dừng lại: mỗi video được lưu VOD vị trí đã được khôi phục khi bạn quay lại để truyền hoặc tải về. Tiến trình được lưu trên thiết bị này cho trình xem hiện thời; nó không đồng bộ hoá giữa các thiết bị.

Sắp xếp danh sách phát, lưu thứ tự và tiếp tục phát. Xem ngoại tuyến cần bản tải xuống hoàn tất và giấy phép ngoại tuyến còn dùng được, chưa hết hạn.

The preview.11 release updated the public evaluation APK. The current Console SDK is 1.0.0-beta.14, bundle 20261028.1; the integration instructions below use that bundle.

1. Chọn đường dẫn đến sự kết hợp#

Tải từ Developer Tools → Device SDKs. SDK yêu cầu quyền Professional hoặc Enterprise còn hiệu lực, bao gồm đợt đánh giá Professional đã được phê duyệt. Standard vẫn có APIs gốc để xây dựng trình phát riêng. Quyền SDK được kiểm tra khi tạo phiên và gia hạn giấy phép; giấy phép ngoại tuyến không hết hạn muộn hơn thời hạn dịch vụ của tài khoản.

Current preview: 1.0.0-beta.14 · customer bundle 20261028.1 · Media3 1.11.0. The ZIP contains a local Maven repository, Gradle wrapper, annotated README, phone/TV/native samples, an installable phone sample APK in apks/, and offline integration notes. It contains no customer credentials or protected movies.

2. Chuẩn bị nội dung và hậu phương#

  1. Gói và xuất bản a Widevine- Mã hóa tương ứng DASH tiêu đề trong của bạn DRM-X Môi trường, sao chép chính xác Xuất bản Content ID.
  2. Cấu hình máy phục vụ của bạn Environment ID, Site Key và Access KeySử dụng tải về được PHP Sự kết hợp cho một điểm bắt đầu làm việc.
  3. Thao tác đăng nhập và kiểm tra phúc lợi người dùng. Ứng dụng của bạn gọi hậu phương của bạn bằng biểu tượng người dùng ngắn hạn; hậu phương chọn nội dung, chính sách và URL đáng tin cậy.
  4. Chạy phát lại trực tuyến đầu tiên trên vật lý WidevineChỉ thêm mục tiêu ngoài luồng sau khi hợp đồng được thực hiện.

Video đã mã hóa và giấy phép DRM được truyền qua các đường riêng

CDN phân phối video đã mã hóa. Backend kiểm tra quyền truy cập, còn dịch vụ giấy phép DRM cho phép giải mã trên thiết bị tương thích.

  1. Lưu trữ / CDN

    Phân phối nội dung đã mã hóa đến trình phát.

  2. Backend của bạn

    Kiểm tra quyền xem và yêu cầu thông tin cấp quyền phát từ DRM-X.

  3. DRM-X

    Xác thực thông tin cấp quyền phát và xử lý yêu cầu giấy phép DRM.

  4. Trình phát + thiết bị

    Dùng hệ thống DRM của thiết bị để nhận giấy phép và phát nội dung đã mã hóa.

Giao phương tiện và giấy phép là riêng biệt. Chỉ giữ hậu phương khách hàng DRM-X máy phục vụ thông tin xác thực.
Đổi giá trị nàyNơi nó thuộc vềNghĩa là
sessionEndpointAndroidCủa bạn HTTPS Điểm kết thúc phát lại, không phải máy chủ bằng lái thô URL.
offlineEndpointAndroidchỉ ngắt kết nốiĐiểm kết thúc thu thập/mới của bạn.
contentIdAndroid yêu cầu; thẩm tra hậu phươngBản được xuất bản được bảo vệ Content ID người xem có quyền truy cập.
Huy hiệu truy cập khách hàngỨng dụng bộ nhớ sau khi đăng nhậpHuy hiệu người dùng ngắn hạn của bạn, không bao giờ là Site Key hay Access Key.
Environment ID, Site Key, Access KeyChỉ cấu hình bí mật máy phục vụDRM-X tài khoản thông tin xác thựcĐừng bao giờ vận chuyển chúng vào một APK, HTML hay kiểm soát nguồn.
Danh sách chính sách và đáng tin cậy URLBackend của bạnQuyền lợi kinh doanh và bản đồ gói hàng. Đừng nhận những thứ này từ ứng dụng.

3. Cài đặt và chạy SDK#

Open the extracted ZIP's root folder in Android Studio. Use JDK 17, Android SDK 36 and Android Gradle Plugin 9.2.0; the included wrapper uses Gradle 9.4.1. Minimum device API is 23. Build dependencies require internet access. Select samples.phone and Run. Its initial values use the public demo backend and title; follow its sign-in and streaming controls before switching to your own backend.

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

Đối với một ứng dụng đã có, sao chép ZIPCủa maven/ thư mục vào gốc dự án của bạn. In settings.gradle.kts:

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

Trong môđun ứng dụng của bạn build.gradle.kts:

dependencies {
    implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14")
}
Cài đặt đứng yên AAR thay vào đó

Để cái AAR trong môđun ứng dụng của bạn libs/Một căn hộ AAR không có giải quyết quan hệ phụ thuộc tự động, vì vậy thêm ba phù hợp Media3 tự phụ thuộc vào mình. Đừng cài đặt cả hai mẫu.

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

Vào AndroidManifest.xmlCho phép truy cập Internet và tắt bản sao lưu cho mẫu nhỏ tối thiểu này. Trong một ứng dụng sản xuất cần bản sao lưu, đặc biệt loại bỏ siêu dữ liệu giấy phép tắt của thiết bị.

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

Tìm kiếm mẫu điện thoại MainActivity.kt Vì drmx.sessionEndpoint, drmx.offlineEndpoint, drmx.contentId và customer.loginEndpoint. Hãy thay đổi thiết lập hậu phương đã ghi rõ, hoặc gởi những bổ sung chủ ý này từ ứng dụng của bạn sau khi đăng nhập. customer.accessToken lúc chạy. ZIP README Giải thích mỗi tham số và các móc chứng minh để thay thế.

Xây dựng một thử thách APK sử dụng AAR#

Ví dụ này sử dụng dự án điện thoại có thể chạy từ đầy đủ SDK ZIP và rõ ràng liên kết các đứng một AARCách tiếp cận tương tự cũng hoạt động với một phương pháp đã có Android ứng dụng.

  1. Download and extract the full Android SDK ZIP. Open its root folder in Android Studio.
  2. Keep libs/drmx-universal-player-1.0.0-beta.14.aar in the extracted root. Use JDK 17, Android SDK 36 and the included Gradle wrapper.
  3. Vào samples/phone/build.gradle.kts, thay thế DRM-X Quan hệ phụ thuộc với các mục sau đây bên trong dependenciesGiữ những tập tin xây dựng còn lại.
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")

Bỏ implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14") Cũng vậy thôi. SDK Không bao gồm cả hai lần. AAR cần cả ba. Media3 quan hệ phụ thuộc được hiển thị bên trên.

  1. Để giữ bản lập trình đã tải về cùng với bản thử nghiệm của bạn xây dựng, thay đổi môđun điện thoại applicationId phải com.example.drmxtestĐể lại vùng tên không thay đổi cho mẫu đã cung cấp này.
  2. Xây dựng APKsau đó cài đặt nó trên a USB-đã kết nối Android thiết bị có USB Có quyền gỡ lỗi.
# 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

Mở ứng dụng thử và trò chơi tap. Nguồn điện thoại cung cấp sử dụng cùng một chứng minh công cộng. Để thử ra tiêu đề riêng, hãy thay thế phiên chạy/ đầu/ trang đã đánh dấu, rồi xuất bản. Content ID in MainActivity.kt, và cung cấp xác thực ngắn hạn cho người dùng của bạn trong lúc chạy. Máy chủ của bạn giữ Site Key và Access Key.

Cái điều khiển gọn gàng đến từ DrmXPlayerView trong AARHoạt động được cung cấp cho thấy tốc độ Play-to-fite, quay trả lời, tải chủ sở hữu, phát lại và tái tạo. ví dụ tích hợp điều khiển Bên dưới khi thêm ô xem này vào ứng dụng của riêng bạn.

4. yêu cầu máy phục vụ và chính sách mặc định#

Hạt Android Ứng dụng yêu cầu hậu phương của bạn cho phiên chạy phát lại. Hậu phương của bạn xác thực người dùng, xác nhận quyền lợi, bản đồ Content ID để xuất bản các phương tiện truyền thông và cuộc gọi DRM-XThấy chưa? Phiên chạy phát lại API cho toàn bộ hợp đồng truyền tải.

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

Các khả năng bảo thủ trên không xác nhận Widevine L1Sử dụng SDKTrợ giúp khả năng của bạn, hoặc đánh giá thiết bị của riêng bạn, cho các thiết bị thật. Khả năng khách hàng hỗ trợ sự lựa chọn tương thích; họ không bao giờ cung cấp quyền lợi chất lượng.

Hạt PHP mặc định ví dụ multi-tier-standard cho phát lại trực tuyến. Hãy chọn chính sách được ghi chép bằng một phím khi gói này sử dụng một phím. Chính sách của bạn phải khớp với mô hình khóa và quyền của người xem. Thay đổi cấu hình máy phục vụ hay của nó. DRMX_LICENSE_POLICY_TEMPLATE thiết lập môi trường, không phải tham số truy vấn trong trình phátThấy chưa? Chính sách chứng nhận.

Ngoại tuyến cần có chính sách cố định của riêng mình. Đặt playback_policy.persistent thành true trong chính sách do máy chủ cấp và chọn thời lượng cấp phép, cho thuê, phát lại và gia hạn. Tham chiếu sử dụng lần lượt là 7 ngày/3 ngày/48 giờ/7 ngày; đây là những quy tắc kinh doanh mẫu, không phải sự chấp thuận của studio hoặc mặc định trên toàn nền tảng. Trả về siêu dữ liệu phù hợp. Giấy phép Widevine vẫn có hiệu lực.

Đường dẫn thứ nhất: chơi qua DrmXPlayer#

Bên trong hoạt động của bạn, tạo ra một PlayerViewDùng cửa sổ bảo mật và gọi prepare sau khi đăng nhập. The wrapper trao đổi phiên chạy, cấu hình Widevine@ info/ rich com.haihaisoft.drmx.player và 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()

Đăng ký DrmXPlayerListener cho phép, sẵn sàng, chơi và sai lầm UIHiển thị mã lỗi an toàn thay vì bỏ đi các thẻ chứng nhận hoặc các chương trình trả lời. Hãy dùng mẫu điện thoại bao gồm để hoàn tất vòng đời, điều khiển toàn màn hình và tải xuống hàng đợi.

trình phát điều khiển và xoay không gián đoạn#

Gõ Phát trên video để bắt đầu quyền truy cập. Mở màn hình hay Thiết lập không có giấy phép phát thanh. Tạm dừng/ chủ đề, thay đổi thiết lập và xoay điện thoại để bảo tồn phiên chạy hiện thời.

Ảnh chụp màn hình cài đặt Samsung Android hiện tại. Tính năng bảo vệ ảnh chụp màn hình che phần hiển thị chính của trình phát.
Ảnh chụp màn hình cài đặt Samsung Android hiện tại. Tính năng bảo vệ ảnh chụp màn hình che phần hiển thị chính của trình phát.
Điều khiểnNhững gì khách hàng có thể làm
Chất lượngChọn tự động hay hỗ trợ, giải quyết có giấy phép từ danh sách. Tùy chọn không sẵn sàng giải thích được bằng lái hay thiết bị/DRM Tự động tuân theo những hạn chế tương tự.
Theo dõi âm thanhHãy chọn bất cứ ngôn ngữ âm thanh nào được gói cung cấp.
Phụ đềCCChọn ngôn ngữ phụ đề hoặc tắt. CC Nút xuất hiện khi có chú thích được hỗ trợ tồn tại và bật/tắt trực tiếp.
Hình ảnh độ xoayĐiều chỉnh phông chữ, màu và kích cỡ chữ, màu nền và độ mờ đục. Tùy thích được lưu vào thiết bị.
Tốc độ chơi lại/ giờ giấcChơi với tốc độ 0.25–2× hoặc tạm dừng tự động sau một khoảng đã chọn.
Tìm kiếm / Toàn màn hìnhXâm nhập dòng thời gian, tìm kiếm 10 giây, hoặc là hai bên. Xoay vào cảnh mà không cần khởi động lại.

Mỗi menu video có Move up / Move down cùng Download khi trực tuyến hoặc Delete khi ngoại tuyến. Download bị vô hiệu hóa khi tải xong. Công tắc Downloaded videos chọn phát ngoại tuyến; chỉ các luồng đã lưu trong bộ nhớ đệm mới khả dụng. Không có nút riêng để tải, xóa hoặc tải toàn bộ danh sách phát.

Bản ghi sao chép bên cạnh bộ chân trang thông tin sao chép các lỗi gần đây thành văn bản, vì vậy bạn có thể gửi hỗ trợ các chi tiết không chụp màn hình. Báo cáo chứa một báo cáo IDChiến dịch. SDK/AndroidPhiên bản/số hoá CDM/Media3 lỗi, khung chồng đã chọn, kết nối/ xuốngload/ lùi và đếm lại. Họ loại ra thông tin xác thựccác địa chỉ URL, phím, mã nhận diện khoá, dữ liệu giấy phép và đầy đủ CDM Đồ bỏ.

Mẫu được lưu giữ lại đến 32 báo cáo địa phương (64 KB) trong năm giờ. Khi được kết nối và ký tên, nó tải lên các lỗi đang chờ tới hậu phương được cấu hình của khách hàng, ít nhất một lần trong 30 giây. Máy phục vụ chứng minh công cộng giữ lại cảnh báo/ báo khủng bố trong năm giờ, kết hợp các sự kiện lặp đi lặp lại, kết nối tập tin tại 512 KB và cắt tỉa mỗi phút tiến bộ y tế và phát lại thành công không được ghi lại. drmx.diagnosticsEndpoint; chẩn đoán lỗi không bao giờ gián đoạn phát lại.

Giấy phép ngoại tuyến đã được sử dụng? Preview.5 sửa trường hợp thiết bị Samsung Android đã báo cáo CDM 368 với "Giấy phép được tải trong phiên khác nhau". Nhấn Play ngoại tuyến một lần nữa sẽ tiếp tục trình phát hiện có. Gia hạn trước tiên sẽ giải phóng phiên DRM của nó, sau đó gia hạn giấy phép được lưu trữ. Giấy phép tải xuống và liên tục được giữ nguyên; không cần thiết lập lại dữ liệu ứng dụng.
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()

Danh sách điện thoại cung cấp điều khiển định hướng và kích cỡ màn hình thay đổi; onConfigurationChanged Đổi kích thước cùng một hoạt động và trình phátHãy giữ cả hai phần khi thích nghi nó. Máy nào tái tạo hoạt động nên giữ lại chủ phát lại và gắn kết khung xem. Quá trình chết khác với xoay và cần khôi phục trạng thái bình thường. Các tùy chọn 4K/8K cần thiết nội dung tương ứng, giấy phép và thiết bị/DRM Hỗ trợ; các CDM vẫn có thẩm quyền.

Để chuẩn bị trình phát, dừng lại và thả bộ ngắt kết nối hoạt động DRM trước khi kiểm tra, tái tạo hoặc phát hành cùng một giấy phép bền bỉ. SDKCủa player.stopForOfflineLicenseOperation { /* call manager here */ } Chờ đợi cho Media3 Chuẩn bị phát hành lại các phương tiện truyền thông. stop() Hàng đợi hoạt động không đều. checkLicense() Trong khi cùng một bằng lái đang chơi; phát lại bản thân xác nhận của nó CDM Tình trạng. Mẫu đã cung cấp hiển thị toàn bộ tay ra và tắt các hành động chồng chéo trong khi thay đổi.

6, lấy bằng lái và tải xuống truyền thông#

Chuẩn bị phát ngoại tuyến

Cần có cả bản tải xuống hoàn chỉnh và giấy phép hợp lệ gắn với thiết bị.

  1. Tải nội dung đã mã hóa

    Khi có mạng, tải video cùng các track âm thanh và phụ đề cần thiết.

  2. Lấy giấy phép lưu lâu dài

    Khi có mạng, lấy giấy phép Widevine gắn với thiết bị. Lưu riêng tư mã Key-set ID dạng opaque của giấy phép.

  3. Kiểm tra trạng thái sẵn sàng

    Xác nhận tải xuống đã hoàn tất và giấy phép còn hiệu lực.

  4. Phát ngoại tuyến

    Đọc nội dung trong bộ nhớ đệm và khôi phục giấy phép cục bộ. Gia hạn và giải phóng giấy phép cần kết nối Internet.

Sẵn sàng ngoại tuyến = các phương tiện được mã hóa đầy đủ + một giấy phép gắn thiết bị hợp lệ. Không ai thay thế phương tiện khác.
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 */ }
}

Hạt SDK tiết kiệm sương mù Widevine Bộ khoá ID và siêu dữ liệu không bí mật. CDM Cầm giữ bằng lái, nó không bền bỉ. DRM License Tokens, chìa khóa thô, thách thức hoặc bằng lái đáp ứng byte. com.haihaisoft.drmx.offline.licenses.v1.xml từ bản dự phòng.

Tạo ra một ứng dụng được sở hữu Media3 SimpleCacheBộ quản lý tải về và cơ sở dữ liệu. Dùng DownloadHelper với sự cấp phép của MediaItem và chọn phim bên trong maximumHeightVào Media3 1.11 cuộc gọi trở lại chuẩn bị là onPrepared(helper, tracksInfoUpdated)Mẫu điện thoại enqueueDownload và OfflineTestRuntime hiển thị các thực hiện chạy được.

// 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()

Tải về tất cả các ngôn ngữ mà bạn định phơi bày ngoại tuyến. createDownloadRequest Không có phím luồng tải xuống mọi đường dẫn, thường là lãng phí cho video thích ứng. Hiển thị tiến trình tải về và đợi Download.STATE_COMPLETEDCho việc truyền tải quá trình sản xuất, tích hợp Media3 DownloadServiceGiới hạn lưu trữ và Android Yêu cầu xác thực dịch vụ/ ghi rõ; các hàng đợi chứng minh chạy trong tiến trình xử lý.

Nội dung ngoại tuyến nhiều khóa: Media3 khôi phục một bộ khóa ID cho một mục phát lại. Giấy phép lâu dài của nó phải chứa mọi khóa cần thiết cho video, âm thanh đã chọn và các track được mã hóa khác. Kiểm tra điều này với chính sách máy chủ và gói thực của bạn; việc bật nhiều phiên không kết hợp các giấy phép ngoại tuyến độc lập.

7. Đường dẫn B: phục hồi với những gì đã có Media3 trình phát#

Đọc toàn bộ việc tải về và sử dụng các phím dòng chính xác. Đống tải về và phát lại phải chia sẻ vị trí bộ nhớ tạm và bản đồ khóa. Ví dụ này cố ý tắt khả năng truy cập ngược dòng cho phát lại nghiêm ngặt:

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.

Để sử dụng cùng dòng chảy ngoại tuyến với đường dẫn A, xây dựng DrmXPlayer Với cái này mediaSourceFactory và gọi player.prepareOffline(playback)Sự khôi phục bình thường sử dụng địa phương CDM Bằng lái mà không yêu cầu một thẻ mới. Một mục nhập bộ nhớ tạm còn thiếu nên tạo ra lỗi khi bị ngắt kết nối, chứ không phải bật âm thầm lên mạng.

Đường dẫn C: bản xứ Media3không DRM-X AAR#

Bạn không bắt buộc phải sử dụng DrmXPlayer hoặc DrmXOfflineManager. Thêm ba phần phụ thuộc Media3 từ bước 3 và triển khai cùng một hợp đồng phụ trợ khách hàng. Sử dụng session.manifestUrl (không phải session.manifest.url), giấy phép được trả lại URL và các tiêu đề ủy quyền ngắn hạn. Áp dụng chiều cao tối đa được trả về và xử lý việc giải phóng phiên phát trực tuyến khi dừng, thay thế, kết thúc, lỗi và hủy.

Hoạt động bản địa hoàn thành bên dưới được biên dịch như samples:native bên này SDK Phát hành, nó xử lý HTTPS, câu trả lời bị hạn chế đọc, giấy phép nền, câu trả lời cũ và sự ra mắt đặt chỗ. Thay thế điểm kết thúc đã đánh dấu.Content ID và cung cấp biểu tượng đăng nhập của riêng bạn. Ví dụ là điểm khởi đầu; thêm sự cập nhật sản phẩm của bạn, phù hợp với tài khoản và chu kỳ sống ứng dụng.

Hoàn tất hoạt động truyền tải bản địa (Kotlinkhông DRM-X Phụ thuộc
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() }
        }
    }
}

Cho phép tắt máy bản xứ, sử dụng OfflineLicenseHelper.downloadLicensebộ chìa khóa mờ nhạt của nó ID Riêng tư và phục hồi với MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).setKeySetId(id). Những trình trợ giúp biên dịch này bao gồm việc lấy, khoảng thời gian kiểm tra, thay đổi, phát hành và tải xuống/ chơi lại mục. Nó là không phải là ứng dụng toàn bộ về nhà: bạn vẫn còn sở hữu bộ nhớ tạm, dịch vụ tải về, tiến trình, siêu dữ liệu, tổ hợp người dùng/ công cụ uy tín.

Người giúp đỡ bền bỉ bản địa (Kotlinkhông DRM-X Phụ thuộc
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())
    }
}

Trước khi gọi trợ lý cấp thấp này, xác nhận rằng phản ứng hậu phương mới phù hợp với yêu cầu Content IDthao tác, ký và xác thực giai đoạn xác thực. Kiên trì một thao tác ngoại tuyến và danh sách uy tín/ tập tin siêu dữ liệu cùng với bộ khoá IDKhông bao giờ là biểu tượng. Chạy các cuộc gọi trợ lý trên chỉ công nhân để lấy bằng lái HTTP đầu trang dùng setLicenseRequestHeaders trên DRM cấu hình; Widevine Tham số phần mềm phím tùy chọn là một bản đồ khác.

Sự kết hợp mới sẽ được dùng AndroidX Media3 DRM hỗ trợDi sản com.google.android.exoplayer2 Dự án nên theo sau hướng dẫn di trú chính thức; không pha trộn di sản và AndroidLớp X hoặc giả sử các mẫu biên dịch không thay đổi đối với các phiên bản cũ.

9 Kiểm tra, làm lại, phát hành và chuyển đổi ngôn ngữ#

These are separate actions. Run one at a time after the active offline DRM session has been released: use player.stopForOfflineLicenseOperation { /* one operation below */ } with DrmXPlayer, or release the existing native Media3 player first. Do not check a license while it is playing. A check is optional for displaying durations; it must not prevent renewal of an expired license.

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 đọc siêu dữ liệu; checkLicense Các truy vấn có thẩm quyền trên máy cục bộ CDM Thời gian mới/ thả lại cần thiết sự kết nối và quyền hạn hoạt động mới từ hậu phương của bạn. Việc xóa phương tiện không giải phóng bằng lái. Khi tắt, hãy thông báo lại cho kết nối tiếp theo thay vì báo cáo rằng máy phục vụ đã xác nhận nó. Đóng bộ quản lý khi người sở hữu nó bị hủy.

// 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()

Xây dựng sự lựa chọn từ các đường dẫn sẵn sàng và tắt các lựa chọn không sẵn sàng. Tùy chọn này không thể tạo một đường dẫn tiếng Anh không có trong gói. Người dùng ngoại tuyến chỉ có thể chọn ngôn ngữ đã tải về. Bảo trì nhà nước chứng nhận/cache qua màn hình; toàn bộ màn hình là một thay đổi xem/ luồng, không phải là lý do để lấy giấy phép khác.

10 Yêu cầu kết thúc cho phép ngoại tuyến#

Ứng dụng contentId, contentType: "vod", drmSystem: "widevine", playbackMode: "offline", operation: "acquire" | "renew" | "release", offlineLicenseId (Tiếng chuông để có được) và chân thật platformCapabilities. Kiểm tra xem mã số thay đổi / gỡ lỗi là của người dùng xác thực, thiết bị và nội dung. Chọn bên máy phục vụ chính sách, vấn đề mới DRM License Token và trả lại hợp đồng:

{
  "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
  }
}

Đây là những người giữ chỗ giải thích, không phải đáp ứng bằng lái có thể tái sử dụng. Trả lại những giá trị mới từ thao tác thực tế. SDK cũng hỗ trợ integrationMode: "token-proxy" với một ràng buộc drm.authorization Kẻ hủy bỏ, trở lại. Cache-Control: no-store; không bao giờ đăng nhập thành công xác nhận. xem các có thể tải về SDKCủa OFFLINE_INTEGRATION.md và PHP Điểm cuối của mẫu để xử lý yêu cầu bê tông.

11. Kiểm tra thiết bị và bắn lỗi#

Thử / Triệu chứngMong đợi ứng xử / kiểm tra tiếp
SDK Tải về 403Chọn một tổ chức với tài liệu hướng dẫn hoạt động hay hệ thống Enterprise. Một tiến trình nâng cấp có kế hoạch sau khi thử không mở khóa SDK.
Phiên chạy 401 / 403Kiểm tra đăng nhập, phúc lợi xã hội, môi trường, được xuất bản Content ID Không bao giờ bỏ qua xác thực.
Lỗi ngoại tuyến nhưng hoạt động trực tuyếnCheck CDM khoảng thời gian, tải về xong, vị trí bộ nhớ tạm, phím luồng và tất cả các phím đã được cấp phép.
Mất tiếng Anh/ Trung QuốcKiểm tra siêu dữ liệu ngôn ngữ gói, dấu chân thật và cách chọn ngôn ngữ ngoại tuyến.
L1, HDCP hoặc bộ giải mã thất bạiKiểm tra phần cứng vật lý, sự cung cấp, mã hóa và yêu cầu xuất. Một chuỗi khả năng không phải là xác thực.
Khởi động lại, quay, nâng cấpGiấy phép và bộ nhớ tạm bền bỉ tồn tại hỗ trợ thay đổi vòng đời mà không cần lấy bản sao. Không được phép sao lưu vào thiết bị mới.
Chế độ máy bayTắt luôn dữ liệu Wi-Fi và di động; khởi chạy lại ứng dụng, chơi cục bộ và chuyển đổi mọi ngôn ngữ đã hứa.
Hạn sử dụng, áp lực lưu trữ, chuyển đổi tài khoảnHiển thị tình trạng không sẵn sàng/re mới rõ ràng, phục hồi các phương tiện không đầy đủ, và ép buộc người dùng/ công cụ truy cập mà không làm rò rỉ danh mục ngoại tuyến của người dùng khác.

Kiểm tra từng điện thoại được hỗ trợ, máy tính bảng, Android TV/Google TV, Fire TV hay nhúng Android Mô hình WidevineGiải mã an toàn, mã hóa/giải quyết, HDCPHiển thị bên ngoài, thay đổi đồng hồ và ngắt kết nối lâu. Android thiết bị có cùng một DRM Nội dung Hollywood đòi hỏi sự bảo vệ và yêu cầu thiết bị quyền sở hữu, không chỉ là giải mã thành công.

Không có cơ hội nhận được trước khi ngắt kết nối? Đó là một cách triển khai khác: dịch vụ cấp phép trên tàu đủ điều kiện sẽ cấp giấy phép qua mạng cục bộ trong suốt hành trình. Nó không được kích hoạt chỉ bằng cách sử dụng SDK này. DRM-X có thể làm việc với bạn trong quá trình triển khai tùy chỉnh bao gồm các quyền của nhà cung cấp, cung cấp, bảo vệ khóa và kiểm tra chấp nhận ngắt kết nối hoàn toàn. Xem trên máy bay DRM để biết máy bay, xe buýt, xe lửa và tàu thủy.

Danh sách phát và bộ điều khiển dùng chung của Universal Player#

Trình phát Android và Apple có Previous/Play-Pause/Next, CC, Settings và điều khiển toàn màn hình. Move up / Move down giữ nguyên video hiện tại và lưu thứ tự danh sách cho cả phát trực tuyến lẫn ngoại tuyến. Lựa chọn phát ngẫu nhiên và lặp lại được giữ khi mở lại ứng dụng; nếu tắt cả hai, phát sẽ dừng sau mục cuối.

Cung cấp cùng khách/ xem storageNamespace phải DrmXPlayer và playerView.storageNamespace trước khi cài đặt danh sách nhạc. VOD tiến trình được lưu thường xuyên và tạm dừng/ Dừng, sau đó phục hồi cho điều đó Content ID trong truyền tải hay tải về. Phát lại trực tiếp không dùng lưu VOD vị trí đã tải về. Việc tải về hoàn tất giữ lại cùng một hành vi xáo trộn/repeat/resume mà không có nguồn truyền thông mạng. Tình trạng chuyển động thiết bị này không đồng bộ với sự tiến triển giữa các thiết bị.

Web và Android có bảng tối và điều khiển xanh gọn. Danh sách hỗ trợ trước/sau, trộn, lặp một/tất cả và chuyển tự động có thể hủy. Mỗi mục dùng yêu cầu cấp quyền mới. Chương và bản chép lời lấy từ danh mục.

Có tốc độ, hẹn giờ ngủ, phụ đề, chất lượng cho phép, ngôn ngữ, dấu trang phiên và lặp A–B. Web thêm phím tắt, tiếp tục cục bộ tùy chọn riêng từng người xem, tìm bản chép lời và PiP được hỗ trợ. Android giữ tải Widevine ngoại tuyến và gia hạn. Truyền màn hình và tiến độ liên thiết bị cần tích hợp riêng.

Thiết lập biểu tượng cá nhân và tầu Enterprise

Đóng dấu không cho phép trên bề mặt phim. trình phát Cho biểu tượng, thông tin sản phẩm và trình phát Phiên bản. Mạng cũng cung cấp thông tin này bằng cách nhấn chuột phải. Mô phỏng điện thoại công cộng bao gồm WayMaker, lòng tốt của Chúa, dầu tươi và video thử nghiệm 4K; mỗi sự lựa chọn nhận được giấy phép riêng.

Đặt playerBranding thành {"mode":"hidden"} hoặc {"mode":"custom","logoUrl":"https://your-site.example/logo.png","label":"Your brand"} trong backend đáng tin cậy. Nền tảng kiểm tra Professional hoặc Enterprise đang hoạt động. Gói khác dùng {"mode":"default"}; yêu cầu tùy chỉnh/ẩn không được phép trả 403. Tính năng này độc lập với watermark bảo mật.

Bản PHP có danh sách và thương hiệu chỉnh sửa được. ZIP Android có ví dụ callback danh sách. Giữ cấp quyền danh sách trong backend; không nhúng thông tin client API vào bất kỳ trình phát nào.