Cómo trabajar con MediaPlayer y la administración de derechos digitales (DRM)

A partir de Android 8.0 (nivel de API 26), MediaPlayer incluye APIs que son compatibles con la reproducción de material protegido por DRM. Las APIs de DRM de MediaPlayer son similares a las APIs de bajo nivel que proporciona MediaDrm, pero funcionan a un nivel más alto y no exponen a los objetos de extractor, DRM y criptografía subyacentes.

Si bien la API de DRM de MediaPlayer no proporciona la funcionalidad completa de MediaDrm, admite los casos de uso más comunes. La implementación actual puede controlar los siguientes tipos de contenido:

  • Archivos de medios locales protegidos por Widevine
  • Archivos multimedia de transmisión o control remoto protegidos por Widevine

En el siguiente fragmento de código, se demuestra cómo usar los nuevos métodos MediaPlayer de DRM en una implementación síncrona.

Para administrar medios controlados por DRM, debes incluir los nuevos métodos junto con el flujo habitual de llamadas de MediaPlayer, como se muestra en este ejemplo:

Kotlin

mediaPlayer?.apply {
    setDataSource()
    setOnDrmConfigHelper() // optional, for custom configuration
    prepare()
    drmInfo?.also {
        prepareDrm()
        getKeyRequest()
        provideKeyResponse()
    }

    // MediaPlayer is now ready to use
    start()
    // ...play/pause/resume...
    stop()
    releaseDrm()
}

Java

setDataSource();
setOnDrmConfigHelper(); // optional, for custom configuration
prepare();
if (getDrmInfo() != null) {
  prepareDrm();
  getKeyRequest();
  provideKeyResponse();
}

// MediaPlayer is now ready to use
start();
// ...play/pause/resume...
stop();
releaseDrm();

Para comenzar, inicializa el objeto MediaPlayer y establece su fuente con setDataSource(), como de costumbre. Luego, para usar DRM, sigue estos pasos:

  1. Si quieres que tu app realice una configuración personalizada, define una interfaz OnDrmConfigHelper y adjúntala al reproductor mediante setOnDrmConfigHelper().
  2. Llama a prepare().
  3. Llama a getDrmInfo(). Si la fuente tiene contenido DRM, el método muestra un valor no nulo de MediaPlayer.DrmInfo.

Si MediaPlayer.DrmInfo existe:

  1. Examina el mapa de UUID disponibles y elige uno.
  2. Llama a prepareDrm() para preparar la configuración de DRM para la fuente actual.
    • Si creaste y registraste una devolución de llamada de OnDrmConfigHelper, se la llama mientras se está ejecutando prepareDrm(). Esto te permite realizar una configuración personalizada de las propiedades de DRM antes de abrir la sesión de DRM. Se realiza la devolución de llamada de manera síncrona en el subproceso que llamó a prepareDrm(). Para acceder a las propiedades de DRM, llama a getDrmPropertyString() y setDrmPropertyString(). Evita realizar operaciones prolongadas.
    • Si aún no se aprovisionó el dispositivo, prepareDrm() también accede al servidor de aprovisionamiento para hacerlo. La duración de esta operación puede variar según la conectividad de la red.
  3. Para obtener un arreglo de bytes de solicitud de clave opaca para enviar a un servidor de licencias, llama a getKeyRequest().
  4. Para informar al motor de DRM sobre la respuesta clave que recibió el servidor de licencias, llama a provideKeyResponse(). El resultado depende del tipo de solicitud de clave:
    • Si la respuesta es para una solicitud de clave sin conexión, el resultado es un identificador de conjunto de claves. Puedes usar este identificador de conjunto de claves con restoreKeys() para restaurar las claves en una nueva sesión.
    • Si la respuesta es para una solicitud de transmisión o lanzamiento, el resultado es nulo.

Prepara la DRM de forma asíncrona

De forma predeterminada, prepareDrm() se ejecuta de forma síncrona y se bloquea hasta que finaliza la preparación. Sin embargo, la primera preparación de DRM en un dispositivo nuevo también puede requerir aprovisionamiento, que prepareDrm() administra de forma interna y puede tardar un poco en completarse debido a la operación de red involucrada. Puedes evitar el bloqueo en prepareDrm() definiendo y configurando un MediaPlayer.OnDrmPreparedListener.

Establece un OnDrmPreparedListener. prepareDrm() realiza el aprovisionamiento (si es necesario) y la preparación en segundo plano. Cuando finalizan el aprovisionamiento y la preparación, el sistema llama al objeto de escucha. No hagas ninguna suposición sobre la secuencia de llamada o el subproceso en el que se ejecuta el objeto de escucha (a menos que lo registres con un subproceso de controlador). El sistema puede llamar al objeto de escucha antes o después de que se muestre prepareDrm().

Cómo configurar la DRM de forma asíncrona

Puedes inicializar la DRM de forma asíncrona creando y registrando el MediaPlayer.OnDrmInfoListener para la preparación de DRM y el MediaPlayer.OnDrmPreparedListener para iniciar el reproductor. Funcionan junto con prepareAsync(), como se muestra en este ejemplo:

Kotlin

setOnPreparedListener()
setOnDrmInfoListener()
setDataSource()
prepareAsync()
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
override fun onDrmInfo(mediaPlayer: MediaPlayer, drmInfo: MediaPlayer.DrmInfo) {
    mediaPlayer.apply {
        prepareDrm()
        getKeyRequest()
        provideKeyResponse()
    }
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
override fun onPrepared(mediaPlayer: MediaPlayer) {
    mediaPlayer.start()
}

Java

setOnPreparedListener();
setOnDrmInfoListener();
setDataSource();
prepareAsync();
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
onDrmInfo() {
  prepareDrm();
  getKeyRequest();
  provideKeyResponse();
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
onPrepared() {

start();
}

Cómo controlar el contenido multimedia encriptado

A partir de Android 8.0 (nivel de API 26), MediaPlayer también puede desencriptar el esquema de encriptación común (CENC) y los medios encriptados de nivel de muestra HLS (MÉTODO=MUESTRA-AES) para los tipos de transmisión elemental H.264 y AAC. Antes, los medios encriptados de segmento completo (MÉTODO=AES-128) eran compatibles.

Más información

Jetpack Media3 es la solución recomendada para la reproducción de contenido multimedia en tu app. Obtén más información al respecto.

En estas páginas, se analizan temas relacionados con la grabación, el almacenamiento y la reproducción de audio y video: