DRM-X 6.0 حمام وشعار فرع الزيتونDRM-X 6.0الوثائق
◎ العربية

Android SDK وMedia3 الأصلي

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

عرض Markdownأمثلة قابلة للتجربةآخر تحديث 2026-10-26

لتخصيص الواجهة ومعرفة القيم الافتراضية للمعلمات وأمثلة دورة الحياة، اقرأ PLAYER-UI-CONFIGURATION.md. لوحات المشغّل المنبثقة الافتراضية معتمة؛ ويُضبط تعتيم الترجمة والعلامات المائية بشكل منفصل.

جرّب عرض PHP باتباع DEMO-QUICKSTART.md داخل ملف SDK ZIP. لاستخدام محتواك، أكمل تكامل موقعك والخادم الخلفي أولاً، ثم اتبع MOBILE-AND-TV-APPS.md لربط تطبيقك بنظام تسجيل الدخول نفسه وقواعد صلاحية المشاهدة نفسها.

نزّل Android SDK والتطبيقات النموذجية من Console → Developer Tools → Device SDKs باستخدام اشتراك Professional أو Enterprise نشط.
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.

جرب Android المشغّل#

تنزيل، تثبيت، اضغط Play. يستخدم تطبيق التقييم المجاني الفيديو التجريبي العام الخاص بنا ويقوم بتسجيل الدخول تلقائيًا. لا تحتاج إلى إنشاء APK أو إدخال DRM-X بيانات الاعتماد لتجربتها.

Donload Androidعينة APK

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

  1. افتح هذه الصفحة على هاتفك Android وتحمّل APK
  2. افتح الملف المُحمّل Android أطلب، السماح بالتركيب من المصفح أو مدير الملف الذي استخدمته، ثم صنبور Install.
  3. مفتوح DRM-X 6.0 مشغّل الفيديوابقوا متصلين و اسجلوا الفيديو اللعب الزر، التطبيق يطلب رخصة تيار عندما تضغط على اللعب
  4. يفتح Settings لاختيار الجودة، الصوت، الأصفاد أو سرعة العزف
  5. افتح قائمة الخطوط الثلاثة للفيديو واختر Download. فعّل Downloaded videos لاستخدام Offline Mode، وأوقفه لاستخدام Online Mode. في Offline Mode، اختر Delete من قائمة الفيديو نفسه ثم أكّد. يتطلب تجديد الترخيص اتصالًا وتفويضًا ساريًا؛ وعند حدوث خطأ دون اتصال يمكنك البقاء دون اتصال أو الانتقال إلى الوضع المتصل.

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

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

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

SHA-256:

b96ba527ddf9abae5bff2f8b85a39a12b59c934aaacde1961c8096aa32524c54

مع "Android" وجهاز "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#

تم إصداره في 17 سبتمبر 2026. يتضمن تطبيق الهاتف والعميل SDK اختبار 4K Live DRM. يستخدم التشغيل المباشر بيانات جديدة ولا يمكن تنزيلها. الحدث الاختباري متاح فقط أثناء البث النشط. اتبع دليل الإعداد AWS وOBS لإنشاء الحدث الخاص بك.

What is new in Android المشغّل preview.12#

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

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

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

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.

What is new in Android المشغّل preview.11#

أُطلق سراحه في 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 إعادة تشغيل ويَنْعكسُ في قائمةِ اللعب المُحمّلةِ.
  • أغلقت الشوفان وكررت: بعض الأساليب تبقى مسلطة الضوء وتبقى على قيد الحياة في العزف عبر الإنترنت وخارج الإنترنت، مع ثرثرة وتكرار كلتيهما، أشرطة الفيديو تتبع النظام المنقذ وتتوقف بعد البند النهائي، وألغيت إجراءات قائمة اللاعبين " لاحقاً " .
  • استأنف من حيث توقفت: يُستعاد موضع VOD المحفوظ لكل فيديو عند العودة إلى البث أو تشغيل التنزيل. يُحفظ التقدّم على هذا الجهاز للمشاهد الحالي، ولا تتم مزامنته بين الأجهزة.

نظّم قوائم التشغيل واحفظ ترتيبها واستأنف التشغيل. تتطلب المشاهدة دون اتصال تنزيلًا مكتملًا وترخيصًا صالحًا للاستخدام وغير منتهي الصلاحية دون اتصال.

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 - اختيار مسار التكامل#

