# Android SDK وMedia3 الأصلي

نزّل مشغل Android الذي يتضمن عناصر تحكم داخل الفيديو وقوائم تشغيل قابلة للسحب وحفظ إعدادات التشغيل العشوائي والتكرار واستئناف التشغيل دون اتصال. ادمج التشغيل المحمي باستخدام DRM-X أو Media3 الأصلي.

**اختر المشغّل، واحتفظ برخصة واحدة**استخدام DRM-X 6.0 Android SDK لدمج مرشد، أو الحفاظ على السكان الأصليين Media3 المشغّلكلاهما يتواصلان مع مساندتك الموثّقة Widevine- Android SDK دعم التصفير والتدفئة المستمرين VOD على نحو متوافق API 23+ نبائط، بما في ذلك الهواتف، والأقراص، والتلفزيون، والمزروعات Android أنظمة. 

## جرب Android المشغّل[#](#try-android-app)



**تحميل، تركيب، تشغيل، لعبة الصحافة.**The free evaluation app uses our public demo video and signs in automatically. You do not need to build an APK or enter DRM-X بيانات الاعتماد to try it. 

[Donload Androidعينة APK ](https://docs.drm-x.com/downloads/android/1.0.0-preview.12/drmx-android-player-1.0.0-preview.12.apk)



**1.0.0-preview.12** · 7.02 MB · Android 6.0+ (API 23+) · DRM-X 6.0 Player · آخر تحديث 2026-09-14


- افتح هذه الصفحة على هاتفك Android وتحمّل APK
- افتح الملف المُحمّل Android أطلب، السماح بالتركيب من المصفح أو مدير الملف الذي استخدمته، ثم صنبور **Install**.
- مفتوح **DRM-X 6.0 مشغّل الفيديو**ابقوا متصلين و اسجلوا الفيديو **اللعب** الزر، التطبيق يطلب رخصة تيار عندما تضغط على اللعب
- يفتح **Settings ** لاختيار الجودة، الصوت، الأصفاد أو سرعة العزف
- Tap **الحمولة** للفيديو الحالي أو **عدد اللاعبين** للجميع VOD أصناف - تستخدم الحمولات أعلى شحنة مرخص بها مدعمة تصل إلى 1080 رطلا L1أو 480p على L3. **شغل خارج الشبكة** (ب) يفتح قائمة اللعب المحلية المكتملة مع القائمة التالية/السابقة، والفخ، والتكرار، والتشغيل الآلي؛ **اذهب إلى الإنترنت** يعود إلى القائمة الكاملة. **التجديد** يجدد الرخصة المختارة بينما هي متصله



وتستمر عمليات تنزيل وسائط الإعلام المستفسرة بالفعل في الخلفية، فإذا أوقفت Android التطبيق أثناء إعداد التراخيص أو المسارات، أعيد فتحه بينما كان متصلاً بالانتهاء من إعداد البنود المتبقية، وتُحتفظ بالتحميلات الموجودة، ولا تُستخدم إلا في وسائط الإعلام المكبوتة؛ وتحتاج عمليات التنزيل غير المكتملة أو المنتهية الصلاحية إلى استكمالها أو تجديدها.



Widevine حالات الفشل (الإخفاق في توفير الخدمات)Media3 6002) يستخدم حالياً نقلاً مصدقاً عليه إلى خدمة غوغل الرسمية. L3 CDM يمكن الحصول عليها SD مفاتيح، جهاز لا يعمل Widevine يحتاج التنفيذ إلى إصلاح جهاز/جهاز؛ ولا يمكن للرخصة أن تضيف مفقودة DRM الدعم.



العينة (APK) عامة وموقعة من أجل التقييم (AAR) و(SDK) الكاملان أدناه لا يزالان متاحين من خلال حسابات كونسول المؤهلة أبقِ تطبيقك الإنتاجي الخاص يُوقّعُ مفتاحاً وسجلاً خلفياً منفصلاً عن هذا الرقم.

