# SDK Android e Media3 nativo

Baixe o Android reprodutor com controles em vídeo, listas de reprodução arrastáveis, shuffle/repeat salvos e currículo offline. Integre reprodução protegida usando DRM-X ou nativo Media3.

**Escolha seu reprodutor, mantenha um serviço de licença.**Utilizar o DRM-X 6.0 Android SDK para uma integração guiada, ou manter seu nativo Media3 reprodutor. Ambos se conectam à sua infraestrutura autenticada e Widevine. A Android SDK suporta streaming e off-line persistente VOD em compatível API 23+ dispositivos, incluindo telefones, tablets, TVs e incorporados Android sistemas. 

## Tente o Android reprodutor [ # ](#try-android-app)



**Baixe, instale, pressione Play.**A aplicação de avaliação gratuita utiliza o nosso vídeo demo público e assina automaticamente. Não é necessário criar um APK ou inserir DRM-X credenciais para o experimentar. 

[Baixar a amostra Android APK ](https://docs.drm-x.com/downloads/android/1.0.0-preview.12/drmx-android-player-1.0.0-preview.12.apk)



**1.0.0-preview.12** · 7.02 MB · Android 6.0+ (API 23+) · DRM-X 6.0 Player · Atualizado 2026-09-14


- Abra esta página no seu telefone Android e baixe o APK.
- Abra o arquivo baixado. Se Android perguntar, permita a instalação do navegador ou gerenciador de arquivos que você usou, então toque em **Instalar **.
- Abrir **DRM-X 6.0 player**, mantenha-se ligado, e toque no vídeo **Reproduzir** botão. O aplicativo solicita uma licença de streaming quando você pressiona Play.
- Abrir **Configurações** para escolher a qualidade, áudio, legendas ou velocidade de reprodução. Rodar o telefone para continuar em tela cheia.
- Toque em **Baixar** para o vídeo atual ou **Obter lista de reprodução** para todos VOD itens. Os downloads usam a versão licenciada mais suportada até 1080p em L1, ou 480p em L3. **Reproduzir offline** abre a lista de reprodução local completa com próxima/anterior, embaralhamento, repetição e reprodução automática; **Ir em linha** retorna para a lista completa. **Renovar** renova a licença selecionada enquanto estiver conectado.



Os downloads de mídia já em fila de espera continuam em segundo plano. Se Android parar o aplicativo enquanto prepara licenças ou faixas, reabri-lo enquanto estiver conectado para terminar a preparação dos itens restantes. Os downloads existentes são retidos. A reprodução offline usa apenas mídia em cache; downloads incompletos ou expirados requerem conclusão ou renovação.



Widevine Falhas de provisionamento (Media3 6002) agora use um relé de demo-servidor autenticado para o serviço oficial do Google. L3 CDM pode receber SD chaves. Um dispositivo sem funcionamento Widevine a implementação necessita de um dispositivo/firmware fixo; uma licença não pode adicionar faltando DRM apoio.



A amostra APK é pública e assinada para avaliação. O AAR e o SDK completo abaixo permanecem disponíveis através de contas de Console elegíveis. Mantenha sua própria chave de assinatura de aplicativo de produção e login de infraestrutura separados desta demonstração.

APK checksum e USB instalação

SHA-256:

```
8fa044b82c0abab7ce2a987f349524fd7ad1e78f3c897771f4764cb2aa521180
```

Com ferramentas de plataforma Android e um dispositivo autorizado USB:

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

Instale este APK sobre a amostra oficial existente sem desinstalá- lo. A mesma chave de assinatura e aplicação ID preservar downloads, licenças offline, posições salvas e preferências. Se o seu próprio build usar outra chave de assinatura, use um aplicativo separado ID para manter ambos os aplicativos e preservar as licenças offline da amostra.





## O que há de novo em Android reprodutor preview.12[#](#latest-player-update)



Lançado em 14 de setembro de 2026. Corrige a reprodução transferida, fazendo uma solicitação de licença inesperada quando uma licença Widevine expirar ou tiver um minuto ou menos restante. Isto pode aparecer como Media3 erro `6004`, um erro HTTP enquanto estiver conectado, ou um erro DNS enquanto estiver desconectado.



A reprodutor agora restaura a licença baixada localmente. As licenças válidas mantêm o tempo restante de reprodução; as licenças expiradas mostram **Conectar e tocar em Renovar **. As regras de validade e proteção da licença continuam a ser aplicadas pelo dispositivo. Os diagnósticos também incluem o status numérico HTTP quando uma operação de rede falha, sem expor cabeçalhos de requisição, URLs ou respostas de licença.


- Instale preview.12 sobre o aplicativo oficial existente. Mantenha seus dados e downloads atuais do aplicativo.
- Se WayMaker ou outro vídeo baixado precisar de renovação, conecte-se à internet, selecione esse vídeo e toque em **Renew**.
- Aguarde por **Licença renovada. Pronto para jogar offline. **, então escolha **Jogue offline **. O vídeo criptografado permanece baixado.



Esta versão mantém os controles de antevisão.11 e as funcionalidades da lista de reprodução. A aplicação de avaliação pública e a sua antevisão incorporada reprodutor.12; a Consola separada distribuída SDK permanece pré-visualização.10 / bundle 20260910.8.





## O que há de novo em Android reprodutor preview.11[#](#player-preview11)



Lançado em 14 de setembro de 2026. Este aplicativo de avaliação inclui os mais recentes controles reprodutor e melhorias na lista de reprodução, com um estilo de interação consistente entre os nossos Android e Apple reprodutores.


- ** Controles dentro do vídeo:** centralizado Reproduzir/Pausa, Anterior e Seguinte para playlists multi-vídeo, CC, Configurações e um botão de tela cheia. Controles escondem-se após dois segundos durante a reprodução; toque no vídeo para revelá- los novamente. 
- **Capções e configurações de reprodução: ** use CC para mostrar ou esconder legendas disponíveis, e Configurações para escolher o idioma de legenda, áudio, velocidade de reprodução e qualidade permitida. As escolhas off-line usam apenas faixas incluídas no download. 
- **Drag to reorder:** arraste o punho de um item da lista de reprodução para alterar a sua posição. A ordem salva sobrevive a uma reiniciação de uma aplicação e é refletida na lista de reprodução baixada. A reordenação mantém o vídeo atual reproduzido. Toque na alça para as ações acessíveis Mover para cima / Descer. 
- ** Embaralhamento salvo e repetir:** os modos seleccionados permanecem realçados e sobrevivem ao reiniciar na reprodução online e offline. Com o embaralhamento e a repetição de ambos, os vídeos seguem a ordem gravada e param após o item final. A acção "Play next" do menu da lista de reprodução foi removida. 
- **Resuma onde você parou:** a posição de cada vídeo salvo VOD é restaurada quando você retorna à reprodução de streaming ou baixado. O progresso é salvo neste dispositivo para o visualizador atual; ele não sincroniza entre dispositivos. 



As alterações reprodutor foram verificadas em um Samsung S24 Ultra, incluindo arrastar playlist durante a reprodução, salvar o pedido após o relançamento, retomar a reprodução e baixar a reprodução com Wi-Fi e dados móveis desativados. A visualização offline ainda requer um download completo e uma licença off-line utilizável e não expirada.



Esta versão atualiza a avaliação pública APK. O Console SDK distribuído separadamente permanece `1.0.0-preview.10`, bundle `20260910.8`; suas instruções de integração existentes abaixo referem-se a esse pacote.





## 1. Escolha um caminho de integração[#](#choose-your-path)



[PATH A · QUICKEST START**DRM-X reprodutor**

Use o DrmXPlayer, a amostra de telefone e o gerenciador offline. Mantenha suas próprias regras de login e negócios.

](#android)[PATH B · KEEP YOUR UI**Media3 + DRM-X ajudantes**

Mantenha uma reprodutor existente e baixe a pilha. Deixe o DrmXOfflineManager gerenciar licenças persistentes.

](#existing-media3)[PATH C · NATIVE ONLY**Media3 sem AAR**

Implemente o mesmo contrato de infraestrutura e ciclo de vida da licença nativa você mesmo. No DRM-X reprodutor dependência.

](#configure-media3) 

[Transfira o ZIP ou AAR do SDK Android em Console → Integrations](https://6.drm-x.com/console/integrations). As organizações com **Professional ($499/mês) ou Enterprise** ativos podem transferi-lo. As contas Trial e Standard não podem transferir o SDK. Esta documentação, incluindo exemplos Media3 nativos, é pública; a reprodução exige uma conta autorizada, conteúdo publicado e um dispositivo compatível.



Current preview: `1.0.0-preview.10` · customer bundle `20260910.8` · Media3 `1.11.0`. The ZIP contains a local Maven repository, Gradle wrapper, annotated README, phone/TV/native samples, an installable phone sample APK in `apks/`, and offline integration notes. It contains no customer credentials or protected movies.



## 2. Prepare seu conteúdo e infraestrutura [#](#before-you-code)


- Empacote e publique um título de Widevine compatível com DASH no seu Ambiente DRM-X. Copie o valor exacto **Publicado Content ID**.
- Configurar o servidor Environment ID, Site Key e Access Key. Utilização [o download possível PHP integração](https://developer.drm-x.com/php-sample/) para um ponto de partida de trabalho.
- Implemente a verificação de login e direito do usuário. Seu aplicativo chama sua infraestrutura com seu token de usuário de curta duração; sua infraestrutura escolhe o conteúdo, política e URLs confiáveis.
- Execute a reprodução online primeiro em um dispositivo com capacidade física Widevine. Adicione a aquisição offline apenas após o contrato de streaming funcionar.



O vídeo criptografado e as licenças DRM seguem caminhos diferentes

A CDN entrega o vídeo criptografado. Seu backend verifica o acesso, e o serviço de licenças DRM autoriza a descriptografia em um dispositivo compatível.

- 01**Armazenamento / CDN**

Entrega a mídia criptografada ao player.

- 02**Seu backend**

Verifica se o espectador pode assistir e solicita a autorização de reprodução ao DRM-X.

- 03**DRM-X**

Valida a autorização de reprodução e processa a solicitação de licença DRM.

- 04**Player + dispositivo**

Usa o sistema DRM do dispositivo para obter uma licença e reproduzir a mídia criptografada.

Entrega de mídia e entrega de licença são separadas. Somente a infraestrutura do cliente detém DRM-X servidor credenciais. 

| Alterar este valor | Onde pertence | Significado | 
| --- | --- | --- |

| sessionEndpoint | Android | Seu endpoint de reprodução-sessão HTTPS, não o servidor de licença bruto URL. | 

| offlineEndpoint | Android, apenas offline | O seu ponto final de autorização de aquisição/renovação/lançamento. | 

| contentId | Android request; a infra- estrutura valida | O visualizador tem o direito de acesso protegido pelo Published Content ID. | 

| token de acesso ao cliente | Memória da aplicação após o login | Seu token de usuário de curta duração, nunca um Site Key ou Access Key. | 

| Environment ID, Site Key, Access Key | Apenas configuração secreta do servidor | DRM-X conta credenciaisNunca os envies numa nave. APK, HTML ou controlo da fonte. | 

| Política e manifesto de confiança URL | Seu backend | Direito de negócio e mapeamento de pacotes. Não aceite estes a partir do aplicativo. | 



## 3. Instalar e executar o SDK[#](#install-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 uma aplicação existente, copie a pasta ZIP `maven/` na sua raiz do projecto. Em `settings.gradle.kts`:



```
dependencyResolutionManagement {
    repositories {
        maven { url = uri(rootDir.resolve("maven")) }
        google()
        mavenCentral()
    }
}
```



No módulo do seu aplicativo `build.gradle.kts`:



```
dependencies {
    implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-preview.10")
}
```

Instalando o autônomo AAR em vez disso

Coloque o AAR no módulo do seu aplicativo `libs/`. Um plano AAR não tem resolução de dependência transitiva automática, então adicione as três dependências Media3 correspondentes. Não instale ambos os formulários.

```
implementation(files("libs/drmx-universal-player-1.0.0-preview.10.aar"))
implementation("androidx.media3:media3-exoplayer:1.11.0")
implementation("androidx.media3:media3-exoplayer-dash:1.11.0")
implementation("androidx.media3:media3-ui:1.11.0")
```



Em `AndroidManifest.xml`, permitir o acesso à Internet e desativar backups para esta amostra mínima. Em um aplicativo de produção que precisa de backups, especificamente excluir metadados de licença offline ligado ao dispositivo.



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



Procure o exemplo de telefone `MainActivity.kt` para `drmx.sessionEndpoint`, `drmx.offlineEndpoint`, `drmx.contentId` e `customer.loginEndpoint`. Altere as configurações de recuo marcadas, ou passe estes extras de Intenção de sua aplicação após o login. Passe `customer.accessToken` em tempo de execução. O ZIP README explica cada parâmetro e quais ganchos de demonstração para substituir.



## Construir um teste APK usando o AAR [ # ](#build-apk-from-aar)



Este exemplo usa o projeto de telefone executável a partir do SDK ZIP completo e explicitamente liga o autônomo AAR. A mesma abordagem funciona para uma aplicação Android existente.


- Download and extract the [full Android SDK ZIP](https://6.drm-x.com/console/integrations). Open its root folder in Android Studio.
- Keep `libs/drmx-universal-player-1.0.0-preview.10.aar` in the extracted root. Use JDK 17, Android SDK 36 and the included Gradle wrapper.
- Em `samples/phone/build.gradle.kts`, substitua a dependência DRM-X do Maven pelas seguintes entradas dentro `dependencies`. Mantenha o resto desse arquivo de compilação.



```
implementation(files(rootProject.file("libs/drmx-universal-player-1.0.0-preview.10.aar")))
implementation("androidx.media3:media3-exoplayer:1.11.0")
implementation("androidx.media3:media3-exoplayer-dash:1.11.0")
implementation("androidx.media3:media3-ui:1.11.0")
```



Remova `implementation("com.haihaisoft.drmx:drmx-universal-player:1.0.0-preview.10")` para que o mesmo SDK não seja incluído duas vezes. Um plano AAR precisa das três dependências Media3 mostradas acima.


- Para manter o demo baixado instalado ao lado do seu próprio test build, altere o módulo de telefone `applicationId` para `com.example.drmxtest`. Deixe seu espaço de nomes inalterado para esta amostra fornecida.
- Compilar o APK, em seguida, instalá-lo em um dispositivo USB- conectado Android com USB depuração 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
```



Abre a aplicação de teste e toque em Reproduzir. A fonte de telefone fornecida usa a mesma demonstração pública inicialmente. Para testar o seu próprio título, substitua os parâmetros de sessão/offline/login marcados e Published Content ID em `MainActivity.kt`, e forneça a autenticação de curta duração do seu próprio utilizador em tempo de execução. O seu servidor mantém o Site Key e Access Key.



Os controles compactos vêm de `DrmXPlayerView` no AAR. A Atividade fornecida demonstra a reprodução para autorização, rotação responsiva, propriedade de download, reprodução e renovação offline. Veja [ o exemplo de integração de controle ](#player-controls) abaixo ao adicionar esta visualização à sua própria aplicação.





## 4. Requisição do servidor e política padrão[#](#server-request)



A aplicação Android pede à sua infra- estrutura uma sessão de reprodução. A sua infra- estrutura autentica o utilizador, verifica o direito, os mapas Content ID para os media publicados e as chamadas DRM-X. Veja [Reproduzir sessão API](https://docs.drm-x.com/pt/api-reference/playback-sessions) para o contrato de transmissão 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
  }
}
```



As capacidades conservadoras acima não afirmam Widevine L1. Use o auxiliar de capacidade do SDK, ou sua própria avaliação de dispositivo, para dispositivos reais. As capacidades do cliente ajudam a seleção de compatibilidade; eles nunca concedem direito de qualidade.



A amostra PHP é por omissão `multi-tier-standard` para reprodução online. Seleccione a sua política de uma chave única documentada quando o seu pacote usar uma chave. A sua política deverá corresponder ao modelo da chave do pacote e aos direitos do visualizador. Mude a configuração do servidor ou a sua configuração do ambiente `DRMX_LICENSE_POLICY_TEMPLATE`, não um parâmetro de consulta no reprodutor. Veja a política de licenciamento [](https://docs.drm-x.com/pt/license-service/policies).



**Offline precisa de sua própria política persistente.**Definir `playback_policy.persistent` A referência usa 7 dias / 3 dias / 48 horas / 7 dias, respectivamente; estas são regras de exemplo de negócios, não aprovação de estúdio ou um padrão de plataforma. Devolva os metadados correspondentes. A licença Widevine permanece autorizada.



## 5. Caminho A: jogar através do DrmXPlayer[#](#android)



Dentro de sua Atividade, crie um `PlayerView`, use uma janela segura e chame `prepare` após o login. A embalagem troca a sessão, configura Widevine, aplica a altura máxima retornada e libera a reserva de streaming quando parada. As importações vêm de `com.haihaisoft.drmx.player` e 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()
```



Registre-se `DrmXPlayerListener` para autorizar, preparar, reproduzir e erro UI. Mostre códigos de erro seguros em vez de despejar tokens de licença ou corpos de resposta. Use a amostra de telefone incluída para o ciclo de vida completo, controles de tela cheia e fila de download.



## Controles do player e rotação ininterrupta[#](#player-controls)



Toque em **Reproduzir ** no vídeo para iniciar a autorização. Abrir a tela ou Configurações não adquire uma licença de streaming. Pause/resumir, alterando as configurações e girando o telefone preservar a sessão atual.



![DRM-X Android reprodutor Configurações em um Samsung S24 Ultra, mostrando qualidade, faixa de áudio, legendas/CC, Velocidade de reprodução e Temporizador de Sono.](https://docs.drm-x.com/assets/android-player-settings-preview4.png)Configurações reais em um Samsung S24 Ultra. Toque em uma linha para ver suas opções disponíveis; o submenu de áudio mostra nomes completos de idioma. Qualidade disponível e idiomas dependem do conteúdo, licença e dispositivo. 

| Controlo | O que os clientes podem fazer | 
| --- | --- |

| Qualidade | Escolha Auto ou uma resolução suportada e licenciada no manifesto. Opções indisponível explicam a restrição de licença ou dispositivo/DRM. Auto obedece às mesmas restrições. | 

| Faixa de áudio | Escolha qualquer idioma de áudio suportado fornecido pelo pacote. | 

| LegendasCC | Escolha um idioma de legenda ou Desligado. O botão CC aparece quando existem legendas suportadas e as ativa diretamente. | 

| Aparência da legenda | Ajuste a fonte, a cor e o tamanho do texto, as cores de fundo/janela e opacidade. As preferências são salvas no dispositivo. | 

| Velocidade de reprodução / temporizador de sono | Reproduzir em 0,25-2× velocidade ou pausa automaticamente após um intervalo escolhido. | 

| Procurar / tela cheia | Esfregue a linha do tempo, procure 10 segundos ou toque duas vezes em ambos os lados. Rodar para a paisagem sem reiniciar a reprodução. | 


As três ações compactas abaixo da imagem são **Download** (para uso offline), **Jogar offline** e **Renovar** (a licença offline). A amostra baixa uma versão de vídeo permitida e as faixas de áudio/subtítulo suportadas; menus offline expõem apenas mídia baixada. A renovação requer conectividade.



**O log de cópia** ao lado do rodapé da informação copia erros recentes como texto, para que você possa enviar suporte aos detalhes sem uma captura de tela. Os relatórios contêm um relatório ID, operação, SDK/Android/versão do dispositivo, erros numéricos CDM/Media3, quadros de pilha selecionados, conexão/download/estado de reprodução e uma contagem repetida. Excluem credenciais, URLs de mídia, chaves, IDs de chaves, dados de licença e despejos CDM completos.



A amostra mantém até 32 relatórios locais (64 KB) durante cinco horas. Quando conectado e conectado, ele envia erros pendentes para a infraestrutura configurada do cliente, no máximo uma vez por 30 segundos. O servidor público de demonstração mantém avisos/ erros por cinco horas, combina eventos repetidos, fecha o arquivo em 512 KB e poda a cada minuto. O progresso rotineiro e a reprodução bem- sucedida não são registrados. As integrações personalizadas devem fornecer explicitamente a sua própria origem `drmx.diagnosticsEndpoint`; erros de diagnóstico nunca interrompem a reprodução.



**A licença off-line já está em uso?**Preview.5 corrige o caso S24 que reportou CDM 368 com “Licença carregada em sessão diferente.” Pressionar Play offline novamente retoma o reprodutor existente. Renovar primeiro libera sua sessão DRM, em seguida, renova a licença armazenada. A licença de download e persistente são preservadas; não é necessária redefinição de dados de aplicativos. 

```
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()
```



O manifesto do telefone fornecido manipula as mudanças de orientação e tamanho do ecrã; o seu `onConfigurationChanged` redimensiona a mesma Actividade e reprodutor. Mantenha ambas as partes ao adaptá- la. Uma máquina que recria as Actividades deverá manter o seu proprietário de reprodução e religar a visualização. A morte do processo é diferente da rotação e necessita de uma restauração normal do estado. As opções 4K/8K requerem o mesmo conteúdo, direito à licença e suporte ao dispositivo/DRM; o CDM continua a ser autoritário.



Para uma licença personalizada reprodutores, pare e solte a sessão ativa offline DRM antes de verificar, renovar ou liberar a mesma licença persistente. A SDK `player.stopForOfflineLicenseOperation { /* call manager here */ }` espera que a fila de reprodução Media3 libere a mídia. As filas simples `stop()` que funcionam assíncronas. Não execute uma `checkLicense()` separada enquanto a mesma licença estiver a ser reproduzida; a reprodução em si valida o seu estado CDM. A amostra fornecida mostra o handoff completo e desactiva as ações sobrepostas durante a renovação.



## 6. Adquira uma licença e baixe a mídia[#](#offline-download)



Prepare a reprodução offline

São necessários tanto o download completo quanto uma licença válida vinculada ao dispositivo.

- 01**Baixe a mídia criptografada**

Enquanto estiver online, baixe o vídeo e as faixas de áudio e legendas necessárias.

- 02**Obtenha uma licença persistente**

Enquanto estiver online, obtenha a licença Widevine vinculada ao dispositivo. Armazene seu Key-set ID opaco de forma privada.

- 03**Verifique se tudo está pronto**

Confirme que o download está completo e que a licença é válida.

- 04**Reproduza offline**

Leia a mídia em cache e restaure a licença local. A renovação e a liberação exigem conexão com a internet.

Pronto off-line = mídia criptografada completa + uma licença válida ligada ao dispositivo. Nenhum dos dois substitui o outro. 

```
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 */ }
}
```



O SDK salva o conjunto de teclas Widevine opaco ID e metadados não secretos. O CDM detém a licença. Não persiste DRM License Tokens, chaves brutas, desafios ou bytes de resposta de licença. Excluir `com.haihaisoft.drmx.offline.licenses.v1.xml` das cópias de segurança.



Criar uma aplicação Media3 `SimpleCache`, gerenciador de download e banco de dados. Use `DownloadHelper` com o MediaItem licenciado e selecione vídeo dentro `maximumHeight`. In Media3 1.11 o callback preparado é `onPrepared(helper, tracksInfoUpdated)`A amostra do telefone. `enqueueDownload` e `OfflineTestRuntime` mostrar a implementação executável.



```
// 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()
```



Baixe cada idioma que você pretende expor offline. Chamando `createDownloadRequest` sem chaves de transmissão baixa todas as faixas, que geralmente é um desperdício para o vídeo adaptativo. Mostre o progresso do download e aguarde por `Download.STATE_COMPLETED`. Para transferências de fundo de produção, integre Media3 `DownloadService`, limites de armazenamento e Android requisitos de primeiro plano/notificação; a fila de demonstração é executada em processo.



**Conteúdo off-line de várias teclas:**Media3 restaura uma chave ID para um item de reprodução. Sua licença persistente deve conter todas as chaves necessárias para as faixas criptografadas de vídeo, áudio e outras. Teste isso com seu pacote real e política de servidor; habilitando várias sessões não combina licenças offline independentes.



## 7. Caminho B: restaurar com seu existente Media3 reprodutor [# ](#existing-media3)



Leia a transferência completa e use as suas teclas de transmissão exatas. A pilha de transferência e reprodução deverá partilhar a mesma localização da 'cache' e o mapeamento de chaves. Este exemplo desactiva deliberadamente o acesso a montante para uma reprodução off- line estrita:



```
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 usar o mesmo fluxo offline com o Caminho A, construa `DrmXPlayer` com este `mediaSourceFactory` e chame `player.prepareOffline(playback)`. A restauração ordinária usa a licença local CDM sem solicitar uma nova. Um item de cache ausente deve produzir um erro durante a aceitação desconectada, não mudar silenciosamente para a internet.



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



Você não é obrigado a usar o DrmXPlayer ou o DrmXOfflineManager. Adicione as três dependências Media3 do passo 3 e implemente o mesmo contrato de infraestrutura do cliente. Use `session.manifestUrl` (não `session.manifest.url`), a licença retornada URL e os cabeçalhos de autorização limitados. Aplique a altura máxima retornada e lide com a liberação da sessão de streaming na parada, substituição, fim, falha e destruição.



A Atividade nativa completa abaixo é compilada como `samples:native` ao lado desta versão SDK. Ela lida com HTTPS, leituras de resposta limitadas, autorização de fundo, respostas antigas e liberação de reservas. Substitua o endpoint/ Content ID marcado e forneça seu próprio token de login. O exemplo é um ponto de partida; adicione seu ciclo de vida de atualização de produção, mudança de conta e navegação de aplicativos.

Atividade completa de transmissão nativa (Kotlin, nenhuma dependência 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() }
        }
    }
}

```



Para licenciamento off-line nativo, use `OfflineLicenseHelper.downloadLicense`, persista no seu conjunto de chaves opaco ID em privado e restaure com `MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID).setKeySetId(id)`. O seguinte helper compilado cobre a aquisição, verificação de duração, renovação, lançamento e itens de download/reprodução. É ** não um aplicativo de download completo **: você ainda possui o cache, serviço de download, progresso, metadados, vinculação de usuário/dispositivo e autorização de operação confiável.

Ajudante nativo de licença persistente (Kotlin, sem dependência 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())
    }
}

```