نزّل من Developer Tools → Device SDKs. يتطلب استخدام SDK صلاحية Professional أو Enterprise فعّالة، بما فيها تقييمات Professional المعتمدة. يحتفظ Standard بواجهات APIs الأصلية لبناء مشغلك الخاص. تُفحص صلاحية SDK عند إنشاء الجلسة وتجديد الترخيص؛ ولا تتجاوز صلاحية التراخيص غير المتصلة فترة خدمة الحساب.

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 - إعداد محتواك ودعمك#

  1. حزمة ونشر Widevine- مشفرة متوافقة DASH اللقب في DRM-X البيئة، نسخها بدقة منشورة Content ID.
  2. إئتمن خادمك Environment ID, Site Key و Access Key- الاستخدام قابل للتنزيل PHP التكامل لنقطة انطلاق
  3. تنفيذ سجل دخول المستخدمين وفحص الاستحقاق الخاص بك، تطبيقك يستدعي ظهرك مع مستعمله القصير العمر؛ وخلفيتك تختار المحتوى، والسياسات، والأوراق المالية.
  4. تشغيل تشغيل على شبكة الإنترنت أولا على جهاز Widevine يمكن تشغيله، أضف الاقتناء خارج الشبكة فقط بعد أن يعمل عقد التدفق.

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

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

  1. التخزين / CDN

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

  2. خادم التطبيق لديك

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

  3. DRM-X

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

  4. المشغّل + الجهاز

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

توصيل وسائل الإعلام وتسليم الرخصة منفصلان فقط العملاء المساندين يحملون DRM-X خادم بيانات الاعتماد
تغيير هذه القيمةحيث تنتميمعناه
sessionEndpointAndroidنقطة نهاية الدورة الخاصة بك HTTPS وليس خادم الرخصة الخام URL
offlineEndpointAndroidالحصول/التجديد/الترخيص نهاية نقطة.
contentIdAndroid طلب؛ مصادقة مدعمةالمشاهدين الذين نشروا حماية (Content ID) يحق لهم الوصول
وصول العملاء إلىالذاكرة التطبيقية بعد الشعارمستعملك ذو العمر القصير، لا يوجد Site Key أو Access Key.
Environment ID، Site Key، Access Keyتشكيلة سرية فقطDRM-X حساب بيانات الاعتماد لا تشحنها أبداً في APK، HTML
السياسة العامة والبيان الموثوق به URLخادم التطبيق لديكاستحقاق الأعمال التجارية ورسم الخرائط لا تقبلها من التطبيق.

(3) جهزوا و أديروا (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-beta.14")
}
Installing the standalone AAR instead

ضع AAR في وحدة تطبيقك libs/ شقة AAR ليس لديها قرار تبعية عابرة آلياً لذا أضف الثلاثة المطابقة 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) ، اسمحي بالدخول عبر الإنترنت و الدعم المزيل لهذه العينة الدنيا 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#

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

  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. في (samples/phone/build.gradle.kts)، ابدلي تبعية (DRM-X مافين) مع القيود التالية داخل (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) نفسه مشمولاً مرتين (AAR) مسطحة تحتاج إلى (Media3) المُعاليات المُبيّنة أعلاه.

  1. applicationIdإلى com.example.drmxtestاتركوا مكان إسمها بدون تغيير لهذه العينة المزودة
  2. 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 ويبين النشاط المورّد التناوب بين اللاعبين والتخزين، والتناوب المستجيب، وملكية التحميل، والتكرار والتجديد عن طريق الإنترنت. انظر (يث) مثال التكامل تحت المراقبة () عندما يضيف هذا المنظر إلى تطبيقك الخاص

4 - طلب الموظفين وسياسة التقصير#

The Android طلب دعمك لجلسة إسترجاع مسرحية ظهرك يصادق على المستعمل ويتحقق من الاستحقاق والخرائط Content ID to published media and calls 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

The PHP عيّنات التخلف عن الدفع multi-tier-standard إختار سياسته الموثقة ذات المفتاح الوحيد عندما تستخدم حزمتك مفتاحاً واحداً، سياستك يجب أن تتطابق مع النموذج الرئيسي و حقوق المشاهد DRMX_LICENSE_POLICY_TEMPLATE البيئة، وليس بارامتر استفسارات في المشغّلانظر سياسات الترخيص.

يحتاج عدم الاتصال بالإنترنت إلى سياسته المستمرة الخاصة. اضبط playback_policy.persistent على true في السياسة الصادرة عن الخادم واختر فترات الترخيص والتأجير والتشغيل والتجديد. يستخدم المرجع 7 أيام / 3 أيام / 48 ساعة / 7 أيام على التوالي؛ هذه أمثلة لقواعد العمل، وليست موافقة الاستوديو أو الإعداد الافتراضي على مستوى النظام الأساسي. إرجاع البيانات الوصفية المطابقة. يظل ترخيص Widevine موثوقًا.

5. Path A: play through DrmXPlayer#

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.

مراقبة مشغلات الفيديو والتناوب غير المتقطع#

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