APK كشوف {و تركيب USB

SHA-256:

```
8fa044b82c0abab7ce2a987f349524fd7ad1e78f3c897771f4764cb2aa521180
```

مع "Android" وجهاز "USB" مرخص له

```
adb install -r drmx-android-player-1.0.0-preview.12.apk
```

ضع هذا APK على العينة الرسمية الحالية دون أن يُبطلها، نفس مفتاح التوقيع والتطبيق ID للحفاظ على الحمولات، والرخص الخارجية، وإنقاذ المواقف والأفضليات، إذا كان بنائك الخاص يستخدم مفتاح توقيع آخر، استخدم تطبيقا منفصلا ID للحفاظ على كلا التطبيقين والحفاظ على رخص العينة خارج الخط.





## What is new in Android المشغّل preview.12[#](#latest-player-update)



(أ) أُطلق سراحه في 14 أيلول/سبتمبر 2026 - أُنزلت الأكسدة التي قدمت طلباً غير متوقع للرخصة عندما تنتهي صلاحية رخصة Widevine أو أن تبقى دقيقة واحدة أو أقل، وقد يبدو ذلك خطأ Media3 `6004`، خطأ HTTP في حين أنه متصل، أو خطأ DNS في حين يُفصل.



The المشغّل يستعيد الآن رخصة التنزيل محلياً، ويحتفظ الرخص الجاهزة بالوقت المتبقي للإسترجاع؛ ويظهر الرخص المنتهية صلاحيتها **Connect and tap Renew**ولا يزال الجهاز يُنفّذ قواعد الرخصة والحماية، وتشمل التشخيصات أيضا العددية HTTP (ب) الحالة التي تفشل فيها عملية الشبكة، دون عرض رؤساء الطلبات، أو طلبات التوريد أو استجابات الترخيص.


- اثبتي ما قبل العرض 12 على التطبيق الرسمي الحالي، واحتفظي ببيانات تطبيقك الحالية وبأعمال التنزيل.
- If WayMaker or another downloaded video needs renewal, connect to the internet, select that video and tap **Renew**.
- انتظروا **License renewed.جاهزون للعب خارج الخط **ثم اختاروا **Play offline **.



هذا الإفراجِ يَحتفظُ بضوابطِ ما قبلِ 11 وملامح قائمةِ اللعبِ. المشغّل تحديد ما قبل النظرة 12؛ وكونسول الموزع بشكل منفصل SDK ما زال العرض الأولي)١٠(.





## What is new in Android المشغّل preview.11[#](#player-preview11)



أُطلق سراحه في 14 أيلول/سبتمبر 2026، ويشمل هذا التطبيق التقييم آخر ما المشغّل من ضوابط وتحسينات في قوائم اللعب، مع اتباع أسلوب تفاعلي متسق عبر أسلوبنا Android وأبل المشغّلات.


- **مراقبون داخل الفيديو: **مُركزون في "بلاي/بوز" و "بريفيوس" و "التالي" في قائمة اللعب المتعددة الأقراص CC، "ستينغز" و زر شاشات كاملة. 
- **Captions and playback settings: ** use CC to show or hide available subtitles, and Settings to choose caption language, audio, playback speed and permitted quality. 
- **Drag to reorder: ** يَجْرعُ a مقبض مِنْ قائمةِ اللعبِ لتَغيير موقعِه الطلبِ المنقّحِ يَبْقى a إعادة تشغيل ويَنْعكسُ في قائمةِ اللعب المُحمّلةِ. 
- **أغلقت الشوفان وكررت: **بعض الأساليب تبقى مسلطة الضوء وتبقى على قيد الحياة في العزف عبر الإنترنت وخارج الإنترنت، مع ثرثرة وتكرار كلتيهما، أشرطة الفيديو تتبع النظام المنقذ وتتوقف بعد البند النهائي، وألغيت إجراءات قائمة اللاعبين &quot; لاحقاً &quot; . 
- **استأنف من حيث توقفت:** يُستعاد موضع VOD المحفوظ لكل فيديو عند العودة إلى البث أو تشغيل التنزيل. يُحفظ التقدّم على هذا الجهاز للمشاهد الحالي، ولا تتم مزامنته بين الأجهزة. 



The المشغّل changes were verified on a Samsung S24 Ultra, including playlist dragging during playback, Save order after relaunch, playback resume and downloaded playback with Wi-Fi and mobile data disabled. ولا يزال النظر عبر الخطوط الجوية يتطلب تحميلاً مكتملاً ورخصة خارجية قابلة للاستعمال وغير مكتملة.



هذا الإصدار يستكمل التقييم العام (APK) أما كونسول SDK الموزع بشكل منفصل، فلا يزال `1.0.0-preview.10`، مخبأ `20260910.8`؛ وتشير تعليماته الحالية المتعلقة بالتكامل أدناه إلى تلك المجموعة.





## 1 - اختيار مسار التكامل[#](#choose-your-path)



[PATH QUICKEST START**DRM-X المشغّل**

Use DrmXPlayer, the phone sample and the offline manager. Keep your own login and business rules.

](#android)[PATHB KEEP YOUR UI**Media3 + DRM-X مساعدة**

احتفظ بـ المشغّل و كومة من التحميل دع (دريمكس أوفلين ماناجر) يدير رخصاً ثابتة

](#existing-media3)[PATHC NATIVE ONLY**Media3 بدون AAR**

تنفيذ نفس العقد الداعم ورخصة الرخصة الأصلية على قيد الحياة لا DRM-X المشغّل التبعية

](#configure-media3) 

[نزّل ملف ZIP أو AAR الخاص بـ Android SDK من Console → Integrations](https://6.drm-x.com/console/integrations). يمكن للمؤسسات ذات اشتراك **Professional النشط ($499/شهر) أو Enterprise** التنزيل. لا يمكن لحسابات Trial وStandard تنزيل SDK. هذه الوثائق، بما فيها أمثلة Media3 الأصلية، عامة؛ لكن التشغيل يتطلب حسابًا مخوّلًا ومحتوى منشورًا وجهازًا متوافقًا.



Current preview: `1.0.0-preview.10` · customer bundle `20260910.8` · 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 - إعداد محتواك ودعمك[#](#before-you-code)


- حزمة ونشر Widevine- مشفرة متوافقة DASH اللقب في DRM-X البيئة، نسخها بدقة **منشورة Content ID**.
- إئتمن خادمك Environment ID, Site Key و Access Key- الاستخدام [قابل للتنزيل PHP التكامل](https://developer.drm-x.com/php-sample/) لنقطة انطلاق
- تنفيذ سجل دخول المستخدمين وفحص الاستحقاق الخاص بك، تطبيقك يستدعي ظهرك مع مستعمله القصير العمر؛ وخلفيتك تختار المحتوى، والسياسات، والأوراق المالية.
- تشغيل تشغيل على شبكة الإنترنت أولا على جهاز Widevine يمكن تشغيله، أضف الاقتناء خارج الشبكة فقط بعد أن يعمل عقد التدفق.



يُرسَل الفيديو المشفّر وتراخيص DRM عبر مسارين منفصلين

توزّع CDN الفيديو المشفّر. يتحقق خادم التطبيق من صلاحية الوصول، وتسمح خدمة تراخيص DRM بفك التشفير على جهاز متوافق.

- 01**التخزين / CDN**

يوزّع الوسائط المشفّرة إلى المشغّل.

- 02**خادم التطبيق لديك**

يتحقق من حق المشاهد في الوصول ويطلب تصريح التشغيل من DRM-X.

- 03**DRM-X**

تتحقق من تصريح التشغيل وتعالج طلب ترخيص DRM.

- 04**المشغّل + الجهاز**

يستخدم نظام DRM في الجهاز للحصول على ترخيص وتشغيل الوسائط المشفّرة.

توصيل وسائل الإعلام وتسليم الرخصة منفصلان فقط العملاء المساندين يحملون DRM-X خادم بيانات الاعتماد 

| تغيير هذه القيمة | حيث تنتمي | معناه | 
| --- | --- | --- |

| 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[) ](#install-sdk)



افتح المجلد الجذر لملف ZIP بعد فكّه في Android Studio. استخدم JDK 17 وAndroid SDK 36 وAndroid Gradle Plugin 9.2.0؛ يستخدم الغلاف المرفق Gradle 9.4.1. الحد الأدنى للجهاز هو API 23. يتطلب تنزيل تبعيات البناء اتصالًا بالإنترنت. اختر `samples.phone` ثم Run. تستخدم القيم الأولية خادم العرض العام وعنوان الفيديو التجريبي؛ جرّب تسجيل الدخول والبث قبل الانتقال إلى خادمك الخاص.



```
# 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-preview.10")
}
```

Installing the standalone AAR instead

ضع AAR في وحدة تطبيقك `libs/` شقة AAR ليس لديها قرار تبعية عابرة آلياً لذا أضف الثلاثة المطابقة Media3

```
implementation(files("libs/drmx-universal-player-1.0.0-preview.10.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`) ، اسمحي بالدخول عبر الإنترنت و الدعم المزيل لهذه العينة الدنيا In a production app that needs supportives, specifically exclude tool-bound offline license metadata instead.



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



فتش عينة الهاتف `MainActivity.kt` for `drmx.sessionEndpoint`, `drmx.offlineEndpoint`, `drmx.contentId` و `customer.loginEndpoint`- غيّر بيئات الإنهيار المميزة، أو تمرير هذه الإضافات البارزة من طلبك بعد الولوج `customer.accessToken` في وقت الهروب ZIP README يشرح كل بارامتر و التي تخفض الخطافات لتحل محلها



## Build a test APK using the AAR[#](#build-apk-from-aar)



هذا المثال يستخدم مشروع الهاتف القابل للطي من (SDK ZIP) بالكامل ويربط (AAR) نفس النهج يعمل من أجل تطبيق (Android) الحالي


- Download and extract the [full Android SDK ZIP ](https://6.drm-x.com/console/integrations). Open its root folder in Android Studio.
- Keep `libs/drmx-universal-player-1.0.0-preview.10.aar` in the extracted root. Use JDK 17, Android SDK 36 and the included Gradle wrapper.
- في (`samples/phone/build.gradle.kts`)، ابدلي تبعية (DRM-X مافين) مع القيود التالية داخل (`dependencies`) احتفظ بباقي ملف البناء



```
implementation(files(rootProject.file("libs/drmx-universal-player-1.0.0-preview.10.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-preview.10")`) حتى لا يكون (SDK) نفسه مشمولاً مرتين (AAR) مسطحة تحتاج إلى (Media3) المُعاليات المُبيّنة أعلاه.


- `applicationId`إلى `com.example.drmxtest`اتركوا مكان إسمها بدون تغيير لهذه العينة المزودة
- Build the APK, then install it on a USB-connected Android tool with USB debugging authorized.



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



افتحي تطبيق الاختبار ولعبة النقر، يستخدم مصدر الهاتف المزود نفس الهدم العام في البداية، لاختبار عنوانك الخاص، والاستعاضة عن نقطة النهاية المميزة/الخط/اللوحة Content ID في `MainActivity.kt`و يزود مستعملك الخاص بتوثيق قصير الأجل في وقت التشغيل خادمك يحتفظ Site Key و Access Key.



(الضوابط المدمجة تأتي من (`DrmXPlayerView`) في (AAR ويبين النشاط المورّد التناوب بين اللاعبين والتخزين، والتناوب المستجيب، وملكية التحميل، والتكرار والتجديد عن طريق الإنترنت. انظر ([يث) مثال التكامل تحت المراقبة (](#player-controls)) عندما يضيف هذا المنظر إلى تطبيقك الخاص





## 4 - طلب الموظفين وسياسة التقصير[#](#server-request)



The Android طلب دعمك لجلسة إسترجاع مسرحية ظهرك يصادق على المستعمل ويتحقق من الاستحقاق والخرائط Content ID to published media and calls DRM-Xانظر [جلسة العزف API](https://docs.drm-x.com/ar/api-reference/playback-sessions) من أجل عقد الإرسال الكامل.



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



The PHP عيّنات التخلف عن الدفع `multi-tier-standard` إختار سياسته الموثقة ذات المفتاح الوحيد عندما تستخدم حزمتك مفتاحاً واحداً، سياستك يجب أن تتطابق مع النموذج الرئيسي و حقوق المشاهد `DRMX_LICENSE_POLICY_TEMPLATE` البيئة، وليس بارامتر استفسارات في المشغّلانظر [سياسات الترخيص](https://docs.drm-x.com/ar/license-service/policies).



**فالخط الخارجي يحتاج إلى سياسته الثابتة الخاصة به.**المجموعة `playback_policy.persistent` ويصدق على السياسة التي تصدرها الخواديم واختيار الرخصة، والتأجير، والعبوات، ومدة التجديد، ويستخدم المرجع 7 أيام/ثلاثة أيام/48 ساعة/ 7 أيام على التوالي؛ وهذه هي على سبيل المثال قواعد العمل، لا الموافقة على الاستوديو، أو التقصير على نطاق المنهاج، كما أن الرخصة Widevine لا تزال ذات حجية.



## 5. Path A: play through DrmXPlayer[#](#android)



Inside your Activity, create a `PlayerView`, use a secure window and call `prepare` after login. The wrapper exchanges the session, configures Widevine, applies the returned maximum height and releases the streaming reservation when stopped. Imports come from `com.haihaisoft.drmx.player` and 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.



## مراقبة مشغلات الفيديو والتناوب غير المتقطع[#](#player-controls)



Tap **Play ** على شريط الفيديو للبدء بالإذن، فتح الشاشة أو المثبتات لا يكتسب رخصة تيار، والوقف/الاستعراض، والإطارات المتغيرة، وتناوب الهاتف يحفظ الدورة الحالية.



![DRM-X Android المشغّل Settings on a Samsung S24 Ultra, showing Quality, Audio track, Subtitles/CC, Playback speed and sleep timer.](https://docs.drm-x.com/assets/android-player-settings-preview4.png)(أ) وضعيات فعلية على موقع سامسونغ S24 Ultra. Tap a row to see its available options; the audio submenu shows full language names. Available quality and languages depend on the content, license and tool. 

| الرقابة | ما يمكن للزبائن القيام به | 
| --- | --- |

| النوعية | اختيار أوتوماتيكي أو قرار مرخص له مدعوم من البيان، والخيارات غير المتاحة تفسر الترخيص أو الجهاز/DRM القيد - تطيع الآلية نفس القيود - | 

| Audio track | اختر أي لغة صوتية مدعومة تزود بها المجموعة. | 

| العنوان الفرعي/CC | اختر لغة الاختباء أو خارجاً الزر CC يظهر عند وجود أغطية مدعمة ويضربها مباشرة | 

| مثول للقبض | مُجرّد لون وحجم نُصّة، ألوان خلفية/ألوان ونواة، وظلمة، تُوفّر الأفضليات على الجهاز | 

| سرعة النعاس/وقت النوم | نلعب بسرعة 0.25 - 2 أو نتوقف تلقائياً بعد فترة مختاره | 

| كشك/شاشة كاملة | اكسر الخط الزمني، وابحث عن 10 ثواني، أو قفزة مزدوجة من كلا الجانبين، اتجه إلى المشهد دون إعادة تشغيل الملعب. | 


The three compact actions below the picture are **Download** (for offline use), **Play offline** and **Renew** (the offline license). The sample downloads one permitted video rendition and the supported audio/subtitle tracks; offline menus expose only downloaded media. Renewal requires connectivity.



**سجل النسخ** إلى جانب نسخ المعلومات المأخوذة من الأخطاء الأخيرة كنص، حتى تتمكن من إرسال الدعم للتفاصيل بدون تصوّر، فالتقارير تحتوي على تقرير ID،عملية SDK/Android/النسخة النهائية، العددية CDM/Media3 :: الأخطاء، والأطر المتحركة المختارة، والربط/التحميل/الاسترجاع، والعد المكرر. بيانات الاعتمادURLs Media URLs, key, key-set IDs, license data and full CDM قمامة.



تحتفظ العينة بما يصل إلى 32 تقريرا محليا (64 تقريرا) KB(د) لمدة خمس ساعات، عندما يتم ربطها وتوقيعها، فإنها تُرفع إلى الزبون المهيّل، مرة واحدة في الـ 30 ثانية على الأكثر، ويحتفظ الخادم العام بالتحذيرات/الطوارئ لمدة خمس ساعات، ويجمع بين الأحداث المتكررة، ويرفع الملف إلى 512 KB التقدم الروتيني والعزف الناجح لا يتم تسجيله، ويجب أن توفر الاندماجات العرفية لنفسها بشكل صريح. `drmx.diagnosticsEndpoint`الأخطاء التشخيصية لا تقاطع العزف أبداً



**-رخصة مستعملة بالفعل؟**Preview.5 fixes the S24 case that reported CDM 368 with “License loaded in different session.” Pressing Play offline again resumes the existing المشغّل. Renew first releases its DRM session, then reneweds the stored license. The download and persistent license are preserved; no app-data reset is required. 

```
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. Acquire a license and download the media[#](#offline-download)



جهّز التشغيل دون اتصال

يلزم اكتمال التنزيل ووجود ترخيص صالح مرتبط بالجهاز معًا.

- 01**نزّل الوسائط المشفّرة**

أثناء الاتصال بالإنترنت، نزّل الفيديو ومسارات الصوت والترجمة التي تحتاجها.

- 02**احصل على ترخيص دائم التخزين**

أثناء الاتصال بالإنترنت، احصل على ترخيص Widevine المرتبط بالجهاز. خزّن معرّفه المبهم Key-set ID بصورة خاصة.

- 03**تحقّق من الجاهزية**

تأكّد من اكتمال التنزيل ومن صلاحية الترخيص.

- 04**شغّل دون اتصال**

اقرأ الوسائط المخزّنة مؤقتًا واستعد الترخيص المحلي. يتطلب التجديد وتحرير الترخيص اتصالًا بالإنترنت.

مجهزة خارج الشبكة = وسائل إعلام مشفرة كاملة + رخصة صالحة ذات جهاز محدد ولا أحد يحل محل الآخر. 

```
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 ينقذ المظلة Widevine مجموعة المفاتيح ID البيانات الوصفية غير السرية CDM يمسك الرخصة، لا تستمر DRM License Tokens(د) مفاتيح خام أو تحديات أو رخص استجابة من جانب الأطراف. `com.haihaisoft.drmx.offline.licenses.v1.xml` من الدعم.



إنشاء وظيفة واحدة مملوكة للتطبيق Media3 `SimpleCache`مدير الحمولة و قاعدة البيانات `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 متطلبات الخدمة/الإخطاء الأرضي.



**المحتوى غير المباشر من الرفات المتعددة:**Media3 يعيد مجموعة مفاتيح واحدة ID - يجب أن تتضمن رخصتها المستمرة كل مفتاح مطلوب لأجهزة الفيديو والصوت وغيرها من المسارات المشفرة المختارة، وفحصها بمجموعتك الحقيقية وبسياسة الخادم؛ وإتاحة دورات متعددة لا تجمع بين تراخيص مستقلة من الخارج.



## (7) المسار باء: استعادة (Media3 المشغّل [) (](#existing-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.
```



(لإستعمال نفس التدفق من الخارج مع (باث ألف (بناء (`DrmXPlayer`) مع (`mediaSourceFactory`) و الاتصال بـ(`player.prepareOffline(playback)` الاستعادة العادية تستخدم رخصة (CDM) المحلية بدون طلب واحدة جديدة يجب أن يُحدث دخول مخبأ مفقود خطأ أثناء القبول المفصّل، لا أن يتحول صامتً إلى الإنترنت.



## 8. Path C: native Media3, no DRM-X AAR[#](#configure-media3)



You are not required to use DrmXPlayer or DrmXOfflineManager. Add the three Media3 dependencies from step 3 and implement the same customer backend contract. Use `session.manifestUrl` (not `session.manifest.url`), the returned license URL and bounded authorization headers. Apply the returned maximum height and handle streaming session release on stop, replacement, end, failure and destruction.



The complete native Activity below is compiled as `samples:native` along this SDK release. It handles HTTPS, bounded response reads, background authorization, stale responses and reservation release and replace the marked endpoint/Content ID and supply your own login token. The example is a starting point; add your production refresh, account-switch and app-navigation life cycle.

اكتمال النشاط السكاني (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` واحفظ معرّف مجموعة المفاتيح غير القابل للتفسير في مساحة خاصة، ثم استعده باستخدام `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(أ) فترة صلاحية التشغيل والتشغيل والمستعملين/الزيارة والإذن؛ IDالمُساعد المُدير يُنادي على خيط العامل. HTTP استخدام المصابيح `setLicenseRequestHeaders` on the DRM (أ) التشكيل؛ Widevine والبارامترات الاختيارية لطلب المفتاح هي خريطة مختلفة.



ينبغي أن تستخدم عمليات التكامل الجديدة [AndroidX Media3 DRM الدعم](https://developer.android.com/media/media3/exoplayer/drm)- الإرث `com.google.android.exoplayer2` ينبغي أن تتبع المشاريع [دليل الهجرة الرسمية](https://developer.android.com/media/media3/exoplayer/migration-guide); do not mix legacy and AndroidX classes or assume the sample collects changed against older versions.



## 9 - لغات التحقق والتجديد والإفراج والتبديل[#](#lifecycle-and-offline-behavior)



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`يقرأ البيانات الفوقية؛ `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 - تنفيذ نقطة نهاية الإذن خارج الإنترنت[#](#offline-backend-contract)



التطبيق ينشر (`contentId`) و(`contentType: "vod"`) و(`drmSystem: "widevine"`) و(`playbackMode: "offline"`) و(`operation: "acquire" | "renew" | "release"`) و(`offlineLicenseId`) (مطلياً للإقتناء الأولي) و(`platformCapabilities`) الحقيقية التحقق من أن بطاقات الهوية المتجددة/الثابتة تعود إلى المستخدم الموثق والجهاز والمحتوى. اختيار خادم السياسات، إصدار 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 الدعم أيضا `integrationMode: "token-proxy"` مُلزَم `drm.authorization` أيها المجند، إرجع `Cache-Control: no-store`; لا تُسجل أبداً أجساد الأذون الناجحة SDK' `OFFLINE_INTEGRATION.md` و PHP العينة خارج نقطة النهاية لمناولة الطلبات الملموسة



## 11 - اختبار أجهزة الخاص بك والتشويش[#](#device-qualification)



### Widevine توفير واختبار شبكة الصين



ويحصل التقدير على شهادة الجهاز قبل الحصول على ترخيص المحتوى. CDMطلب موقّع من خلال زبونه الموثّق HTTPS هذا يساعد عندما لا يستطيع الهاتف الوصول إلى خدمة جوجل مباشرة



ويستخدم توفير ٢,٠ صندوق مفاتيح للمصنع، ويستخدم توفير ٤,٠ نموذجا مختلفا لحرق الأجهزة، وهذه النسخ منفصلة عن الصيغ. L1/L3 مستويات الأمن، المشغّل لا يبدلها، لقد تم بالفعل توفيرها L3 ويمكن للهاتف أن يلعب في حين أن هاتفا آخر ينتظر توفير الأجهزة، فالمصادر الرسمية العامة التي تم استعراضها هنا لا تثبت أن الصين لا تؤيد سوى 2.0. [NXPدليل تكامل الأجهزة](https://www.nxp.com/docs/en/user-guide/UG10158.pdf) و [Android"التنفيذ"](https://android.googlesource.com/platform/packages/apps/RemoteProvisioner/+/refs/tags/android-13.0.0_r15/src/com/android/remoteprovisioner/WidevineProvisioner.java).



تقارير الأخطاء السابقة 10 تميز بين النقل المباشر أو النقل البري، والتوقيت المتوقع/DNS/TLS/HTTP الفشل HTTP الوضع والوقت المُنقضي `provisioningModel: BootCertificateChain` يشير إلى AOSP 4-0 نموذج؛ `unknown` أو `other` لا يحدد 2.0. PHP وتميز السجلات بين المدخلات المرفوضة من حالات الفشل في نظام Google، وتستبعد التقارير الحمولات الموقّعة، والسجلات الموحّدة المصدر، بيانات الاعتماد وأجهزة تحديد هوية فريدة من نوعها



تحديث الـ APK في مكانها، وتدفق البقايا، وتنزيل واحد على الشبكة المتضررة، ثم الاحتفاظ بسجل النسخ والوقت التقريبي، والاحتفاظ بالشحنات والترخيصات الموجودة، ولا يمكن للنفقة إصلاح Widevine غير متاح، أو بيانات الاعتماد الذي رفضه الجهاز/غوغل، ولا بد من القبول الحقيقي على الهاتف المتأثر.



مِنْ ما قبل المقابلةِ 8، مواطن L2/L3 أو على مستوى غير معروف Widevine النبض يقتصر على SD (حتى 576ب) قبل التزيين Android القدرات من خلال دعمكم: الدورة API برمجيات ضيقة Widevine تراخيص متعددة المفاتيح للصوت/SD المفاتيح PHP وتحافظ العينات على هذه الحقول، كما تحجب الطلبات غير المباشرة. SD التسليم والسياسة العامة التي تسمح بالبرمجيات DRM ولا بد من ذلك؛ ولا تزال القواعد الأمنية الوحيدة الروتينية والوحيدة دون تغيير.() وتستعمل &quot; ستينغز &quot; لوحة مترجمة دون إغراق الفيديو. `cryptoErrorCode` تدرج في تقارير الأخطاء الآمنة.



| الاختبار/الأعراض | السلوك المتوقع/الفحص التالي | 
| --- | --- |

| SDK عودة الحمولة 403 | (ب) اختيار منظمة لها اشتراك نشط في الفئة الفنية أو في المشاريع - لا يفتح التحسين المقرر بعد التجربة SDK. | 

| الجلسة 401 / 403 | تحقّق من الدخول، الاستحقاق، البيئة، النشرة Content ID والسياسة العامة، لا تلغي التوثيق أبداً. | 

| الفشل غير المباشر لكن العمل على الإنترنت | تحقق من CDMمدة، تحميل كامل، موقع كيتش، مفاتيح تيار وجميع المفاتيح المرخصة المطلوبة. | 

| الإنجليزية/ الصينية المفقودة | التحقّق من البيانات الفوقية للغة، المسارات الفعلية واختيار اللغات خارج الإنترنت. | 

| L1، HDCP أو إخفاق ديكور | (ج) التحقق من الاحتياجات من المعدات المادية، والتزود، والرمز (ج) والنواتج - لا توجد قيود على القدرات. | 

| إعادة التشغيل، التناوب، الترقية | ويبقى استمرار الرخصة والكاتش على قيد الحياة مدعمين بتغييرات دورة الحياة دون تكرار الاحتياز، ويجب ألا تهاجر المساندة رخصاً لجهاز جديد. | 

| وضعية الطائرات | Disable Wi-Fi and mobile data too; restart the app, play locally and exchange every promised language. | 

| فروة، ضغط للتخزين، مفتاح الحساب | أظهروا حالة واضحة غير متوافرة/جديدة، واستعادة وسائل الإعلام غير المكتملة، وإنفاذ وصول المستخدمين/الديف من دون تسرب كتالوج مستعمل آخر. | 


تأهّل كلّ هاتف مُدعم، مُقرّب Android TV/Google TV, Fire TV أو مشتملة Android نموذج Widevineتمزيق مأمون، شفرة (ج) HDCPعرض خارجي، تغير الساعات، وفصل طويل ليس كل شيء Android الجهاز نفسه DRM إحتواء (هوليوود) يتطلب حماية الحائز على الحقوق ومتطلبات الأجهزة، ليس فقط التزيين الناجح.



**لا توجد فرصة للاحتياز قبل الانفصال؟**وهذا نشر مختلف: إصدار تراخيص مؤهلة على متن الشبكة المحلية خلال الرحلة، ولا يمكن ذلك إلا باستخدام SDK. DRM-X يمكن أن يعمل معك على نشر تقليدي يشمل إذن مقدمي الخدمات، وتوفير الحماية الرئيسية، واختبار القبول المتقطع تماما. [على متن السفينة DRM للطائرات والحافلات والقطارات والسفن](https://multi-drm.drm-x.com/ar/solutions/on-board-drm).



## Universal Player قائمة اللعب والضوابط المشتركة [# ](#universal-player-playlists-and-shared-controls)



The Android preview.11 and later evaluation apps and current Android/Apple development sources share in-video Previous/Play-Pause/Next, CC, Settings and fullscreen controls with two-second hiding during playback. Android's playlist menu no longer includes “Play next”. Selected shuffle/repeat settings remain highlighted and persist across restarts. With both off, playback follows the saved playlist order and stops at the final video. Drag handles reorder items without restarting the current video; the saved order also applies to the downloaded playlist. These latest source APIs are not yet included in the Console's preview.10 SDK bundle.



زود نفس الزبون/المقابلة (`storageNamespace`) لـ(`DrmXPlayer`) و(`playerView.storageNamespace`) قبل وضع قائمة اللعب تم إنقاذ تقدم (VOD) دورياً وعلى التوقف/التوقف، ثم استعيدت لـ(Content ID) في التدفق أو التنزيل. العزف الحي لا يستخدم موقع (VOD) المنقذ. وتحتفظ عمليات التنزيل المنجزة بنفس سلوك الشوفان/إعادة التكرار/العطر دون وجود مصدر لشبكة وسائط الإعلام. ولا تتزامن هذه الدولة مع التقدم بين الأجهزة.



تستخدم الطبعات الإلكترونية و Android فريقاً مظلماً متسقاً مع ضوابط زرقاء مدمجة، وتدعم اللاعبون اللاعبون مقدماً أوتوماتيكياً/مسبقاً، ويكررون واحداً/جميعاً ويلغون تقدماً تلقائياً، ويستخدم كل بند طلباً جديداً مأذوناً به، ويأتي الفصل والبيانات الوصفية من قائمة محتوياتكم.



ويمكن الاطلاع على سرعـة الـبـيـتـبـيـت، وتوقيت النـوم، والاختيـارات، والجودة المسموح بها، واللغات السمعية، والعلامات الكتابية للدورات، والوسـم ألف - باء، وتضيف الشبكة العالمية مسارات اختصار لوحة المفاتيح، واستئناف المشاهدين المحليين الاختياريين، والبحث عن النصوص، ودعم برنامج &quot; بروزر &quot; . Android يُستبقون السكان الأصليين Widevine عمليات التنزيل والتجديد من الخارج - يتطلب إحراز تقدم في مجال الاختراع والتقدُّم عبر التصاميم تكاملاً منفصلاً في التطبيقات.



### الشعارات الفنية والتجهيزية



براندينغ يبقى بعيداً عن سطح الفيديو، ويفتح الباب عن المشغّل للشعار المرخص به، ومعلومات المنتج ونسخة المشغّل، ويعرض الشبكة أيضاً هذه المعلومات على الموقع الصحيح، ويشتمل الرقم القياسي للهاتف العام على واي ماكر، وخيرة الله، وزيت فريش، وVresh Test Video؛ ويتلقى كل انتقاء إذنه الخاص.



ضعي (`playerBranding`) في مساندتك الموثوقة لـ(`{"mode":"hidden"}`) أو (`{"mode":"custom","logoUrl":"https://your-site.example/logo.png","label":"Your brand"}`) ويتحقق المنبر من الاشتراك الناشط من الفئة الفنية أو المؤسسة. وتستخدم خطط أخرى `{"mode":"default"}`؛ وتطلب إعادة 403 غير مأذون لها بالعرف/الضمان. هذا منفصل عن العلامات المائية الأمنية.



The PHP download includes editable playlist and branding formation. The Android client ZIP includes the playlist callback example. Keep playlist authorization in your backend; never embed API client بيانات الاعتماد in either المشغّل.

<!-- Canonical: https://docs.drm-x.com/ar/sdk/android -->
<!-- Language: ar -->
