SDK de Android y Media3 nativo
Descargue el reproductor Android con listas que se pueden reordenar arrastrando, ajustes guardados de reproducción aleatoria y repetición, y reanudación sin conexión. Integre la reproducción protegida con DRM-X o Media3 nativo.
Para personalizar la interfaz y consultar valores predeterminados y ejemplos del ciclo de vida, lee PLAYER-UI-CONFIGURATION.md. Los paneles emergentes del reproductor son opacos; la opacidad de los subtítulos y las marcas de agua se configura por separado.
Pruebe la demostración PHP siguiendo DEMO-QUICKSTART.md en el SDK ZIP. Para usar su contenido, integre primero su sitio web y backend; después, siga MOBILE-AND-TV-APPS.md para conectar la aplicación al mismo inicio de sesión y las mismas reglas de acceso.
Prueba el Android reproductor
1.0.0-beta.11 · 12.15 MB · Android 6.0+ (API 23+) · DRM-X 6.0 Player
- Abra esta página en su Android teléfono y descargar el APK.
- Abra el archivo descargado. Si Android pregunta, permita la instalación desde el navegador o gestor de archivos que utilizó, a continuación, pulse Install.
- Abrir DRM-X 6.0 Player, mantenerse conectado y pulsar el botón Play del vídeo. La aplicación solicita una licencia de streaming cuando pulsa Play.
- Abrir Configuraciones para elegir calidad, audio, leyendas o velocidad de reproducción. Rota el teléfono para continuar en pantalla completa.
- Abra el menú de tres líneas del vídeo y elija Download. Active Downloaded videos para Offline Mode; desactívelo para Online Mode. En Offline Mode, elija Delete en el menú de ese vídeo y confirme. Renovar la licencia requiere conexión y autorización vigente; ante un error sin conexión puede mantener ese modo o pasar al modo en línea.
Si Android detiene la aplicación mientras prepara licencias o pistas, vuelva a abrirla mientras esté conectada para terminar la preparación de los elementos restantes. Se mantienen descargas existentes. La reproducción sin conexión sólo utiliza medios cachés; las descargas incompletas o caducadas requieren terminación o renovación.
La muestra APK es pública y firmada para evaluación. La AAR y la completa SDK siguen disponibles a través de cuentas de consola elegibles. Mantenga su propia aplicación de producción firmando clave y backend login separado de esta demo.
APK checksum y USB instalación
SHA-256:
b96ba527ddf9abae5bff2f8b85a39a12b59c934aaacde1961c8096aa32524c54Con Android herramientas de plataforma y un dispositivo autorizado USB:
adb install -r drmx-android-player-1.0.0-beta.11.apkInstala esto APK sobre la muestra oficial existente sin desinstalarla. La misma clave de firma y aplicación ID preserva las descargas, licencias sin conexión, posiciones y preferencias guardadas. Si su propia construcción utiliza otra clave de firma, utilice una aplicación separada ID para mantener ambas aplicaciones y preservar las licencias sin conexión de la muestra.
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, abra el menú de tres líneas del vídeo y elija Delete. Confirme para eliminar solo sus medios cifrados y referencias de licencia locales; Cancel los conserva. Vuelva a conectarse para descargarlo de nuevo.
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#
Publicado el 17 de septiembre de 2026. La aplicación de teléfono y el cliente SDK incluyen la prueba 4K Live DRM. La reproducción en vivo utiliza manifiestos nuevos y no se puede descargar. El evento de prueba está disponible solo durante una transmisión activa. Siga las guías de configuración AWS y OBS para crear su propio evento.
Lo nuevo en Android reproductor vista previa.12#
Liberado 14 septiembre 2026. Arreglos descargados reproducción haciendo una solicitud de licencia inesperada cuando una licencia Widevine está caducada o tiene un minuto o menos restante. Esto podría aparecer como error Media3 6004, un error HTTP mientras está conectado, o un error DNS mientras se desconecta.
El reproductor ahora restaura la licencia descargada localmente. Las licencias válidas conservan su tiempo de reproducción restante; licencias expiradas muestran Connect y tap Renew. Las reglas de validez y protección de licencias siguen siendo aplicadas por el dispositivo. Los diagnósticos también incluyen el estado numérico HTTP cuando una operación de red falla, sin exponer encabezados de solicitud, URLs o respuestas de licencia.
- Instala la vista previa.12 sobre la aplicación oficial existente. Mantenga los datos y descargas de su aplicación actual.
- Si WayMaker u otro vídeo descargado necesita renovación, conectarse a Internet, seleccione ese vídeo y grifo Renew.
- Espere License renovado. Listo para jugar sin conexión., luego elija Play offline. El vídeo cifrado permanece descargado.
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.
Lo nuevo en Android reproductor vista previa.11#
Publicado 14 septiembre 2026. Esta aplicación de evaluación incluye los últimos reproductor controles y mejoras de la lista de reproducción, con un estilo de interacción consistente a través de nuestro Android y Apple reproductores.
- Controles dentro del vídeo: centrado Play/Pause, Anterior y Siguiente para listas multivideo, CC, Ajustes y un botón de pantalla completa. Los controles se ocultan después de dos segundos mientras juegan; toque el vídeo para revelarlos de nuevo.
- Capciones y configuración de reproducción: use CC para mostrar o ocultar subtítulos disponibles, y Ajustes para elegir el lenguaje de captura, audio, velocidad de reproducción y calidad permitida. Las opciones sin conexión utilizan sólo las pistas incluidas en la descarga.
- Drag to reorder: arrastrar el mango de un elemento de lista para cambiar su posición. El pedido guardado sobrevive a un reinicio de la aplicación y se refleja en la lista de reproducción descargada. Reordering mantiene el vídeo actual jugando. Pulse el mango para ser accesible Move up / Move down actions.
- El shuffle guardado y repetir: los modos seleccionados permanecen resaltados y sobreviven en reiniciados en la reproducción en línea y fuera de línea. Con el shuffle y repetir ambos apagados, los vídeos siguen el orden guardado y deténganse después del elemento final.
- Resumen donde se detuvo: la posición guardada de cada vídeo VOD se restaura cuando regresa a la reproducción de streaming o descargada. El progreso se guarda en este dispositivo para el espectador actual; no sincroniza entre dispositivos.
Organice listas, guarde su orden y reanude la reproducción. La visualización sin conexión requiere una descarga completa y una licencia sin conexión válida y no caducada.
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. Elija un camino de integración #
Utilice DrmXPlayer, la muestra de teléfono y el administrador sin conexión. Mantenga su propio login y reglas de negocio.
PATH B KEEP YOUR UIMedia3 + DRM-X ayudantesMantenga una pila existente reproductor y descargue. Deje que DrmXOfflineManager administre licencias persistentes.
PATH C · NATIVE ONLYMedia3 sin el AAREjecute el mismo contrato de backend y el ciclo de vida de licencia nativa. No DRM-X reproductor dependencia.
Descargue desde Developer Tools → Device SDKs. El SDK requiere acceso Professional o Enterprise activo, incluidas las evaluaciones Professional aprobadas. Standard conserva las APIs nativas para su propio reproductor. El acceso al SDK se verifica al crear sesiones y renovar licencias; las licencias sin conexión no superan el periodo de servicio de la cuenta.
Versión preliminar actual: 1.0.0-beta.14 · paquete para clientes 20261028.1 · Media3 1.11.0. El ZIP contiene un repositorio Maven local, Gradle wrapper, un README comentado, ejemplos para teléfono/TV/nativos, un APK instalable del ejemplo para teléfono en apks/ y notas de integración sin conexión. No contiene credenciales de clientes ni películas protegidas.
2. Prepare su contenido y backend#
- Paquete y publicar un Widevine-compatible título encriptado DASH en su DRM-X Medio ambiente. Copia su exacta Publicado Content ID.
- Configure su servidor Environment ID, Site Key y Access Key. Use la integración descargable PHP para un punto de partida de trabajo.
- Implementa tu login de usuario y control de derechos. Tu app llama a tu backend con su ficha de usuario de corta duración; tu backend elige el contenido, la política y las URLs de confianza.
- Ejecute la reproducción en línea primero en un dispositivo físico Widevine-capable. Agregue la adquisición fuera de línea sólo después de que el contrato de streaming funciona.
El vídeo cifrado y las licencias DRM se entregan por rutas distintas
La CDN entrega el vídeo cifrado. Su backend comprueba el acceso y el servicio de licencias DRM autoriza el descifrado en un dispositivo compatible.
- Almacenamiento / CDN
Entrega el contenido cifrado al reproductor.
- Su backend
Comprueba si el espectador tiene permiso y solicita la autorización de reproducción a DRM-X.
- DRM-X
Valida la autorización de reproducción y procesa la solicitud de licencia DRM.
- Reproductor + dispositivo
Utiliza el sistema DRM del dispositivo para obtener una licencia y reproducir el contenido cifrado.
| Cambiar este valor | Donde pertenece | Significado |
|---|---|---|
| sessionEndpoint | Android | Su HTTPS punto final de reproducción-sesión, no el servidor de licencia cruda URL. |
| offlineEndpoint | Android, offline only | Su punto final de autorización de adquisición/renovar/renunciar. |
| contentId | Android petición; backend validates | El publicado protegido Content ID el espectador tiene derecho a acceso. |
| acceso al cliente token | App de memoria después de iniciar sesión | Tu token de usuario de corta duración, nunca un Site Key o Access Key. |
| Environment ID, Site Key, Access Key | Configuración secreta del servidor solamente | DRM-X cuenta credenciales. Nunca enviarlos en un APK, HTML o control de fuente. |
| Política y manifiesto de confianza URL | Su backend | Derecho de negocio y mapeo de paquetes. No acepte estos de la aplicación. |
3. Instalar y ejecutar el SDK
Open the extracted ZIP's root folder in Android Studio. Use JDK 17, Android SDK 36 and Android Gradle Plugin 9.2.0; the included wrapper uses Gradle 9.4.1. Minimum device API is 23. Build dependencies require internet access. Select samples.phone and Run. Its initial values use the public demo backend and title; follow its sign-in and streaming controls before switching to your own backend.
# Windows
gradlew.bat :samples:phone:assembleDebug
# macOS / Linux
sh gradlew :samples:phone:assembleDebug
Para una aplicación existente, copie la carpeta ZIP maven/ en su raíz del proyecto. En settings.gradle.kts:
dependencyResolutionManagement {
repositories {
maven { url = uri(rootDir.resolve("maven")) }
google()
mavenCentral()
}
}
En el módulo de aplicación build.gradle.kts:
dependencies {
implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14")
}
Instalar el standalone AAR en su lugar
Ponga el AAR en el módulo de aplicación libs/. Un piso AAR no tiene una resolución de dependencia transitiva automática, así que agregue las tres dependencias iguales Media3 usted mismo. No instale ambos formularios.
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")En AndroidManifest.xml, permite el acceso a Internet y desactivar copias de seguridad para esta muestra mínima. En una aplicación de producción que necesita copias de seguridad, excluye específicamente los metadatos de licencias sin conexión con dispositivo en su lugar.
<uses-permission android:name="android.permission.INTERNET" />
<application android:allowBackup="false" android:usesCleartextTraffic="false" ... >
Busque la muestra de teléfono MainActivity.kt para drmx.sessionEndpoint, drmx.offlineEndpoint, drmx.contentId y customer.loginEndpoint. Cambia la configuración de caída marcada, o pasa estos extras de Intent de su aplicación después de iniciar sesión. Pase customer.accessToken en tiempo de ejecución. ZIP README explica cada parámetro y que ganchos de demostración para reemplazar.
Construir una prueba APK utilizando el AAR
Este ejemplo utiliza el proyecto de teléfono ejecutable de la completa SDK ZIP y vincula explícitamente la independiente AAR. El mismo enfoque funciona para una aplicación existente Android.
- Descargar y extraer el completo Android SDK ZIP. Abrir su carpeta raíz en Android Studio.
- Keep
libs/drmx-universal-player-1.0.0-beta.14.aarin the extracted root. Use JDK 17, Android SDK 36 and the included Gradle wrapper. - En
samples/phone/build.gradle.kts, sustitúyase la dependencia DRM-X Maven por las siguientes entradas dentrodependencies. Mantenga el resto de ese archivo de construcción.
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")
Quitar implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-beta.14") por lo que el mismo SDK no se incluye dos veces. Un piso AAR necesita las tres Media3 dependencias mostradas arriba.
- Para mantener la demo descargada instalada junto a su propia compilación de pruebas, cambie el módulo de teléfono
applicationIdacom.example.drmxtest. Deje su espacio de nombres sin cambios para esta muestra suministrada. - Construir el APK, luego instalarlo en un USB-conectado Android dispositivo con USB depuración autorizada.
# 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
Abra la aplicación de prueba y toque Play. La fuente de teléfono suministrada utiliza la misma demostración pública inicialmente. Para probar su propio título, sustitúyase los puntos finales marcados de sesión/offline/login y Publicado Content ID en MainActivity.kt, y suministre la autenticación de corta duración de su propio usuario en tiempo de ejecución. Su servidor mantiene el Site Key y Access Key.
Los controles compactos provienen de DrmXPlayerView en el AAR. La actividad suministrada demuestra Play-to-authorize, la rotación receptiva, la propiedad de descarga, la reproducción y renovación sin conexión. Ver el ejemplo de integración de control abajo al añadir esta vista a su propia aplicación.
4. Solicitud de servidor y política predeterminada#
La aplicación Android pide su backend para una sesión de reproducción. Su backend autentica al usuario, verifica el derecho, mapas Content ID a los medios publicados y llamadas DRM-X. Ver Playback Session API para el contrato de streaming completo.
{
"contentId": "YOUR-PUBLISHED-CONTENT-ID",
"contentType": "vod",
"drmSystem": "widevine",
"playbackMode": "streaming",
"platformCapabilities": {
"clientPlatform": "android",
"drmSystems": ["widevine"], "manifestTypes": ["dash"],
"maximumHeight": 1080, "secureDecoder": false, "persistentState": false
}
}
Las capacidades conservadoras arriba no afirman Widevine L1. Utilice el ayudante de capacidad SDK, o su propia evaluación de dispositivos, para dispositivos reales. Las capacidades del cliente ayudan a la selección de compatibilidad; nunca otorgan derecho de calidad.
El PHP muestra predeterminado a multi-tier-standard para la reproducción en línea. Seleccione su política de clave única documentada cuando su paquete utiliza una clave. Su política debe coincidir con el modelo clave del paquete y los derechos del espectador. Cambie la configuración del servidor o su configuración de entorno DRMX_LICENSE_POLICY_TEMPLATE, no un parámetro de consulta en el reproductor. Ver license policies.
playback_policy.persistent en verdadero en la política emitida por el servidor y elija las duraciones de licencia, alquiler, reproducción y renovación. La referencia utiliza 7 días/3 días/48 horas/7 días respectivamente; Estos son ejemplos de reglas comerciales, no la aprobación del estudio o un valor predeterminado en toda la plataforma. Devuelve metadatos coincidentes. La licencia Widevine sigue siendo autorizada.5. Path A: play through DrmXPlayer#
Dentro de su Actividad, crear un PlayerView, utilizar una ventana segura y llamar prepare después de iniciar sesión. El envoltorio intercambia la sesión, configura Widevine, aplica la altura máxima devuelta y libera la reserva de streaming cuando se detiene. Las importaciones vienen de com.haihaisoft.drmx.player y 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()
Registro DrmXPlayerListener para autorizar, listo, jugar y error UI. Mostrar códigos de error seguros en lugar de dumping fichas de licencia o cuerpos de respuesta. Utilice la muestra de teléfono incluida para el ciclo de vida completo, controles de pantalla completa y cola de descarga.
Controles de reproductores y rotación ininterrumpida #
Pulsa Juega en el vídeo para iniciar la autorización. Abrir la pantalla o Ajustes no adquiere una licencia de streaming. Pausa/resume, cambiando la configuración y girando el teléfono preserva la sesión actual.