لقطة شاشة حالية لإعدادات Samsung Android. تحجب حماية لقطات الشاشة الواجهة الرئيسية للمشغّل.
لقطة شاشة حالية لإعدادات Samsung Android. تحجب حماية لقطات الشاشة الواجهة الرئيسية للمشغّل.
الرقابةما يمكن للزبائن القيام به
النوعيةاختيار أوتوماتيكي أو قرار مرخص له مدعوم من البيان، والخيارات غير المتاحة تفسر الترخيص أو الجهاز/DRM القيد - تطيع الآلية نفس القيود -
Audio trackاختر أي لغة صوتية مدعومة تزود بها المجموعة.
العنوان الفرعي/CCاختر لغة الاختباء أو خارجاً الزر CC يظهر عند وجود أغطية مدعمة ويضربها مباشرة
مثول للقبضمُجرّد لون وحجم نُصّة، ألوان خلفية/ألوان ونواة، وظلمة، تُوفّر الأفضليات على الجهاز
سرعة النعاس/وقت النومنلعب بسرعة 0.25 - 2 أو نتوقف تلقائياً بعد فترة مختاره
كشك/شاشة كاملةاكسر الخط الزمني، وابحث عن 10 ثواني، أو قفزة مزدوجة من كلا الجانبين، اتجه إلى المشهد دون إعادة تشغيل الملعب.

تحتوي قائمة كل فيديو على Move up / Move down، وعلى Download عند الاتصال أو Delete دون اتصال. يُعطّل Download بعد اكتمال التنزيل. يختار مفتاح Downloaded videos التشغيل دون اتصال، ولا تتاح إلا المسارات المخزنة مؤقتًا. لا توجد أزرار منفصلة للتنزيل أو الحذف أو تنزيل قائمة التشغيل كاملة.

سجل النسخ إلى جانب نسخ المعلومات المأخوذة من الأخطاء الأخيرة كنص، حتى تتمكن من إرسال الدعم للتفاصيل بدون تصوّر، فالتقارير تحتوي على تقرير 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 حالة جهاز Samsung Android التي أظهرت CDM 368 ورسالة «الترخيص محمّل في جلسة مختلفة». الضغط على Play دون اتصال مجددًا يستأنف المشغّل الحالي. يحرّر التجديد جلسة DRM أولًا ثم يجدّد الترخيص المخزّن. تبقى الملفات المنزّلة والتراخيص الدائمة محفوظة ولا تحتاج إلى إعادة ضبط بيانات التطبيق.
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#

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

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

  1. نزّل الوسائط المشفّرة

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

  2. احصل على ترخيص دائم التخزين

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

  3. تحقّق من الجاهزية

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

  4. شغّل دون اتصال

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

مجهزة خارج الشبكة = وسائل إعلام مشفرة كاملة + رخصة صالحة ذات جهاز محدد ولا أحد يحل محل الآخر.
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 المشغّل ) ()

اقرأ الحمولة المكتملة واستخدم مفاتيحها بدقة، ويجب أن يتقاسم التفريغ والحزمة الخلفية نفس الموقع ورسم الخرائط الرئيسية، وهذا المثال يؤدي عمداً إلى تعطيل الوصول إلى أعلى المجرى من أجل إعادة التشغيل الصارمة خارج الشبكة:

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#

ليس مطلوبًا منك استخدام DrmXPlayer أو DrmXOfflineManager. أضف التبعيات الثلاثة Media3 من الخطوة 3 وقم بتنفيذ نفس العقد الخلفي للعميل. استخدم session.manifestUrl (وليس session.manifest.url)، والترخيص المرتجع URL ورؤوس التفويض قصيرة العمر. قم بتطبيق الحد الأقصى للارتفاع الذي تم إرجاعه والتعامل مع تحرير جلسة البث عند التوقف والاستبدال والنهاية والفشل والتدمير.

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 الدعم- الإرث com.google.android.exoplayer2 ينبغي أن تتبع المشاريع دليل الهجرة الرسمية; do not mix legacy and AndroidX classes or assume the sample collects changed against older versions.

9 - لغات التحقق والتجديد والإفراج والتبديل#

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 - تنفيذ نقطة نهاية الإذن خارج الإنترنت#

التطبيق ينشر (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 - اختبار أجهزة الخاص بك والتشويش#

الاختبار/الأعراضالسلوك المتوقع/الفحص التالي
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 للطائرات والحافلات والقطارات والسفن.

قوائم التشغيل وعناصر التحكم المشتركة في Universal Player#

توفر مشغلات Android وApple عناصر Previous/Play-Pause/Next وCC وSettings وملء الشاشة. يحافظ Move up / Move down على الفيديو الحالي ويحفظ ترتيب القائمة للتشغيل المتصل ودون اتصال. تبقى خيارات الترتيب العشوائي والتكرار بعد إعادة فتح التطبيق؛ وعند إيقافهما يتوقف التشغيل بعد العنصر الأخير.

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

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

تتوفر السرعة ومؤقت النوم والترجمة والجودة المسموحة ولغات الصوت والعلامات وتكرار A–B. تضيف Web اختصارات واستئنافًا محليًا اختياريًا لكل مشاهد وبحث النص وPiP المدعوم. يحتفظ Android بتنزيل Widevine دون اتصال وتجديده. البث إلى شاشة أخرى والتقدم بين الأجهزة يحتاجان دمجًا إضافيًا.

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

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

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

يتضمن تنزيل PHP قائمة وعلامة قابلة للتعديل، ويحتوي ZIP الخاص بـ Android على مثال callback. أبقِ تفويض القائمة في الخلفية ولا تضع بيانات عميل API في أي مشغل.