Antes de chamar este helper de baixo nível, valide que a resposta nova da infra- estrutura corresponde ao Content ID solicitado, operação, período de validade do usuário/dispositivo e autorização assinado. Persista em um estável offlineLicenseId e metadados de manifesto/pista confiáveis ao lado do conjunto de chaves ID, nunca o token. Execute chamadas de helper de bloqueio em um tópico de worker. Para a licença, os cabeçalhos HTTP usam `setLicenseRequestHeaders` na configuração DRM; Widevine parâmetros de pedido de chaves opcionais são um mapa diferente.



Novas integrações devem ser utilizadas [AndroidX Media3 DRM apoio](https://developer.android.com/media/media3/exoplayer/drm)Legado `com.google.android.exoplayer2` os projectos devem seguir o [guia oficial de migração](https://developer.android.com/media/media3/exoplayer/migration-guide); não misture legado e AndroidClasses X ou assumir que a amostra compila inalterada contra versões mais antigas.



## 9. Verifique, renove, release and switch languages[#](#lifecycle-and-offline-behavior)



Estas são ações separadas. Execute uma de cada vez após a sessão offline ativa DRM ter sido lançada: use `player.stopForOfflineLicenseOperation { /* one operation below */ }` com o DrmXPlayer, ou libere primeiro o nativo Media3 reprodutor existente. Não verifique uma licença enquanto estiver em reprodução. Uma verificação é opcional para exibição de durações; não deve impedir a renovação de uma licença expirada.



```
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` lê metadados; `checkLicense` consultas de duração local autorizada CDM. Renovar/lançar requerem conectividade e uma nova autorização específica de operação da sua infra- estrutura. A exclusão de mídia não libera a licença. Quando desconectada, a liberação da fila para a próxima conexão em vez de informar que o servidor a confirmou. Feche o gerenciador quando seu proprietário for destruído.



```
// 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()
```



Constrói as opções das faixas disponíveis e desactiva as opções indisponíveis. Uma preferência não pode criar uma faixa inglesa ausente do pacote. Os utilizadores offline só podem seleccionar as línguas transferidas. Preservar a licença/estado de cache através da rotação da tela; a tela cheia é uma mudança de visualização/janela, não uma razão para adquirir outra licença.



## 10. Implementar o endpoint de autorização offline[#](#offline-backend-contract)



As mensagens da aplicação `contentId`, `contentType: "vod"`, `drmSystem: "widevine"`, `playbackMode: "offline"`, `operation: "acquire" | "renew" | "release"`, `offlineLicenseId` (null para aquisição inicial) e verdadeiro `platformCapabilities`. Verifique se os IDs de renovação/lançamento pertencem ao utilizador, dispositivo e conteúdo autenticados. Selecione o lado do servidor de políticas, emita um DRM License Token novo e devolva o contrato correspondente:



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



Estes são placeholders explicativos, não uma resposta de licença reutilizável. Devolva novos valores da operação real. SDK também suporta `integrationMode: "token-proxy"` com um limite `drm.authorization` descritor. Return `Cache-Control: no-store`; nunca registrar corpos de autorização bem sucedidos. Veja o download possível SDK's `OFFLINE_INTEGRATION.md` e PHP Endpoint off-line da amostra para o manuseio de pedidos de concreto.



## 11. Teste seus dispositivos e solução de problemas [#](#device-qualification)



### Widevine provisionamento e testes de rede da China



O provimento obtém o certificado do dispositivo antes da aquisição da licença de conteúdo.A partir da pré-visualização.9 a demonstração do telefone envia o pedido assinado do CDM através do seu relé autenticado do cliente HTTPS para o Google. A resposta assinada retorna inalterada. Isto ajuda quando o telefone não consegue alcançar diretamente o serviço de provisionamento do Google; o relé do cliente ainda deve ser acessível.



O Provisioning 2.0 utiliza uma chave de fábrica. O Provisioning 4.0 utiliza um modelo de credencial de dispositivo diferente. L1/L3 níveis de segurança, e reprodutor não os muda. L3 O telefone pode jogar enquanto outro telefone espera pelo provisionamento de dispositivos. As fontes oficiais públicas revisadas aqui não estabelecem que a China suporta apenas 2.0. [NXPGuia de integração do dispositivo](https://www.nxp.com/docs/en/user-guide/UG10158.pdf) e [AndroidAplicação do provisionamento](https://android.googlesource.com/platform/packages/apps/RemoteProvisioner/+/refs/tags/android-13.0.0_r15/src/com/android/remoteprovisioner/WidevineProvisioner.java).



Preview.10 relatórios de erro distinguem transporte direto ou relé, tempo limite/DNS/TLS/HTTP falha, HTTP estado e tempo decorrido. `provisioningModel: BootCertificateChain` indica a AOSP Modelo 4.0; `unknown` ou `other` não estabelece 2.0. PHP logs distinguem entradas rejeitadas de falhas no Google a montante. Relatórios excluem cargas de pagamento assinadas, URLs, credenciais e identificadores únicos do dispositivo.



Atualizar o APK no local, repetir o streaming e um download na rede afetada, em seguida, reter o log de cópia e o tempo aproximado. Os downloads e licenças existentes são retidos. Um relé não pode reparar não disponível Widevine ou credenciais rejeitado pelo dispositivo/Google. A aceitação real no telefone afetado é necessária.



Da antevisão.8, nativo L2/L3 ou nível desconhecido Widevine a reprodução está limitada a SD (até 576p) antes da descodificação. Android capacidades através da sua infra- estrutura: a sessão API reduz o software Widevine Licenças multi- chave para áudio/SD Chaves. PHP amostra preserva estes campos e também caps pedidos offline. SD rendition e uma política que permite software DRM são necessárias; as regras de segurança únicas e uniformes de uma única chave permanecem inalteradas. As configurações usam um painel translúcido sem escurecimento do vídeo. Numeric `cryptoErrorCode` está incluído em relatórios de erro seguros.



| Teste / sintoma | Comportamento esperado / próxima verificação | 
| --- | --- |

| A transferência SDK devolve 403 | Selecione uma organização com uma assinatura ativa Professional ou Enterprise. Uma atualização planejada após a avaliação não desbloqueia o SDK. | 

| Sessão 401 / 403 | Verificar login, direito, Ambiente, Publicado Content ID e política. Nunca ignorar a autenticação. | 

| Falha off-line, mas funciona online | Verifique as durações CDM, o download completo, a localização do cache, as chaves de fluxo e todas as chaves licenciadas necessárias. | 

| Inglês / Chinês em falta | Verifique os metadados da linguagem do pacote, as faixas reais e a seleção do idioma offline. | 

| L1, HDCP ou falha no descodificador | Verifique os requisitos de hardware físico, provisionamento, codec e saída. Uma string de capacidade não é certificação. | 

| Reiniciar, rodar, atualizar | Licença persistente e cache sobrevivem às mudanças suportadas no ciclo de vida sem aquisição duplicada. Os backups não devem migrar licenças para um novo dispositivo. | 

| Modo avião | Desativar Wi-Fi e dados móveis também; reinicie o aplicativo, jogue localmente e mude cada idioma prometido. | 

| Expiração, pressão de armazenamento, interruptor de conta | Mostrar um estado indisponível/renovar claro, recuperar mídia incompleta e forçar o acesso usuário/dispositivo sem vazar o catálogo offline de outro usuário. | 


Qualifique cada telefone, tablet, Android TV/Google TV, Fire TV ou modelo Android suportado para Widevine, decodificação segura, codec/resolução, HDCP, monitores externos, mudanças de relógio e desconexão longa. Nem todos os dispositivos Android têm as mesmas capacidades DRM. O conteúdo de Hollywood requer os requisitos de proteção e dispositivo do titular dos direitos, não apenas descodificação bem sucedida.



**Não há oportunidade de adquirir antes da desconexão?**Esta é uma implantação diferente: uma licença de serviço de licença de bordo qualificada emite licenças através da rede local durante a viagem. Ela não está habilitada apenas usando este SDK. DRM-X pode trabalhar com você em uma implantação personalizada cobrindo permissões de provedor, provisionamento, proteção de chaves e um teste de aceitação totalmente desconectado. Veja [a bordo DRM para aeronaves, autocarros, comboios e navios](https://multi-drm.drm-x.com/pt/solutions/on-board-drm).



## Universal Player listas de reprodução e controles compartilhados [#](#universal-player-playlists-and-shared-controls)



A Android preview.11 e mais tarde aplicativos de avaliação e atual Android/Apple fontes de desenvolvimento compartilhar em vídeo Anterior/Play-Pause/Next, CC, Configurações e controles de tela cheia com dois segundos de ocultação durante a reprodução. AndroidO menu da lista de reprodução já não inclui “Reproduzir o próximo”. As configurações selecionadas do shuffle/repeat permanecem destacadas e persistem ao longo dos reinícios. Com ambos desligados, a reprodução segue a ordem da lista de reprodução gravada e pára no vídeo final. Arraste os comandos para reordenar os itens sem reiniciar o vídeo atual; a ordem salva também se aplica à lista de reprodução baixada. Estas APIs de origem mais recentes ainda não estão incluídas na antevisão do Console. 10 SDK Um pacote.



Forneça o mesmo cliente/visualizador `storageNamespace` para `DrmXPlayer` e `playerView.storageNamespace` antes de configurar a lista de reprodução. O progresso VOD é gravado periodicamente e em pausa/paragem, sendo então restaurado para esse Content ID em transmissão ou reprodução transferida. A reprodução ao vivo não usa uma posição VOD gravada. Os downloads completados mantêm o mesmo comportamento de shuffle/repeat/resume sem uma fonte de mídia de rede. Este estado local do dispositivo não sincroniza o progresso entre dispositivos.



As edições Web e Android usam um painel de configurações escuras consistente com controles azuis compactos. As listas de reprodução suportam próximo/anterior, embaralhe, repita um/todos e cancelável avanço automático. Cada item usa uma nova solicitação de reprodução autorizada. Os metadados de capítulo e transcrição vêm do seu catálogo de conteúdo.



Velocidade de reprodução, temporizador de sono, legendas, qualidade permitida, idiomas de áudio, marcadores de sessão e looping A–B estão disponíveis. Web adiciona atalhos de teclado, currículo local visualizador opcional, pesquisa de transcrição e navegador compatível PiP. Android mantém downloads e renovação nativas Widevine offline. O progresso de carcaça e dispositivo cruzado requer integrações de aplicativos separadas.



### Configurações de logotipo profissional e empresarial



Branding fica fora da superfície do vídeo. Configurações abertas → Sobre reprodutor para o logotipo autorizado, informações do produto e versão reprodutor. Web também oferece esta informação no botão direito do mouse. A demonstração do telefone público inclui WayMaker, Bondade de Deus, Óleo Fresco e Vídeo de Teste 4K; cada seleção recebe sua própria autorização.



Configure `playerBranding` na sua infra- estrutura de confiança para `{"mode":"hidden"}` ou `{"mode":"custom","logoUrl":"https://your-site.example/logo.png","label":"Your brand"}`. A plataforma verifica a subscrição activa do Professional ou Enterprise. Outros planos usam `{"mode":"default"}`; requisições personalizadas/ ocultas não autorizadas devolvem 403. Isto é separado das marcas de água de segurança.



O download PHP inclui a configuração da lista de reprodução editável e da marca. O cliente Android ZIP inclui o exemplo de chamada da lista de reprodução. Mantenha a autorização da lista de reprodução na sua infra- estrutura; nunca incorpore API cliente credenciais em qualquer um dos reprodutor.

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