SDK Android et Media3 natif
Téléchargez le lecteur Android avec des listes réorganisables par glisser-déposer, la mémorisation des réglages de lecture aléatoire et de répétition, et la reprise hors ligne. Intégrez la lecture protégée avec DRM-X ou Media3 natif.
Pour personnaliser l’interface et consulter les valeurs par défaut et les exemples de cycle de vie, lisez PLAYER-UI-CONFIGURATION.md. Les panneaux contextuels du lecteur sont opaques ; l’opacité des sous-titres et des filigranes se règle séparément.
Essayez la démonstration PHP avec DEMO-QUICKSTART.md dans votre SDK ZIP. Pour vos propres contenus, intégrez d’abord votre site et votre backend, puis suivez MOBILE-AND-TV-APPS.md pour connecter votre application au même système de connexion et aux mêmes règles d’accès.
Essayez le Android lecteur#
Télécharger Android échantillon APK
1.0.0-beta.11 · 12.15 MB · Android 6.0+ (API 23+) · DRM-X 6.0 Player
- Ouvrez cette page sur votre téléphone Android et téléchargez le APK.
- Ouvrez le fichier téléchargé. Si Android demande, autorisez l'installation à partir du navigateur ou du gestionnaire de fichiers que vous avez utilisé, puis appuyez sur Install.
- Ouvrez DRM-X 6.0 Player, restez connecté et appuyez sur le bouton Play de la vidéo. L'application demande une licence de diffusion lorsque vous appuyez sur Play.
- Ouvrez Paramètres pour choisir la qualité, l'audio, les légendes ou la vitesse de lecture.
- Ouvrez le menu à trois lignes de la vidéo et choisissez Download. Activez Downloaded videos pour Offline Mode ; désactivez-le pour Online Mode. En Offline Mode, choisissez Delete dans le menu de cette vidéo et confirmez. Le renouvellement de la licence nécessite une connexion et une autorisation valide ; une erreur hors ligne permet de rester hors ligne ou de passer en ligne.
Si Android arrête l'application lors de la préparation des licences ou des pistes, rouvrez-la pendant qu'elle est connectée pour terminer la préparation des éléments restants. Les téléchargements existants sont conservés. La lecture hors ligne n'utilise que les médias mis en cache; les téléchargements incomplets ou expirés nécessitent l'achèvement ou le renouvellement.
L'échantillon APK est public et signé pour l'évaluation. Le AAR et le SDK complet ci-dessous restent disponibles via les comptes Console éligibles. Gardez votre propre application de production de signature de clé et de connexion de moteur séparé de cette démo.
APK somme de contrôle et installation USB
SHA-256:
b96ba527ddf9abae5bff2f8b85a39a12b59c934aaacde1961c8096aa32524c54Avec des outils de plate-forme Android et un dispositif autorisé USB:
adb install -r drmx-android-player-1.0.0-beta.11.apkInstallez ce APK sur l'échantillon officiel existant sans le désinstaller. La même clé de signature et l'application ID préservent les téléchargements, les licences hors ligne, les positions sauvegardées et les préférences. Si votre propre build utilise une autre clé de signature, utilisez une application séparée ID pour conserver les deux applications et préserver les licences hors ligne de l'échantillon.
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.
En Offline Mode, ouvrez le menu à trois lignes de la vidéo et choisissez Delete. Confirmez pour supprimer uniquement ses médias chiffrés et références de licence locaux ; Cancel les conserve. Reconnectez-vous pour la télécharger à nouveau.
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#
Sortie le 17 septembre 2026. L'application téléphonique et le client SDK incluent le test 4K Live DRM. La lecture en direct utilise de nouveaux manifestes et ne peut pas être téléchargée. L'événement test est disponible uniquement pendant une diffusion active. Suivez les guides de configuration AWS et OBS pour créer votre propre événement.
Quoi de neuf dans Android lecteur prévisualisation.12#
Sortie le 14 septembre 2026. Correction de la lecture téléchargée faisant une demande de licence inattendue lorsqu'une licence Widevine est expirée ou qu'il reste une minute ou moins. Cela pourrait apparaître sous la forme d'une erreur Media3 6004, d'une erreur HTTP lors d'une connexion ou d'une erreur DNS lors d'une déconnexion.
Le lecteur restaure maintenant la licence téléchargée localement. Les licences valides conservent leur temps de lecture restant; les licences expirées affichent Connect et tapent Renew. Les règles de validité et de protection de la licence restent appliquées par l'appareil. Les diagnostics incluent également l'état numérique HTTP lorsqu'une opération réseau échoue, sans exposer les en-têtes de requête, les URL ou les réponses de licence.
- Installez preview.12 sur l'application officielle existante. Gardez vos données et téléchargements d'applications en cours.
- Si WayMaker ou une autre vidéo téléchargée a besoin d'être renouvelée, connectez-vous à Internet, sélectionnez cette vidéo et appuyez sur Renouvellement.
- Attendez le License renouvelé. Prêt à jouer hors ligne. , puis choisissez Play offline. La vidéo chiffrée reste téléchargée.
Cette version conserve les commandes et fonctions de playlist de preview.11. L’application d’évaluation publique et son lecteur intégré affichent preview.12. Lors de cette publication, le Console SDK distribué séparément était preview.10 / bundle 20260910.8.
Quoi de neuf dans Android lecteur preview.11#
Publié le 14 septembre 2026. Cette application d'évaluation comprend les dernières améliorations de contrôle et de liste de lecture lecteur, avec un style d'interaction cohérent entre nos Android et Apple lecteurs.
- Contrôle à l'intérieur de la vidéo: centred Play/Pause, Previous and Next for multi-video playlists, CC, Paramètres et un bouton plein écran. Les commandes se cachent après deux secondes pendant la lecture; appuyez sur la vidéo pour les révéler à nouveau.
- Paramètres de saisie et de lecture: utilisez CC pour afficher ou masquer les sous-titres disponibles, et Paramètres pour choisir le langage de légende, audio, vitesse de lecture et qualité autorisée.
- Drag pour réorganiser: faites glisser la poignée d'un élément de liste de lecture pour changer sa position. L'ordre enregistré survit à un redémarrage de l'application et est reflété dans la liste de lecture téléchargée.
- Enregistré shuffle et répéter: les modes sélectionnés restent en surbrillance et survivent aux redémarrages en lecture en ligne et hors ligne. Avec shuffle et répéter les deux off, les vidéos suivent l'ordre enregistré et s'arrêtent après l'élément final.
- Resumez où vous avez arrêté: la position de chaque vidéo enregistrée VOD est restaurée lorsque vous retournez à la lecture en streaming ou téléchargée. Les progrès sont enregistrés sur ce périphérique pour le visionneur actuel; il ne se synchronise pas entre les périphériques.
Organisez les listes de lecture, enregistrez leur ordre et reprenez la lecture. Le visionnage hors ligne nécessite un téléchargement terminé et une licence hors ligne utilisable et non expirée.
La version preview.11 a mis à jour l’APK d’évaluation public. Le Console SDK actuel est 1.0.0-beta.14, bundle 20261028.1. Les instructions ci-dessous utilisent ce bundle.
1. Choisissez un chemin d'intégration#
Utilisez DrmXPlayer, l'échantillon de téléphone et le gestionnaire hors ligne. Gardez vos propres règles de connexion et d'affaires.
PATH B · KEEP YOUR UIMedia3 + DRM-X aidesGarder une pile existante lecteur et télécharger. Laissez DrmXOfflineManager gérer les licences persistantes.
PATH C · NATIVE ONLYMedia3 sans le AARImplémentez le même contrat de moteur et le même cycle de vie de licence natif vous-même. Non DRM-X lecteur dépendance.
Téléchargez depuis Developer Tools → Device SDKs. L’utilisation du SDK exige un accès Professional ou Enterprise actif, y compris une évaluation Professional approuvée. Standard conserve les APIs natives pour votre propre lecteur. L’accès au SDK est vérifié à la création de session et au renouvellement de licence ; les licences hors ligne expirent au plus tard à la fin de la période de service du compte.
Version préliminaire actuelle : 1.0.0-beta.14 · bundle client 20261028.1 · Media3 1.11.0. Le ZIP contient un dépôt Maven local, Gradle wrapper, un README commenté, des exemples téléphone/TV/natifs, un APK installable de l’exemple téléphone dans apks/ et des notes d’intégration hors ligne. Il ne contient ni identifiants client ni films protégés.
2. Préparez votre contenu et votre moteur #
- Paquetez et publiez un titre compatible Widevine crypté DASH dans votre environnement DRM-X. Copiez son exact Publié Content ID.
- Configurez Environment ID, Site Key et Access Key de votre serveur. Utilisez l'intégration PHP téléchargeable pour un point de départ de travail.
- Implémentez votre login utilisateur et la vérification des droits. Votre application appelle votre backend avec son jeton utilisateur de courte durée; votre backend choisit le contenu, la politique et les URL de confiance.
- Exécutez la lecture en ligne d'abord sur un appareil physique compatible Widevine. Ajoutez l'acquisition hors ligne seulement après que le contrat de streaming fonctionne.
La vidéo chiffrée et les licences DRM suivent des chemins distincts
Le CDN distribue la vidéo chiffrée. Votre backend vérifie l’accès, et le service de licences DRM autorise le déchiffrement sur un appareil compatible.
- Stockage / CDN
Distribue les médias chiffrés au lecteur.
- Votre backend
Vérifie le droit de visionnage et demande l’autorisation de lecture à DRM-X.
- DRM-X
Valide l’autorisation de lecture et traite la demande de licence DRM.
- Lecteur + appareil
Utilise le système DRM de l’appareil pour obtenir une licence et lire les médias chiffrés.
| Modifier cette valeur | Où il appartient | Signification |
|---|---|---|
| sessionEndpoint | Android | Votre paramètre de session de lecture HTTPS, pas le serveur de licence brut URL. |
| offlineEndpoint | Android, hors ligne seulement | Votre critère d'autorisation d'acquisition/renouvellement/libération. |
| contentId | Android requête; le moteur valide | Le téléspectateur protégé Content ID publié a droit à l'accès. |
| jeton d'accès au client | Mémoire de l'application après la connexion | Votre jeton d'utilisateur de courte durée, jamais un Site Key ou Access Key. |
| Environment ID, Site Key, Access Key | Configuration secrète du serveur seulement | DRM-X compte identifiants. Ne jamais les expédier dans un APK, HTML ou un contrôle de source. |
| Politique et manifeste de confiance URL | Votre backend | Droit d'entreprise et cartographie des paquets. N'acceptez pas ceux-ci de l'application. |
3. Installez et exécutez le SDK#
Ouvrez la racine du ZIP extrait dans Android Studio. Utilisez JDK 17, Android SDK 36 et Android Gradle Plugin 9.2.0 ; le wrapper fourni utilise Gradle 9.4.1. Le niveau API minimal de l’appareil est 23. Le téléchargement des dépendances de compilation nécessite Internet. Sélectionnez samples.phone, puis Run. Les paramètres initiaux utilisent le backend et le contenu de la démo publique ; testez la connexion et le streaming avant de passer à votre propre backend.
# Windows
gradlew.bat :samples:phone:assembleDebug
# macOS / Linux
sh gradlew :samples:phone:assembleDebug
Pour une application existante, copiez le dossier ZIP de maven/ dans votre racine de projet. Dans settings.gradle.kts :
dependencyResolutionManagement {
repositories {
maven { url = uri(rootDir.resolve("maven")) }
google()
mavenCentral()
}
}
Dans le build.gradle.kts de votre module d'application:
dependencies {
implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14")
}
Installer le standalone AAR à la place
Mettez le AAR dans le libs/ de votre module d'application. Un AAR plat n'a pas de résolution de dépendance transitive automatique, alors ajoutez les trois dépendances Media3 correspondantes vous-même. Ne pas installer les deux formulaires.
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")Dans AndroidManifest.xml, autoriser l'accès à Internet et désactiver les sauvegardes pour cet échantillon minimal. Dans une application de production qui nécessite des sauvegardes, exclure spécifiquement les métadonnées de licence hors ligne liées aux appareils.
<uses-permission android:name="android.permission.INTERNET" />
<application android:allowBackup="false" android:usesCleartextTraffic="false" ... >
Effectuez une recherche sur l'échantillon téléphonique MainActivity.kt pour drmx.sessionEndpoint, drmx.offlineEndpoint, drmx.contentId et customer.loginEndpoint. Changez les paramètres de repli marqués, ou passez ces extras d'intention de votre application après l'ouverture de session. Passez customer.accessToken au moment de l'exécution. Le ZIP README explique chaque paramètre et quels crochets de démonstration sont à remplacer.
Construisez un test APK en utilisant le AAR#
Cet exemple utilise le projet de téléphone exécutable de la SDK ZIP complète et relie explicitement la AAR autonome. La même approche fonctionne pour une application Android existante.
- Télécharger et extraire le full Android SDK ZIP . Ouvrez son dossier racine dans Android Studio.
- Conservez
libs/drmx-universal-player-1.0.0-beta.14.aarà la racine du dossier extrait. Utilisez JDK 17, Android SDK 36 et le Gradle wrapper fourni. - Dans
samples/phone/build.gradle.kts, remplacez la dépendance DRM-X Maven par les entrées suivantes à l'intérieur dedependencies. Gardez le reste de ce fichier de compilation.
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")
Supprimer implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14") de sorte que le même SDK n'est pas inclus deux fois. Un AAR plat a besoin des trois dépendances Media3 indiquées ci-dessus.
- Pour garder la démo téléchargée installée à côté de votre propre compilation de test, changez le
applicationIddu module téléphonique encom.example.drmxtest. Laissez son espace de noms inchangé pour cet échantillon fourni. - Construisez le APK, puis installez-le sur un périphérique USB- connecté Android avec le débogage autorisé USB.
# Windows, from the extracted SDK root
gradlew.bat :samples:phone:assembleDebug
adb install -r samples/phone/build/outputs/apk/debug/phone-debug.apk
# macOS / Linux
sh gradlew :samples:phone:assembleDebug
adb install -r samples/phone/build/outputs/apk/debug/phone-debug.apk
Ouvrez l'application de test et appuyez sur Play. La source de téléphone fournie utilise la même démo publique initialement. Pour tester votre propre titre, remplacez les paramètres marqués session/offline/login et Publié Content ID dans MainActivity.kt, et fournissez l'authentification courte durée de votre propre utilisateur à l'exécution. Votre serveur conserve les Site Key et Access Key.
Les commandes compactes proviennent de DrmXPlayerView dans le AAR. L'activité fournie démontre la fonction Play-to-autoriser, la rotation réactive, la propriété de téléchargement, la lecture hors ligne et le renouvellement. Voir l'exemple d'intégration de contrôle ci-dessous lors de l'ajout de cette vue à votre propre application.
4. Requête du serveur et politique par défaut#
L'application Android demande à votre backend une session de lecture. Votre backend authentifie l'utilisateur, vérifie le droit, les cartes Content ID vers les médias publiés et les appels DRM-X. Voir Playback Session API pour le contrat de diffusion complet.
{
"contentId": "YOUR-PUBLISHED-CONTENT-ID",
"contentType": "vod",
"drmSystem": "widevine",
"playbackMode": "streaming",
"platformCapabilities": {
"clientPlatform": "android",
"drmSystems": ["widevine"], "manifestTypes": ["dash"],
"maximumHeight": 1080, "secureDecoder": false, "persistentState": false
}
}
Les capacités conservatrices ci-dessus n'affirment pas Widevine L1. Utilisez l'aide de capacité du SDK, ou votre propre évaluation d'appareil, pour les appareils réels.
L'échantillon PHP par défaut est multi-tier-standard pour la lecture en ligne. Sélectionnez sa politique documentée à clé unique lorsque votre paquet utilise une clé. Votre politique doit correspondre au modèle de la clé du paquet et aux droits du spectateur. Changez la configuration du serveur ou son environnement DRMX_LICENSE_POLICY_TEMPLATE, pas un paramètre de requête dans le lecteur. Voir politiques de licence.
playback_policy.persistent sur true dans la politique émise par le serveur et choisissez les durées de licence, de location, de lecture et de renouvellement. La référence utilise respectivement 7 jours / 3 jours / 48 heures / 7 jours ; il s'agit d'exemples de règles commerciales, et non d'une approbation du studio ou d'une valeur par défaut à l'échelle de la plateforme. Renvoie les métadonnées correspondantes. La licence Widevine reste faisant foi.5. Chemin A: jouer à travers DrmXPlayer#
À l'intérieur de votre activité, créez un PlayerView, utilisez une fenêtre sécurisée et appelez prepare après l'ouverture de session. L'emballage échange la session, configure Widevine, applique la hauteur maximale retournée et libère la réservation en streaming lorsqu'elle est arrêtée. Les importations proviennent de com.haihaisoft.drmx.player et 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()
Inscrivez DrmXPlayerListener pour autoriser, préparer, jouer et erreur UI. Affichez des codes d'erreur sûrs plutôt que de jeter des jetons de licence ou des corps de réponse. Utilisez l'échantillon de téléphone inclus pour le cycle de vie complet, les contrôles à écran complet et la file d'attente de téléchargement.
Commandes du lecteur et rotation ininterrompue#
Appuyez sur Play sur la vidéo pour commencer l'autorisation. L'ouverture de l'écran ou des paramètres n'acquiert pas de licence de streaming.

