Android SDK とネイティブ Media3
Android プレーヤーをダウンロードして、ドラッグで並べ替えられるプレイリスト、シャッフル・リピート設定の保存、オフラインでの続きからの再生をお試しください。DRM-X またはネイティブ Media3 で保護された再生を組み込めます。
UI のカスタマイズ、パラメーターの既定値、ライフサイクルの例については、PLAYER-UI-CONFIGURATION.md を参照してください。プレーヤー標準のポップアップパネルは不透明です。字幕とウォーターマークの不透明度は別に設定します。
SDK ZIP 内の DEMO-QUICKSTART.md に従って PHP デモをお試しください。自社のコンテンツを使う場合は、まずウェブサイトとバックエンドを統合し、次に MOBILE-AND-TV-APPS.md に従ってアプリを同じログインと視聴権限の仕組みに接続してください。
Android プレーヤー # を試す
Androidのサンプル APK をダウンロード
1.0.0-beta.11 · 12.15 MB · Android 6.0+ (API 23+) · DRM-X 6.0 Player
- このページをAndroidの携帯電話で開き、APKをダウンロードしてください。
- ダウンロードしたファイルを開きます。 Android に尋ねると、使用したブラウザまたはファイルマネージャからインストールを許可し、 Install をタップします。
- DRM-X 6.0 Player を開き、接続を維持し、ビデオの Play ボタンをタップします。 このアプリは、再生を押すときにストリーミングライセンスを要求します。
- 品質、オーディオ、キャプション、再生速度を選択するには、Settingsを開きます。 電話を回転させ、フルスクリーンを継続します。
- 動画の三本線メニューを開き、Download を選びます。Downloaded videos をオンにすると Offline Mode、オフにすると Online Mode になります。Offline Mode では、その動画のメニューから Delete を選び、確認します。ライセンス更新には接続と現在の利用許可が必要です。オフラインエラー時は、オフラインのままにするかオンラインへ切り替えるかを選べます。
すでにキュートされたメディアのダウンロードは、バックグラウンドで続行します。 Android がライセンスやトラックの準備中にアプリを停止すると、残りのアイテムの準備を完了するために接続されている間、再オープンします。 既存のダウンロードが保持されます。 オフライン再生は、キャッシュされたメディアのみを使用します。 不完全なまたは期限切れのダウンロードは、完了または更新が必要です。
サンプル APK は公開され、評価のために署名されます。AAR と完全な SDK は、対象のコンソールアカウントによって利用可能に残ります。このデモとは別にキーとバックエンドのログインを署名する独自の生産アプリをキーに保ちましょう。
APK チェックサムと USB インストール
SHA-256:
b96ba527ddf9abae5bff2f8b85a39a12b59c934aaacde1961c8096aa32524c54Android プラットフォームツールと認証済み USB デバイスを使って:
adb install -r drmx-android-player-1.0.0-beta.11.apkアンインストールせずに、既存の公式サンプルにこのAPKをインストールします。同じ署名キーとアプリケーションIDは、ダウンロード、オフラインライセンス、保存された位置と設定を保存します。あなた自身のビルドが別の署名キーを使用している場合は、別々のアプリケーションIDを使用して、両方のアプリを維持し、サンプルのオフラインライセンスを保存します。
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.
Offline Mode で動画の三本線メニューを開き、Delete を選びます。確認すると、その動画の端末内の暗号化メディアとライセンス参照だけが削除されます。Cancel を選ぶと保持されます。再接続すると再ダウンロードできます。
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#
2026 年 9 月 17 日リリース。電話アプリと顧客 SDK には 4K ライブ DRM テストが含まれています。ライブ再生では新しいマニフェストが使用されるため、ダウンロードできません。テスト イベントは、アクティブなブロードキャスト中にのみ使用できます。 AWS および OBS 設定ガイド に従って、独自のイベントを作成します。
Android プレーヤーのプレビューで新しいもの。12#
リリース14 9月2026。 ダウンロードした再生を修正し、予期しないライセンス要求時に Widevine ライセンスの有効期限が切れるか、または1分未満の残りがあります。 これにより、 Media3 エラー 6004, , , HTTP 接続中のエラー、または DNS 切断中のエラー。
プレーヤー は、ダウンロードしたライセンスをローカルで復元します。有効なライセンスは残りの再生時間を保持します。期限切れのライセンスは の接続を表示し、Renew をタップします。ライセンスの有効性と保護ルールは、デバイスによって強制されます。ネットワーク操作が失敗したときに数値 HTTP ステータスも含まれ、リクエストヘッダ、URL、ライセンス応答を露出せずに。
- 既存の公式アプリに Preview.12 をインストールします。現在のアプリデータを保存し、ダウンロードしてください。
- WayMakerまたは別のダウンロードしたビデオが更新する必要がある場合は、インターネットに接続し、そのビデオを選択し、Renewをタップします。
- Licenseが更新されるのを待ってください。 オフラインで再生する準備ができました。 を選択し、オフラインで再生を選択します。 暗号化されたビデオはダウンロードされています。
このリリースは preview.11 の操作部とプレイリスト機能を維持しています。公開評価アプリと内蔵プレイヤーの表示バージョンは preview.12 です。そのリリース時点で別配布されていた Console SDK は preview.10 / バンドル 20260910.8 でした。
Android プレーヤーのプレビューで新しいもの。11#
2026年9月14日リリース。この評価アプリは、最新の プレーヤー コントロールとプレイリストの改善、一貫性のあるインタラクションスタイルを Android と Apple プレーヤー に含まれています。
- 動画内のコントロール: は Play/Pause を中心とし、マルチビデオプレイリスト、CC、設定、フルスクリーンボタンを次へ。 再生中に 2 秒後に非表示をコントロールします。 ビデオをタップすると、再びそれらを明らかにします。
- キャプションと再生設定: は CC を使用して、利用可能な字幕を表示または非表示にし、設定はキャプション言語、オーディオ、再生速度、および許可された品質を選択します。 オフラインの選択肢は、ダウンロードに含まれるトラックのみを使用します。
- Drag to reorder: は、プレイリストのアイテムのハンドルをドラッグして、その位置を変更します。 保存された注文は、アプリの再起動を生き、ダウンロードしたプレイリストに反映されます。 リオーダーは、現在のビデオ再生を維持します。 アクセス可能な移動/アクションを移動するためのハンドルをタップします。
- 保存されたシャッフルとリピート: 選択したモードは、オンラインでとオフラインの再生で再起動を強調し、存続します。シャッフルとオフの両方で、ビデオは保存された注文に従い、最終アイテムの後に停止します。プレイリストメニューの「次を再生」アクションが削除されました。
- 停止した場所の再開: 各ビデオの保存した VOD 位置は、ストリーミングやダウンロードした再生に戻るときに復元されます。 進行状況は、現在のビューアのためにこのデバイスに保存されます。 デバイス間で同期しません。
プレイリストを整理し、順序を保存して再生を再開できます。オフライン視聴には、完了したダウンロードと、有効期限内で利用可能なオフラインライセンスが必要です。
preview.11 では公開評価用 APK を更新しました。現在の Console SDK は 1.0.0-beta.14、バンドルは 20261028.1 です。以下の手順はこのバンドルを使用します。
1。 統合パスを選択#
DrmXPlayer、電話サンプル、オフラインマネージャを使用してください。 独自のログインとビジネスルールを保持してください。
PATH B・KEEP YOUR UIMedia3 + DRM-X ヘルパー既存の プレーヤー を保ち、スタックをダウンロードしましょう。DrmXOfflineManager は永続的なライセンスを管理しましょう。
PATH C・C NATIVE ONLYAARのないMedia3同じバックエンド契約とネイティブライセンスのライフサイクルを自分で実装します。 DRM-X プレーヤー の依存関係はありません。
Developer Tools → Device SDKs からダウンロードします。SDK の利用には有効な Professional または Enterprise の権限が必要で、承認済みの Professional 評価も含まれます。Standard でも独自プレーヤー向けのネイティブ APIs を利用できます。セッション作成時とライセンス更新時に SDK 権限を確認し、オフラインライセンスの有効期限はアカウントのサービス期間を超えません。
現在のプレビュー: 1.0.0-beta.14 · 顧客バンドル 20261028.1 · Media3 1.11.0。 ZIP にはローカル Maven リポジトリ、Gradle wrapper、注釈付き README、電話/ TV/native サンプル、 apks/ にインストール可能な電話サンプル APK、およびオフライン統合ノートが含まれています。 顧客 認証情報 や保護された映画はありません。
2. コンテンツとバックエンドを準備#
- DRM-X 環境で Widevine 互換暗号化 DASH のタイトルをパッケージ化して公開します。 が Content ID を発行しました。
- サーバのEnvironment ID、Site Key、Access Key を構成します。 作業開始点に をダウンロードできる PHP インテグレーション を使用します。
- ユーザーのログインと認証チェックを実行します。アプリは、あなたのバックエンドを短命のユーザートークンと呼びます。あなたのバックエンドは、コンテンツ、ポリシー、信頼できるURLを選択します。
- 物理的な Widevine 対応デバイスでオンラインでの再生を最初に実行します。 ストリーミング契約が機能した後にのみオフライン取得を追加します。
暗号化された動画と DRM ライセンスは別の経路で配信されます
CDN は暗号化された動画を配信します。バックエンドが視聴権限を確認し、DRM ライセンスサービスが対応デバイスでの復号を許可します。
- ストレージ / CDN
暗号化されたメディアをプレーヤーに配信します。
- お客様のバックエンド
視聴権限を確認し、DRM-X に再生の認可をリクエストします。
- DRM-X
再生の認可情報を検証し、DRM ライセンスリクエストを処理します。
- プレーヤー + デバイス
デバイスの DRM システムを使ってライセンスを取得し、暗号化されたメディアを再生します。
| この値を変更する | 所属する場所 | 意味する |
|---|---|---|
| sessionEndpoint | Android | HTTPS の再生セッションエンドポイントは、生のライセンス サーバー URL ではありません。 |
| offlineEndpoint | Android, オフライン のみ | 取得/更新/リリース承認エンドポイント。 |
| contentId | Android リクエスト; バックエンドの検証 | 公開保護された Content ID のビューアは、アクセスする権利があります。 |
| 顧客アクセストークン | ログイン後のアプリメモリ | 短いユーザートークンは、Site Key や Access Key ということはありません。 |
| Environment ID、Site Key、Access Key | サーバシークレット構成のみ | DRM-Xアカウント認証情報。APK、HTML、またはソースコントロールでそれらを出荷しないでください。 |
| 方針と信頼できるマニフェストURL | お客様のバックエンド | ビジネスの資格とパッケージのマッピング。アプリからこれらを受け入れないでください。 |
3. SDK#をインストールして実行する
Android Studio で抽出された ZIP のルートフォルダーを開きます。 JDK 17, Android SDK 36 と Android Gradle Plugin 9.2.0 を使用します。 含まれているラッパーは Gradle 9.4.1 を使用します。 最小デバイス API は 23 です。 依存関係の構築には、インターネットアクセスが必要です。 samples.phone を選択し、実行します。 その初期値は、パブリックデモバックエンドとタイトルを使用します。 自分のバックエンドに切り替える前に、サインインとストリーミングコントロールに従ってください。
# Windows
gradlew.bat :samples:phone:assembleDebug
# macOS / Linux
sh gradlew :samples:phone:assembleDebug
既存のアプリでは、ZIP の maven/ フォルダをプロジェクトのルートにコピーします。 settings.gradle.kts:
dependencyResolutionManagement {
repositories {
maven { url = uri(rootDir.resolve("maven")) }
google()
mavenCentral()
}
}
アプリケーションモジュールのbuild.gradle.ktsで:
dependencies {
implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14")
}
代わりにスタンドアローン AAR をインストール
app モジュールの libs/ に AAR を置きます。 フラット AAR は自動的な依存関係の解決を持たないので、3つの一致 Media3 の依存関係を自分で追加します。 両方のフォームをインストールしないでください。
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")AndroidManifest.xml では、インターネットアクセスを許可し、この最小限のサンプルのバックアップを無効にします。 バックアップを必要とするプロダクションアプリでは、特にデバイス・バウンド・オフライン・ライセンス・メタデータを除外します。
<uses-permission android:name="android.permission.INTERNET" />
<application android:allowBackup="false" android:usesCleartextTraffic="false" ... >
携帯電話のサンプルの検索 MainActivity.kt 詳しくはこちら drmx.sessionEndpoint, drmx.offlineEndpoint, drmx.contentId そして、 customer.loginEndpoint. マークされたフォールバックの設定を変更するか、ログイン後にアプリケーションからこれらのインテントの追加を渡すか。 パス customer.accessToken ランタイムで。 ZIP README 各パラメータを記述し、どのデモが置換するのかを説明します。
AAR #を使用してテストAPKをビルドする
この例では、実行可能な電話プロジェクトをフルSDK ZIPから使用し、スタンドアローンAARを明示的にリンクします。 同じアプローチは、既存の Androidアプリで動作します。
- full Android SDK ZIP をダウンロードし、抽出します。 Android Studioのルートフォルダを開きます。
libs/drmx-universal-player-1.0.0-beta.14.aarは展開先のルートに置いたままにしてください。JDK 17、Android SDK 36、付属の Gradle wrapper を使用します。samples/phone/build.gradle.ktsでは、DRM-X Maven の依存関係をdependencies内の次のエントリに置き換えます。そのファイルの残りの部分を保ちましょう。
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")
削除する implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14") 同じように SDK 2回は同梱されていません。 フラット AAR 3つのニーズ Media3 上記の依存関係を示します。
- ダウンロードしたデモを自分のテストビルドと一緒にインストールしておくには、電話モジュールの
applicationIdをcom.example.drmxtestに変更します。このサンプルに名前空間は変更されません。 - ビルド APKそれからそれを取付けて下さい USB-コネクティッド Android デバイスと USB 認可されたデバッグ。
# 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
テストアプリを開き、Playをタップします。 付属の電話ソースは、最初に同じパブリックデモを使用します。 独自のタイトルをテストするには、マークされたセッション/オフライン/ログインエンドポイントを置き換え、MainActivity.kt で Content ID を発行し、ランタイムで独自のユーザーの短命認証を提供します。 あなたのサーバーは Site Key と Access Key を保持します。
コンパクトなコントロールは、AAR で DrmXPlayerView から来ます。 提供されたアクティビティは、プレイツーオーサライズ、応答性回転、所有権のダウンロード、オフライン再生、および更新を実証します。 このビューを独自のアプリに追加する際に、以下の の制御統合例 を参照してください。
4. サーバーおよび要求のデフォルトポリシー#
Android アプリケーションは、再生セッションのバックエンドを要求します。 バックエンドは、ユーザを認証し、エン資格を検証し、Content ID を公開したメディアにマップし、 DRM-X を呼び出します。 完全なストリーミング契約の プレイバックセッション API を参照してください。
{
"contentId": "YOUR-PUBLISHED-CONTENT-ID",
"contentType": "vod",
"drmSystem": "widevine",
"playbackMode": "streaming",
"platformCapabilities": {
"clientPlatform": "android",
"drmSystems": ["widevine"], "manifestTypes": ["dash"],
"maximumHeight": 1080, "secureDecoder": false, "persistentState": false
}
}
上記の保守的な機能は Widevine L1 を主張しません。 SDK の機能ヘルパー、または、実際のデバイスに対する独自のデバイス評価を使用してください。 クライアント機能は、互換性の選択を支援します。 それらは質の高い資格を付与しません。
PHP は、オンラインの再生のために multi-tier-standard にデフォルトをサンプルします。パッケージが 1 つのキーを使用するときに、文書化された単一キー ポリシーを選択します。あなたのポリシーは、パッケージのキー モデルとビューアの権利に一致する必要があります。サーバーの構成または DRMX_LICENSE_POLICY_TEMPLATE 環境設定を変更し、 プレーヤー のクエリパラメータではありません。 ライセンスポリシーを参照してください。
playback_policy.persistent を true に設定し、ライセンス、レンタル、再生、および更新期間を選択します。リファレンスでは、それぞれ 7 日 / 3 日 / 48 時間 / 7 日を使用します。これらはビジネス ルールの例であり、スタジオの承認やプラットフォーム全体のデフォルトではありません。一致するメタデータを返します。 Widevine ライセンスは引き続き有効です。5. パスA:DrmXPlayer#を通して遊びます
アクティビティ内では、PlayerView を作成し、ログイン後、安全なウィンドウを使用して prepare を呼び出します。 セッションのラッパー交換は、Widevine を設定し、返された最大高さを適用し、停止時にストリーミング予約を解放します。 インポートは com.haihaisoft.drmx.player と 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()
DrmXPlayerListener を認証、準備、再生、エラー UI に登録します。 ライセンストークンやレスポンスボディをダンプするのではなく、安全なエラーコードを表示します。 含まれた電話サンプルを使用して、ライフサイクル、フルスクリーン制御、キューをダウンロードします。
プレーヤーの制御および中断されていない回転#
動画でPlayをタップすると、承認が開始されます。 画面または設定を開くと、ストリーミングライセンスが取得されません。 ミューズ/再開、設定を変更し、電話を回転させると、現在のセッションが保存されます。