| Control | Qué pueden hacer los clientes |
|---|---|
| Calidad | Elija Auto o una resolución admitida y licenciada del manifiesto. Las opciones no disponibles explican la restricción de licencia o dispositivo/DRM. Auto obedece las mismas restricciones. |
| Carril de audio | Elija cualquier idioma de audio compatible suministrado por el paquete. |
| Subtítulos/CC | Elija un idioma de capción o desactivado. El botón CC aparece cuando existen leyendas soportadas y las toggles directamente. |
| Aparecimiento de la capa | Ajuste la fuente, el color y el tamaño del texto, los colores de fondo/ventana y la opacidad. Las preferencias se guardan en el dispositivo. |
| Velocidad de reproducción / temporizador de sueño | Juega a 0.25–2× velocidad o pausa automáticamente después de un intervalo elegido. |
| Buscar / pantalla completa | Extiende la línea de tiempo, busca 10 segundos, o doble punción a cada lado. Girar al paisaje sin reiniciar la reproducción. |
Cada menú de vídeo incluye Move up / Move down y Download en línea o Delete sin conexión. Download se desactiva al completarse la descarga. El interruptor Downloaded videos selecciona la reproducción sin conexión; solo están disponibles las pistas almacenadas. No hay botones separados para descargar, eliminar o descargar toda la lista.
Copiar el registro al lado de la página de información copias recientes errores como texto, para que pueda enviar soporte los detalles sin una captura de pantalla. ID, operación, SDK/Android/versión de dispositivos, numérico CDM/Media3 errores, marcos de pila seleccionados, estado de conexión/descarga/playback y un recuento de repetición. credenciales, URLs de medios, claves, identificaciones clave, datos de licencia y completos CDM Idiotas.
La muestra conserva hasta 32 informes locales (64) KB) durante cinco horas. Cuando se conecta y se firma, carga errores pendientes al backend del cliente configurado, al menos una vez por 30 segundos. El servidor demo público conserva advertencias/errores durante cinco horas, combina eventos repetidos, tapa el archivo a 512 KB y ciruelas cada minuto. El progreso rutinario y la reproducción exitosa no se han registrado. Las integraciones personalizadas deben suministrar explícitamente su propio origen drmx.diagnosticsEndpoint; errores de diagnóstico nunca interrumpen la reproducción.
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()
El manifiesto del teléfono suministrado maneja cambios de orientación y tamaño de pantalla; su onConfigurationChanged redimensiona la misma Actividad y reproductor. Mantenga ambas partes al adaptarla. Un host que recrea Actividades debe retener a su propietario de reproducción y reajustar la vista. La muerte del proceso es diferente de la rotación y necesita la restauración del estado normal. Las opciones 4K/8K requieren contenido de combinación, derecho de licencia y soporte de dispositivo/DRM; el CDM remainsita
Para uso personal reproductores, parar y liberar el activo fuera de línea DRM sesión antes de comprobar, renovar o liberar la misma licencia persistente. SDK's player.stopForOfflineLicenseOperation { /* call manager here */ } espera por el Media3 cola de reproducción para liberar los medios. stop() colas que trabajan asincrónicamente. No realizar un separado checkLicense() mientras que la misma licencia está jugando; la reproducción misma valida su CDM estado. La muestra suministrada muestra las acciones completas de desactivación y superposición durante la renovación.
6. Adquirir una licencia y descargar los medios #
Prepara la reproducción sin conexión
Necesitas tanto la descarga completa como una licencia válida vinculada al dispositivo.
- Descarga el contenido cifrado
Mientras tengas conexión, descarga el vídeo y las pistas de audio y subtítulos que necesites.
- Obtén una licencia persistente
Mientras tengas conexión, obtén la licencia Widevine vinculada al dispositivo. Guarda su Key-set ID opaco de forma privada.
- Comprueba que todo esté listo
Confirma que la descarga esté completa y que la licencia sea válida.
- Reproduce sin conexión
Lee el contenido en caché y restaura la licencia local. La renovación y la liberación requieren conexión a 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 */ }
}
El SDK guarda el opaque Widevine conjunto de llaves ID y metadatos no secretos. El CDM tiene la licencia. No persiste DRM License Tokens, claves primas, retos o bytes de respuesta de la licencia. Excluir com.haihaisoft.drmx.offline.licenses.v1.xml de copias de seguridad.
Crear una propiedad de la aplicación Media3 SimpleCache, gestor de descarga y base de datos. Use DownloadHelper con la licencia MediaItem y select video dentro maximumHeight. En Media3 1.11 el callback preparado es onPrepared(helper, tracksInfoUpdated). La muestra de teléfono enqueueDownload y OfflineTestRuntime muestran la ejecución ejecutable.
// 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()
Descargue cada idioma que tenga la intención de exponer fuera de línea. Llamando createDownloadRequest sin teclas de flujo descarga todas las pistas, que es generalmente desperdicio para vídeo adaptativo. Mostrar progreso de descarga y esperar Download.STATE_COMPLETED. Para transferencias de fondo de producción, integre Media3 DownloadService, límites de almacenamiento y Android requisitos de servicio/notificación de primer plano; la cola de demostración funciona en proceso.
7. Sendero B: restaurar con su existente Media3 reproductor#
Lea la descarga completada y use sus teclas de flujo exactas. La descarga y la reproducción debe compartir la misma ubicación de caché y la asignación de claves. Este ejemplo deshabilita deliberadamente el acceso de arriba para la reproducción estricta offline:
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.
Para utilizar el mismo flujo sin conexión con Path A, construir DrmXPlayer con este mediaSourceFactory y llamar player.prepareOffline(playback). Restauración ordinaria utiliza la licencia local CDM sin solicitar una nueva. Una entrada de caché falta debe producir un error durante la aceptación desconectada, no cambiar silenciosamente a Internet.
8. Sendero C: nativo Media3, no DRM-X AAR#
No es necesario que utilice DrmXPlayer o DrmXOfflineManager. Agregue las tres dependencias Media3 del paso 3 e implemente el mismo contrato de backend del cliente. Utilice session.manifestUrl (no session.manifest.url), la licencia devuelta URL y los encabezados de autorización de corta duración. Aplique la altura máxima devuelta y controle la liberación de la sesión de transmisión en caso de detención, reemplazo, finalización, falla y destrucción.
La actividad nativa completa a continuación se compila como samples:native junto con esta SDK liberación. Maneja HTTPS, lecturas de la respuesta atada, autorización de fondo, respuestas a las etapas y la liberación de la reserva. Reemplazar el punto final marcado /Content ID y proporcionar su propia ficha de inicio de sesión. El ejemplo es un punto de partida; añadir su actualización de producción, conmutación de cuenta y ciclo de vida de aplicación.
Actividad nativa completa de streaming (Kotlin, no DRM-X dependencia)
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() }
}
}
}
Para licencias nativas fuera de línea, utilice OfflineLicenseHelper.downloadLicense, persiste su opaque key-set ID en privado y restaurar con MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).setKeySetId(id). El siguiente ayudante compilado cubre adquisición, cheques de duración, renovación, lanzamiento y descarga / juegos. Es no una aplicación completa de descarga: todavía posee el caché, servicio de descarga, progreso, metadata, unión de usuario/dispositivo y autorización de operación de confianza.
Ayudador persistente nativo (Kotlin, no DRM-X dependencia)
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())
}
}
Antes de llamar a este ayudante de bajo nivel, valide que la respuesta de backend fresco coincide con la Content ID solicitada, operación, usuario/dispositivo firmado y período de validez de autorización. Persiste un estable sin conexiónLicenseId y confiaba en los metadatos manifiesto/track junto al conjunto clave ID, nunca el token. Ejecutar bloqueo de ayuda llama a un hilo obrero. Para licencia HTTP headers use setLicenseRequestHeaders en la configuración de DRM; Widevine parámetros opcionales de recuperación de clave son un mapa diferente.
Las nuevas integraciones deben usarse AndroidX Media3 DRM apoyoLegacy com.google.android.exoplayer2 los proyectos deben seguir guía oficial de migración; no mezclar el legado y AndroidClases X o asumen que la muestra compila sin cambios en versiones anteriores.
9. Revisar, renovar, soltar y cambiar idiomas #
Estos son acciones separadas. Ejecute uno a la vez después de la sesión activa offline DRM ha sido liberado: use player.stopForOfflineLicenseOperation { /* one operation below */ } con DrmXPlayer, o suelte el nativo existente Media3 reproductor primero. No compruebe una licencia mientras está jugando. Un cheque es opcional para la duración de visualización; no debe evitar la renovación de una licencia vencida.
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 lee metadatos; checkLicense consultas autorizadas locales CDM duraciónes. Renovar/release requiere conectividad y una nueva autorización de operación específica de su backend. Eliminar los medios no libera la licencia. Cuando se desconecte, la cola de liberación para la próxima conexión en lugar de informar que el servidor lo ha confirmado. Cerrar el administrador cuando su propietario es destruido.
// 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()
Construir opciones de pistas disponibles y desactivar opciones indisponibles. Una preferencia no puede crear una pista de Inglés ausente del paquete. Los usuarios sin conexión sólo pueden seleccionar idiomas descargados. Preserve license/cache state across screen rotación; pantalla completa es un cambio de vista/ventana, no una razón para adquirir otra licencia.
10. Implementar el endpoint de autorización sin conexión#
La aplicación post contentId, contentType: "vod", drmSystem: "widevine", playbackMode: "offline", operation: "acquire" | "renew" | "release", offlineLicenseId (null for initial acquisition) and truthful platformCapabilities. Verifica que los IDs de renovación/renuncia pertenecen al usuario autenticado, dispositivo y contenido. Seleccione el lado del servidor de políticas, emita un nuevo DRM License Token y devuelva el contrato de coincidencia:
{
"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
}
}
Estos son marcadores de posición explicativos, no una respuesta de licencia reutilizable. Devuelva los valores frescos de la operación real. El SDK también admite integrationMode: "token-proxy" con un descriptor vinculado drm.authorization. Regresar Cache-Control: no-store; nunca log successful authorization bodies. Ver el descriptor SDK descargable OFFLINE_INTEGRATION.md y PHP muestra de punto final sin conexión para el manejo de solicitudes de concreto.
11. Prueba tus dispositivos y solución de problemas #
| Prueba / síntoma | Comportamiento esperado / siguiente cheque |
|---|---|
| SDK descarga devuelves 403 | Seleccione una organización con una suscripción profesional o Enterprise activa. Una actualización planificada después del juicio no desbloquea el SDK. |
| Sesión 401 / 403 | Check login, derecho, Medio Ambiente, Publicado Content ID y política. Nunca desvíes la autenticación. |
| Sin conexión falla pero las obras en línea | Compruebe CDM duración, descarga completa, ubicación de caché, teclas de flujo y todas las teclas de licencia requeridas. |
| Inglés / chino desaparecido | Comprueba los metadatos de idioma del paquete, las pistas reales y la selección de idiomas fuera de línea. |
| L1, HDCP o fracaso de decodificador | Comprobar hardware físico, provisiones, codec y requisitos de salida. Una cadena de capacidad no es certificación. |
| reinicio, rotación, actualización | Persistent license and cache survivor supported lifecycle changes without duplicate acquisition. Las copias de seguridad no deben migrar licencias a un nuevo dispositivo. |
| Modo de avión | Desactivar el Wi-Fi y los datos móviles también; reiniciar la aplicación, jugar localmente y cambiar cada idioma prometido. |
| Expirsión, presión de almacenamiento, interruptor de cuenta | Mostrar un estado indisponible/renovador claro, recuperar medios incompletos y hacer cumplir el acceso de usuario/dispositivo sin filtrar el catálogo sin conexión de otro usuario. |
Califique cada teléfono compatible, tableta, Android TV/Google TV, Fire TV o incrustado Android modelo para Widevine, decodificación segura, codec/resolución, HDCP, pantallas externas, cambios de reloj y larga desconexión. No cada dispositivo Android tiene las mismas capacidades DRM. El contenido de Hollywood requiere la protección y los requisitos de dispositivo del titular de derechos, no sólo decodificación exitosa.
Listas de reproducción y controles compartidos de Universal Player#
Los reproductores Android y Apple ofrecen Previous/Play-Pause/Next, CC, Settings y pantalla completa. Move up / Move down conserva el vídeo actual y guarda el orden de la lista para reproducir con y sin conexión. Las opciones de reproducción aleatoria y repetición se conservan al reiniciar; con ambas desactivadas, la reproducción termina tras el último elemento.
Proporcione el mismo cliente/viewer storageNamespace a DrmXPlayer y playerView.storageNamespace antes de configurar la lista de reproducción. VOD progreso se guarda periódicamente y en pausa / parada, luego restaurado para que Content ID en streaming o descargado de reproducción. La reproducción en vivo no utiliza una posición VOD guardada las mismas funciones shuffle/repeat/resume sin una fuente de medios de red.
Web y Android comparten panel oscuro y controles azules compactos. Admiten siguiente/anterior, aleatorio, repetir uno/todos y avance automático cancelable. Cada elemento requiere una nueva autorización. Capítulos y transcripción provienen de su catálogo.
Hay velocidad, temporizador de apagado, subtítulos, calidad permitida, audio, marcadores de sesión y bucle A–B. Web añade atajos, reanudación local opcional por espectador, búsqueda de transcripción y PiP compatible. Android mantiene descargas Widevine offline y renovación. La transmisión a otra pantalla y el progreso entre dispositivos requieren integración adicional.
Ajustes de los logotipos profesionales y empresariales
La marca se mantiene fuera de la superficie de vídeo. Abrir configuración → Acerca reproductor para el logotipo autorizado, información del producto y reproductor versión. Web también ofrece esta información sobre el clic derecho. La demo del teléfono público incluye WayMaker, Goodness of God, Fresh Oil y 4K Test Video; cada selección recibe su propia autorización.
En el backend de confianza configure playerBranding como {"mode":"hidden"} o {"mode":"custom","logoUrl":"https://your-site.example/logo.png","label":"Your brand"}. La plataforma comprueba Professional o Enterprise activos. Otros planes usan {"mode":"default"}; peticiones personalizadas u ocultas no autorizadas devuelven 403. Es independiente de la marca de agua de seguridad.
La descarga PHP incluye listas y configuración de marca editables. El ZIP Android incluye el ejemplo de callback. Autorice las listas en el backend y nunca incluya credenciales de cliente API en ninguno de los reproductores.
DRM-X 6.0