| Contrôle | Ce que les clients peuvent faire |
|---|---|
| Qualité | Choisissez Auto ou une résolution sous licence prise en charge à partir du manifeste. Les options non disponibles expliquent la licence ou la restriction de l'appareil/DRM. Auto obéit aux mêmes restrictions. |
| Piste audio | Choisissez n'importe quelle langue audio prise en charge fournie par le paquet. |
| Sous-titres/CC | Choisissez un langage de sous-titre ou désactivez. Le bouton CC s'affiche lorsque les sous-titres pris en charge existent et les bascule directement. |
| Apparence de caption | Réglez la police, la couleur et la taille du texte, les couleurs de fond/fenêtre et l'opacité. Les préférences sont enregistrées sur l'appareil. |
| Vitesse de lecture / minuterie de sommeil | Jouer à 0,25–2× vitesse ou pause automatiquement après un intervalle choisi. |
| Rechercher / plein écran | Frottez la timeline, cherchez 10 secondes, ou double-tap de chaque côté. Rotation vers le paysage sans redémarrer la lecture. |
Chaque menu de vidéo propose Move up / Move down et Download en ligne ou Delete hors ligne. Download est désactivé une fois le téléchargement terminé. Le commutateur Downloaded videos sélectionne la lecture hors ligne ; seules les pistes en cache sont disponibles. Il n’existe pas de boutons distincts pour télécharger, supprimer ou télécharger toute la liste.
Copier le journal à côté du pied de page d'information copie les erreurs récentes comme texte, de sorte que vous pouvez envoyer support les détails sans capture d'écran. ID, fonctionnement, SDK/Android/ version d'appareil, numérique CDM/Media3 les erreurs, les cadres de piles sélectionnés, l'état de connexion/téléchargement/playback et le nombre de répétitions. identifiants, URLs multimédias, clés, IDs de jeu de clés, données de licence et complet CDM Des ordures.
L'échantillon conserve jusqu'à 32 rapports locaux (64 KB) pendant cinq heures. Lorsqu'il est connecté et connecté, il télécharge les erreurs en attente au serveur client configuré, au plus une fois par 30 secondes. Le serveur de démo public conserve les avertissements/erreurs pendant cinq heures, combine des événements répétés, capte le fichier à 512 KB et prune chaque minute. La progression courante et la lecture réussie ne sont pas enregistrées. Les intégrations personnalisées doivent fournir explicitement leur propre source drmx.diagnosticsEndpoint ; les erreurs de diagnostic n'interrompent jamais la lecture.
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()
Le manifeste du téléphone fourni gère les changements d'orientation et de taille d'écran; onConfigurationChanged redimensionne la même activité et lecteur. Gardez les deux parties lors de l'adaptation. Un hôte qui recrée les activités devrait conserver son propriétaire de lecture et réattacher la vue. Procéder à la mort est différent de la rotation et nécessite une restauration de l'état normal.DRM l'appui; CDM demeure faisant autorité.
Pour personnaliser lecteurs, arrêtez et relâchez la session active hors ligne DRM avant de vérifier, renouveler ou libérer la même licence persistante. La SDK de player.stopForOfflineLicenseOperation { /* call manager here */ } attend que la file d'attente de lecture Media3 relâche les médias. Les files d'attente de Plaine stop() fonctionnent asynchronement. Ne pas effectuer une autre checkLicense() pendant que la même licence est en cours de lecture; la lecture valide elle-même son état CDM. L'échantillon fourni montre la sortie complète et désactive les actions qui se chevauchent pendant le renouvellement.
6. Acquérir une licence et télécharger le média#
Préparez la lecture hors ligne
Il faut à la fois un téléchargement complet et une licence valide liée à l’appareil.
- Téléchargez les médias chiffrés
Lorsque vous êtes connecté, téléchargez la vidéo ainsi que les pistes audio et de sous-titres nécessaires.
- Obtenez une licence persistante
Lorsque vous êtes connecté, obtenez la licence Widevine liée à l’appareil. Conservez son Key-set ID opaque dans un stockage privé.
- Vérifiez que tout est prêt
Vérifiez que le téléchargement est terminé et que la licence est valide.
- Lisez hors ligne
Lisez les médias en cache et restaurez la licence locale. Le renouvellement et la libération nécessitent une connexion Internet.
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 */ }
}
Les SDK sauve l'opaque Widevine jeu de clés ID et des métadonnées non secrètes. CDM détient la licence. Il ne persiste pas DRM License Tokens, clés brutes, défis ou octets de réponse de licence. Exclure com.haihaisoft.drmx.offline.licenses.v1.xml des renforts.
Créez une application appartenant à Media3 SimpleCache, gestionnaire de téléchargement et base de données. Utilisez DownloadHelper avec le MediaItem sous licence et sélectionnez la vidéo dans maximumHeight. Dans Media3 1.11 le callback préparé est onPrepared(helper, tracksInfoUpdated). L'échantillon de téléphone enqueueDownload et OfflineTestRuntime montrent l'implémentation exécutable.
// Inside DownloadHelper's prepared callback, after selecting licensed video:
helper.addAudioLanguagesToSelection("en", "zh-Hans", "zh")
helper.addTextLanguagesToSelection(true, "en", "zh-Hans", "zh")
val selected = helper.getDownloadRequest(offlineLicenseId, byteArrayOf())
val request = offlineManager.createDownloadRequest(contentId, selected.streamKeys)
downloadManager.addDownload(request)
helper.release()
Télécharger chaque langue que vous avez l'intention d'exposer hors ligne. Appeler createDownloadRequest sans clés de flux télécharge toutes les pistes, ce qui est généralement gaspillé pour la vidéo adaptative. Afficher la progression du téléchargement et attendre Download.STATE_COMPLETED. Pour les transferts de fond de production, intégrer Media3 DownloadService, les limites de stockage et Android les exigences de premier plan de service/notification; la file de démonstration fonctionne en cours de processus.
7. Chemin B: restaurer avec votre Media3 lecteur#
Lire le téléchargement terminé et utiliser ses touches de flux exacts. La pile de téléchargement et de lecture doit partager le même emplacement de cache et la cartographie des touches. Cet exemple désactive délibérément l'accès en amont pour une lecture hors ligne stricte:
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.
Pour utiliser le même flux hors ligne avec le chemin A, construire DrmXPlayer avec ce mediaSourceFactory et appeler player.prepareOffline(playback). La restauration ordinaire utilise la licence locale CDM sans en demander une nouvelle. Une entrée de cache manquante devrait produire une erreur lors de l'acceptation déconnectée, ne pas basculer silencieusement vers Internet.
8. Chemin C: natif Media3, non DRM-X AAR #
Vous n'êtes pas obligé d'utiliser DrmXPlayer ou DrmXOfflineManager. Ajoutez les trois dépendances Media3 de l'étape 3 et implémentez le même contrat backend client. Utilisez session.manifestUrl (et non session.manifest.url), la licence renvoyée URL et les en-têtes d'autorisation de courte durée. Appliquez la hauteur maximale renvoyée et gérez la libération de la session de streaming en cas d'arrêt, de remplacement, de fin, de panne et de destruction.
L'activité native complète ci-dessous est compilée sous la forme samples:native à côté de cette version SDK. Elle gère HTTPS, la lecture de la réponse limitée, l'autorisation de l'arrière-plan, les réponses statiques et la version de réservation. Remplacez le paramètre marqué/Content ID et fournissez votre propre jeton de connexion. L'exemple est un point de départ; ajoutez votre cycle de vie de mise à jour de production, de commutation de compte et d'application-navigation.
Activité de streaming native complète (Kotlin, aucune dépendance 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() }
}
}
}
Pour licence native hors ligne, utiliser OfflineLicenseHelper.downloadLicense, persistent son ensemble de clés opaques ID en privé et restaurer avec MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).setKeySetId(id). L'aide compilée suivante couvre l'acquisition, les vérifications de durée, le renouvellement, la publication et le téléchargement / playback des éléments. pas une application de téléchargement complète: vous possédez toujours le cache, le service de téléchargement, le progrès, les métadonnées, l'utilisateur/dispositif et l'autorisation d'exploitation fiable.
Aide à la délivrance de licences persistantes (Kotlin, aucune dépendance 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())
}
}
Avant d'appeler cet helper de bas niveau, validez que la nouvelle réponse du moteur correspond à la réponse demandée Content ID, fonctionnement, utilisateur/dispositif signé et période de validité de l'autorisation. Persistez un stable offlineLicenseId et des métadonnées manifeste/track de confiance à côté de l'ensemble de clés ID, jamais le jeton . Exécuter le blocage aide appelle sur un fil de travail . HTTP en-têtes utilisés setLicenseRequestHeaders sur la DRM la configuration; Widevine Les paramètres optionnels de la demande de clé sont une carte différente.
De nouvelles intégrations devraient être utilisées AndroidX Media3 DRM appui. Héritage com.google.android.exoplayer2 les projets devraient suivre Guide officiel des migrations; ne mélangez pas le legs et AndroidX classes ou supposent que l'échantillon compile inchangé par rapport aux anciennes versions.
9. Vérifier, renouveler, libérer et changer de langage #
Il s'agit d'actions séparées. Exécutez une à la fois après la sortie de la session active hors ligne DRM : utilisez player.stopForOfflineLicenseOperation { /* one operation below */ } avec DrmXPlayer, ou relâchez d'abord le natif existant Media3 lecteur. Ne vérifiez pas une licence pendant qu'elle est en cours de lecture. Une vérification est facultative pour afficher les durées; elle ne doit pas empêcher le renouvellement d'une licence expirée.
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 lit les métadonnées; checkLicense interroge les durées locales autorisées CDM. Le renouvellement/release nécessite une connectivité et une nouvelle autorisation spécifique à l'opération de votre moteur. La suppression des médias ne libère pas la licence. Lorsqu'elle est déconnectée, la sortie de file d'attente pour la prochaine connexion plutôt que de signaler que le serveur l'a confirmée. Fermer le gestionnaire lorsque son propriétaire est détruit.
// 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()
Une préférence ne peut pas créer une piste anglaise absente du paquet. Les utilisateurs hors ligne ne peuvent sélectionner que les langues téléchargées. Préserver license/cache état à travers la rotation de l'écran; plein écran est un changement de vue/fenêtre, pas une raison d'acquérir une autre licence.
10. Mettre en œuvre le paramètre d'autorisation hors ligne#
L'application affiche contentId, contentType: "vod", drmSystem: "widevine", playbackMode: "offline", operation: "acquire" | "renew" | "release", offlineLicenseId (null pour l'acquisition initiale) et véridique platformCapabilities. Vérifiez que les identifiants de renouvellement/release appartiennent à l'utilisateur, au périphérique et au contenu authentifiés. Sélectionnez le côté serveur de la politique, publiez un nouveau DRM License Token et retournez le contrat correspondant:
{
"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
}
}
Ce sont des détenteurs de place explicatifs, pas une réponse réutilisable de licence. Retourner des valeurs fraîches de l'opération réelle. SDK soutient également integrationMode: "token-proxy" avec une limite drm.authorization Descripteur. Retour Cache-Control: no-store; ne jamais enregistrer les organismes d'autorisation réussis. SDK's OFFLINE_INTEGRATION.md et PHP le paramètre hors ligne de l'échantillon pour la gestion des demandes concrètes.
11. Testez vos appareils et dépannage #
| Test/symptôme | Comportement attendu / prochaine vérification |
|---|---|
| SDK le téléchargement retourne 403 | Sélectionnez une organisation avec un abonnement professionnel ou Enterprise actif. Une mise à jour prévue après l'essai ne déverrouille pas le SDK. |
| Séance 401/403 | Vérifier la connexion, le droit, l'environnement, publié Content ID et la politique. Ne jamais contourner l'authentification. |
| Hors ligne échoue mais fonctionne en ligne | Vérifiez CDM durées, téléchargement terminé, emplacement du cache, clés de flux et toutes les clés sous licence requises. |
| Français / Chinois manquant | Vérifiez les métadonnées de la langue du paquet, les pistes réelles et la sélection hors ligne de la langue. |
| L1, HDCP ou défaillance du décodeur | Vérifiez le matériel physique, la fourniture, le codec et les exigences de sortie. Une chaîne de capacité n'est pas une certification. |
| Redémarrage, rotation, mise à niveau | Licence et cache persistants survivent aux changements de cycle de vie supportés sans acquisition dupliquée. Les sauvegardes ne doivent pas migrer les licences vers un nouvel appareil. |
| Mode avion | Désactiver aussi le Wi-Fi et les données mobiles; redémarrer l'application, jouer localement et changer chaque langue promise. |
| Expiration, pression de stockage, interrupteur de compte | Afficher un état clair non disponible/renouveller, récupérer des médias incomplets, et faire appliquer l'accès utilisateur/appareil sans fuite du catalogue hors ligne d'un autre utilisateur. |
Qualifier chaque téléphone, tablette, Android TV/Google TV, Fire TV ou modèle Android intégré pour Widevine, décodage sécurisé, codec/résolution, HDCP, affichages externes, changements d'horloge et longue déconnexion. Tous les appareils Android n'ont pas les mêmes capacités DRM.
Listes de lecture et contrôles partagés Universal Player#
Les lecteurs Android et Apple proposent Previous/Play-Pause/Next, CC, Settings et le plein écran. Move up / Move down conserve la vidéo actuelle et enregistre l’ordre de la liste pour la lecture en ligne et hors ligne. Les choix de lecture aléatoire et de répétition persistent au redémarrage ; si les deux sont désactivés, la lecture s’arrête après le dernier élément.
Fournissez le même client/spectateur storageNamespace à DrmXPlayer et playerView.storageNamespace avant de définir la liste de lecture. VOD progrès est enregistré périodiquement et sur pause/arrêt, puis restauré pour cela Content ID dans la lecture en streaming ou téléchargée. Lecture en direct n'utilise pas une position enregistrée VOD. Les téléchargements complétés conservent le même comportement shuffle/repeat/resume sans source de médias réseau. Cet état périphérique-local ne synchronise pas les progrès entre les appareils.
Web et Android partagent panneau sombre et contrôles bleus compacts. Suivant/précédent, aléatoire, répétition un/tous et avance automatique annulable sont disponibles. Chaque élément demande une nouvelle autorisation. Chapitres et transcription viennent du catalogue.
Vitesse, minuterie, sous-titres, qualité autorisée, langues audio, signets et boucle A–B sont disponibles. Web ajoute raccourcis, reprise locale facultative par spectateur, recherche de transcription et PiP compatible. Android conserve téléchargements Widevine hors ligne et renouvellement. Diffusion vers un autre écran et progression interappareils exigent une intégration supplémentaire.
Paramètres du logo professionnel et d'entreprise
Open Settings → A propos de lecteur pour le logo autorisé, l'information sur le produit et la version lecteur. Web offre également cette information en un clic droit. La démo du téléphone public comprend WayMaker, Goodness of God, Fresh Oil et 4K Test Video; chaque sélection reçoit sa propre autorisation.
Dans le backend de confiance, réglez playerBranding sur {"mode":"hidden"} ou {"mode":"custom","logoUrl":"https://your-site.example/logo.png","label":"Your brand"}. La plateforme vérifie Professional ou Enterprise actif. Les autres offres utilisent {"mode":"default"} ; les personnalisations ou masquages non autorisés donnent 403. Ce contrôle est distinct du filigrane de sécurité.
Le téléchargement PHP comprend liste et marque modifiables. Le ZIP Android fournit le callback de liste. Gardez son autorisation dans le backend et n’intégrez jamais les identifiants de client API aux lecteurs.
DRM-X 6.0