| コントロール | 顧客ができること |
|---|---|
| 品質・品質 | マニフェスト内の対応済みかつライセンスで許可された解像度、または Auto を選択してください。選択できない項目には、ライセンスやデバイス/DRM による制限理由が表示されます。Auto にも同じ制限が適用されます。 |
| オーディオトラック | パッケージによって供給されるサポートされている音声言語を選択してください。 |
| サブタイトル/CC | キャプチャ言語またはオフを選択します。 サポートされているキャプションが存在し、それらを直接切り替えるときにCCボタンが表示されます。 |
| キャプションの出現 | フォント、テキストの色とサイズ、背景/ウィンドウの色と不透明度を調整します。 設定はデバイスに保存されます。 |
| 再生速度/スリープタイマー | 選択された間隔の後で0.25–2×の速度で演劇するか、または自動的に停止して下さい。 |
| Seek / フルスクリーン | タイムラインをスクラブし、10秒を探し、または両面タップします。 再生を再起動せずに風景に回転します。 |
各動画のメニューには Move up / Move down と、オンライン時の Download またはオフライン時の Delete があります。完了済みの動画では Download が無効になります。Downloaded videos スイッチでオフライン再生を選びます。利用できるのはキャッシュ済みのトラックだけです。独立したダウンロード、削除、プレイリスト一括ダウンロードのボタンはありません。
ログのコピー 情報フッターのコピーはテキストとして最近のエラーをコピーするので、スクリーンショットなしでサポート情報を送信できます。 レポートにはレポートが含まれています ID、操作、 SDK/Android/deviceバージョン、数値 CDM/Media3 エラー, 選択したスタックフレーム, 接続/ダウンロード/再生状態と繰り返し数. 彼らは除外します 認証情報、媒体の URL、キー、キー セット ID、免許証データおよび完全な CDM ダンプ。
サンプルは、最大32のローカルレポート(64)を保持します。 KB) 5時間。 接続および署名されたとき、それは30秒に一度に構成された顧客のバックエンドに永続的な間違いを、アップロードします。 パブリックデモサーバーは5時間警告/エラーを保持し、繰り返されたでき事を結合し、512でファイルをキャップします KB 毎分プルーン。 ルーチンの進行と成功した再生は記録されません。 カスタムの統合は、明示的に独自の同じオリジンを供給する必要があります drmx.diagnosticsEndpoint;診断の間違いは決して再生を中断しません。
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()
供給された電話マニフェストは、オリエンテーションと画面サイズの変更を処理します。 onConfigurationChanged は同じアクティビティと プレーヤー のサイズを変更します。 両方の部分を合わせると、両方の部分を保持します。 アクティビティを再作成するホストは、再生所有者を保持し、ビューを再添付する必要があります。 プロセスの死は、回転とは異なるであり、通常の状態の復元を必要とします。 4K/8K オプションには、マッチングコンテンツ、ライセンスのエンタイトルとデバイス/ DRM サポートが必要です。 CDM は、承認者のままです。
カスタム プレーヤー では、アクティブなオフライン DRM セッションを止めて、同じ永続的なライセンスを更新または解放します。 SDK の player.stopForOfflineLicenseOperation { /* call manager here */ } は、Media3 の再生キューがメディアを解放するのを待ちます。 平野 stop() は非同期的に動作するキューを待ちます。 同じライセンスが再生されている間、別の checkLicense() を実行しないでください。 再生自体は CDM の状態を検証します。 付属のサンプルは、ハンドの完全な更新と無効な操作をオンに表示します。
6. ライセンスを取得し、メディアをダウンロード#
オフライン再生の準備
ダウンロードの完了と、デバイスに紐づく有効なライセンスの両方が必要です。
- 暗号化メディアをダウンロードする
オンラインの間に、動画と必要な音声・字幕トラックをダウンロードします。
- 永続ライセンスを取得する
オンラインの間に、デバイスに紐づく Widevine ライセンスを取得します。不透明な Key-set ID は非公開で保存してください。
- 準備状況を確認する
ダウンロードが完了し、ライセンスが有効であることを確認します。
- オフラインで再生する
キャッシュ済みメディアを読み込み、ローカルのライセンスを復元します。更新と解放にはインターネット接続が必要です。
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 */ }
}
SDK は不透明な Widevine key-set ID と機密情報を含まないメタデータを保存します。ライセンスは CDM が保持します。DRM License Tokens、未加工のキー、チャレンジ、ライセンス応答のバイト列は永続保存しません。com.haihaisoft.drmx.offline.licenses.v1.xml をバックアップ対象から除外してください。
1つのアプリケーション所有のMedia3 SimpleCache、ダウンロードマネージャとデータベースを作成します。ライセンスされたMediaItemを使用してDownloadHelperを選択し、maximumHeight内のビデオを選択します。 Media3 1.11では、準備されたコールバックはonPrepared(helper, tracksInfoUpdated)です。 電話サンプルのenqueueDownloadとOfflineTestRuntimeは実行可能な実装を示しています。
// 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()
オフラインで表示するすべての言語をダウンロードします。 ストリームキーなしでcreateDownloadRequestを呼び出すと、一般的に適応ビデオのために不十分であるすべてのトラックをダウンロードします。 ダウンロードの進行状況を表示し、Download.STATE_COMPLETEDを待ちます。 生産の背景転送のために、Media3 DownloadService、ストレージの制限とAndroid foreground-service/notificationの要件を統合します。 デモキューは、プロセス内で実行されます。
7. パスB:あなたの既存のMedia3 プレーヤー #と復元して下さい
完全なダウンロードを読んで、その正確なストリームキーを使用します。 ダウンロードと再生スタックは、同じキャッシュの場所とキーマッピングを共有しなければなりません。 この例では、厳密なオフライン再生のための上流アクセスを意図的に無効にします。
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.
Path A で同じオフラインフローを使用するには、この mediaSourceFactory で DrmXPlayer をビルドし、 player.prepareOffline(playback) を呼び出します。通常の復元では、新しいものを要求せずにローカル CDM ライセンスを使用します。接続されていない受信中にキャッシュエントリがエラーになるはずですが、インターネットにサイレントに切り替えることはありません。
8. パスC: ネイティブMedia3、DRM-X AAR#
DrmXPlayer または DrmXOfflineManager を使用する必要はありません。ステップ 3 の 3 つの Media3 依存関係を追加し、同じ顧客バックエンド コントラクトを実装します。 session.manifestUrl (session.manifest.url ではない)、返されたライセンス URL、および有効期限の短い認証ヘッダーを使用します。返された最大高さを適用し、停止、置換、終了、障害、破棄時のストリーミング セッションの解放を処理します。
以下の完全なネイティブアクティビティは、この SDK リリースと一緒に samples:native としてコンパイルされます。 これは、 HTTPS を処理し、バインドされたレスポンスの読み込み、背景の承認、ストールレスポンス、および予約リリースを処理します。 マークされたエンドポイント/ Content ID を置換し、独自のログイントークンを供給します。 この例は、開始点です。 生産のリフレッシュ、アカウントスイッチ、アプリナビゲーションのライフサイクルを追加します。
完全なネイティブストリーミング活動(Kotlin、DRM-Xの依存関係なし)
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() }
}
}
}
ネイティブオフラインライセンスの場合、使用 OfflineLicenseHelper.downloadLicense、persist の不透明のキー セット ID 個人的に復元し、 MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).setKeySetId(id). 以下のコンパイルされたヘルパーは、取得、期間チェック、更新、リリース、およびダウンロード/プレイバック項目をカバーしています。 完全なダウンロードアプリケーションではありません: キャッシュ、ダウンロードサービス、進捗状況、メタデータ、ユーザー/デバイスバインディング、信頼できる運用権限を保有しています。
ネイティブ永続的ライセンスヘルパー(Kotlin、DRM-Xの依存関係なし)
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())
}
}
この低レベルのヘルパーを呼び出す前に、新しいバックエンドの応答が要求されるマッチであることを検証します。 Content ID、操作、署名付きユーザー/デバイスおよび承認の妥当性期間。 安定したオフラインを主張するLicenseIdおよび信頼できるマニフェスト/トラックメタデータをキーセットと一緒に ID、トークンは絶対にありません。 ワーカーの呼び出しをワーカースレッドにブロックする実行。 ライセンス HTTP ヘッダの使用 setLicenseRequestHeaders とりあえず DRM 構成; Widevine オプションのキー要求パラメータは異なるマップです。
新しいインテグレーションは、AndroidX Media3 DRMサポートを使用する必要があります。 レガシーcom.google.android.exoplayer2プロジェクトはの公式マイグレーションガイドに従うべきです。 レガシーとAndroidXクラスをミックスしたり、古いバージョンと変更されていないサンプルコンパイルを仮定したりしないでください。
9. チェック、更新、リリース、切り替え言語#
これらは別のアクションです。 アクティブなオフライン DRM セッションがリリースされた後、一度に 1 つを実行します。: DrmXPlayer で player.stopForOfflineLicenseOperation { /* one operation below */ } を使うか、既存のネイティブ Media3 プレーヤー を最初にリリースします。 再生中にライセンスをチェックしないでください。 期限切れのライセンスの更新を防ぐためのチェックはオプションです。
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 はメタデータを読み込みます。checkLicense は、権限のあるローカル CDM の期間をクエリします。更新/リリースには、接続とバックエンドから新しい操作固有の認証が必要です。メディアを削除しても、ライセンスはリリースされません。接続が解除されると、サーバーが確認したことを報告するのではなく、次の接続のキューリリースがキューされます。所有者が破壊されると、管理者を閉じます。
// 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()
実際の利用可能なトラックから選択をビルドし、利用可能な選択肢を無効にします。 優先順位は、パッケージから不在な英語トラックを作成することはできません。 オフラインユーザーは、ダウンロードした言語のみを選択できます。 画面の回転中にライセンス/キャッシュ状態を保存します。 フルスクリーンは、別のライセンスを取得する理由ではなく、ビュー/ウィンドウの変更です。
10. オフライン認可エンドポイント# を実行
アプリの投稿 contentId, contentType: "vod", drmSystem: "widevine", playbackMode: "offline", operation: "acquire" | "renew" | "release", offlineLicenseId (初期取得のためのnull) と真正な platformCapabilities. 更新/リリース ID が認証されたユーザー、デバイス、コンテンツに属していることを確認します。 ポリシーのサーバーサイドを選択し、新しい DRM License Token を発行し、マッチング契約を返します。
{
"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
}
}
これらは、再利用可能なライセンス応答ではなく、説明的なプレースホルダーです。 実際の操作から新鮮な値を返す。 SDK は、drm.authorization の区切り文字で integrationMode: "token-proxy" もサポートしています。 Cache-Control: no-store を返します。 成功した認証機関をログに記録しません。 ダウンロード可能な SDK の OFFLINE_INTEGRATION.md と PHP のサンプルのオフラインエンドポイントを具体的なリクエスト処理を参照してください。
11. デバイスとトラブルシューティングのテスト#
| 試験・症状 | 期待される行動/次のチェック |
|---|---|
| SDK ダウンロードは403を返します | アクティブなプロフェッショナルまたはエンタープライズサブスクリプションで組織を選択します。 トライアル後の計画的なアップグレードは、SDK のロックを解除しません。 |
| セッション 401 / 403 | ログイン、エンタイトル、環境、公開されたContent IDおよびポリシーを確認してください。 認証を迂回しないでください。 |
| オフラインは失敗しますが、オンライン作品 | CDM の長さ、ダウンロード完了、キャッシュの場所、ストリームキー、および必要なすべてのライセンスキーを確認します。 |
| 英語 / 中国欠落 | パッケージ言語のメタデータ、実際のトラックおよびオフライン言語選択を点検して下さい。 |
| L1、HDCPまたはデコーダーの失敗 | 物理的なハードウェア、プロビジョニング、コーデックおよび出力要件を確認してください。 機能文字列は認証されません。 |
| 再起動、回転、アップグレード | 永続的なライセンスとキャッシュは、重複取得せずにサポートされたライフサイクルの変更を存続します。 バックアップは、ライセンスを新しいデバイスに移行してはいけません。 |
| 飛行機モード | Wi-Fi とモバイルデータを無効化し、アプリを再起動し、ローカルで再生し、約束された言語を切り替えます。 |
| 有効期限、貯蔵圧力、アカウント スイッチ | 未使用/更新状態を表示し、不完全なメディアを回復し、別のユーザーのオフラインカタログを漏らさずにユーザー/デバイスアクセスを強化します。 |
サポートされている電話、タブレット、それぞれを修飾します。 Android TV/Google TV, Fire TV または埋め込まれる Android モデル Widevine、安全な解読、コーデック/決断、 HDCP、外的な表示、クロック変更および長い切断。 ないすべて Android デバイスは同じを持っています DRM 機能。ハリウッドのコンテンツは、権利者の保護とデバイス要件を要求します。, だけでなく、成功したデコード.
Universal Player プレイリストと共有コントロール#
Android と Apple のプレイヤーには Previous/Play-Pause/Next、CC、Settings、全画面の操作があります。Move up / Move down は現在の動画を維持し、オンラインとオフラインで使うリスト順序を保存します。シャッフルとリピートの選択は再起動後も維持され、両方がオフなら最後の項目で再生が終了します。
プレイリストを設定する前に、同じ顧客/ビューア storageNamespace と DrmXPlayer に playerView.storageNamespace を供給します。 VOD の進捗状況は、定期的に保存され、一時停止/停止し、ストリーミングまたはダウンロードした再生中にその Content ID のために復元されます。 ライブ再生は保存された VOD 位置を使用しません。 完了したダウンロードは、ネットワークメディアソースなしで同じシャッフル/受信/再開動作を保持します。 このデバイスローカル状態は、デバイス間の進捗状況を同期しません。
Web と Android は小さな青い操作ボタンと暗い設定パネルを共有します。次・前、シャッフル、1件・全件リピート、取り消し可能な自動送りを提供します。項目ごとに新しい認可要求を行い、章と文字起こしはコンテンツカタログから取得します。
速度、スリープタイマー、字幕、許可画質、音声言語、セッションブックマーク、A–B リピートを提供します。Web はキーボード操作、任意の視聴者別ローカル再開、文字起こし検索、対応ブラウザーの PiP を追加します。Android はネイティブ Widevine のオフラインと更新を維持します。キャストや機器間の進行同期には別途アプリ統合が必要です。
専門および企業ロゴの設定
ブランディングは、ビデオ表面を離れます。 認定ロゴ、製品情報、プレーヤーバージョンの「設定」→「約プレーヤー」を開きます。 Webは、この情報を右クリックで提供します。 公衆電話のデモには、WayMaker、GodのGoodness、Fresh Oil、および4K Test Videoが含まれます。 すべての選択は、独自の承認を受け取ります。
信頼できるバックエンドで playerBranding を {"mode":"hidden"} または {"mode":"custom","logoUrl":"https://your-site.example/logo.png","label":"Your brand"} に設定します。有効な Professional または Enterprise 契約を確認します。他プランは {"mode":"default"} を使用し、許可のないカスタム・非表示要求は403となります。セキュリティ透かしとは別の機能です。
PHP ダウンロードには編集可能なプレイリストとブランド設定があります。Android 顧客 ZIP にはコールバック例があります。プレイリスト認可はバックエンドで行い、どちらのプレーヤーにも API クライアント認証情報を埋め込まないでください。
DRM-X 6.0