Descripción general de MediaPlayer

El framework de contenido multimedia de Android admite la reproducción de varios tipos de contenido multimedia comunes, de modo que puedas integrar fácilmente audio, video e imágenes en tus aplicaciones. Puedes reproducir audio o video desde archivos multimedia almacenados en los recursos de tu aplicación (recursos sin procesar), desde archivos independientes en el sistema de archivos o desde un flujo de datos que llega a través de una conexión de red, todo con las APIs de MediaPlayer.

En este documento, se muestra cómo usar MediaPlayer para escribir una aplicación de reproducción de contenido multimedia que interactúe con el usuario y el sistema a fin de obtener un buen rendimiento y una experiencia del usuario agradable. Como alternativa, es posible que quieras usar ExoPlayer, que es una biblioteca de código abierto personalizable que admite funciones de alto rendimiento que no están disponibles en MediaPlayer.

Nota: Solo puedes reproducir los datos de audio en el dispositivo de salida estándar. Por el momento, este es la bocina del dispositivo móvil o los auriculares Bluetooth. No puedes reproducir archivos de sonido en el audio de la conversación durante una llamada.

Conceptos básicos

Las siguientes clases se usan para reproducir sonido y video en el marco de trabajo de Android:

MediaPlayer
Esta clase es la API principal para reproducir sonido y video.
AudioManager
Esta clase administra fuentes y salidas de audio en un dispositivo.

Declaraciones del manifiesto

Antes de comenzar a desarrollar tu aplicación con MediaPlayer, asegúrate de que el manifiesto contenga las declaraciones adecuadas para permitir el uso de funciones relacionadas.

  • Permiso de Internet: si estás usando MediaPlayer para transmitir contenido basado en la red, tu aplicación debe solicitar acceso a la red.
    <uses-permission android:name="android.permission.INTERNET" />
    
  • Permiso de bloqueo de activación: Si la aplicación del reproductor necesita evitar que la pantalla se atenúe o que el procesador entre en suspensión, o si usa los métodos MediaPlayer.setScreenOnWhilePlaying() o MediaPlayer.setWakeMode(), debes solicitar este permiso.
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

Cómo usar MediaPlayer

Uno de los componentes más importantes del framework de medios es la clase MediaPlayer. Un objeto de esta clase puede recuperar, decodificar y reproducir audio y video con una configuración mínima. Es compatible con varias fuentes de medios diferentes, entra las que se incluyen las siguientes:

  • Recursos locales
  • URI internos, como uno que se pueda obtener de un agente de resolución de contenido
  • URL externas (transmisión)

Para obtener una lista de los formatos multimedia compatibles con Android, consulta la página Formatos de medios compatibles.

A continuación, se muestra un ejemplo de cómo reproducir un audio que está disponible como recurso local sin procesar (guardado en el directorio res/raw/ de tu aplicación):

Kotlin

var mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1)
mediaPlayer.start() // no need to call prepare(); create() does that for you

Java

MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you

En este caso, un recurso "sin procesar" es un archivo que el sistema no intenta analizar de ninguna manera en particular. Sin embargo, el contenido de este recurso no debe ser audio sin procesar. Debe ser un archivo multimedia con el formato y la codificación correctos en uno de los formatos compatibles.

A continuación, se muestra cómo puedes reproducir desde un URI disponible de forma local en el sistema (que obtuviste mediante un agente de resolución de contenido, por ejemplo):

Kotlin

val myUri: Uri = .... // initialize Uri here
val mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(applicationContext, myUri)
    prepare()
    start()
}

Java

Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

La reproducción desde una URL remota a través de la transmisión HTTP tiene el siguiente aspecto:

Kotlin

val url = "http://........" // your URL here
val mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(url)
    prepare() // might take long! (for buffering, etc)
    start()
}

Java

String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

Nota: Si pasas una URL para transmitir un archivo multimedia en línea, el archivo debe poder descargarse de forma progresiva.

Precaución: Debes capturar o pasar IllegalArgumentException y IOException cuando uses setDataSource(), ya que es posible que el archivo al que haces referencia no exista.

Preparación asíncrona

En principio, usar MediaPlayer puede ser sencillo. Sin embargo, es importante tener en cuenta que se necesitan algunos pasos más para integrarlo correctamente con una aplicación para Android típica. Por ejemplo, la llamada a prepare() puede tardar mucho tiempo en ejecutarse, ya que puede implicar la recuperación y decodificación de datos multimedia. Por lo tanto, como en el caso de cualquier método que pueda tardar mucho en ejecutarse, nunca debes llamarlo desde el subproceso de IU de tu aplicación. Si lo haces, la IU se bloqueará hasta que se muestre el método, lo que constituye una experiencia del usuario muy mala y puede causar un error de ANR (Aplicación no responde). Incluso si esperas que tu recurso se cargue rápidamente, recuerda que cualquier acción que tarde más de una décima de segundo en responder en la IU provoca una pausa notable y le da al usuario la impresión de que la aplicación es lenta.

Para evitar colgar el subproceso de IU, genera otro subproceso a fin de preparar el MediaPlayer y notificar al subproceso principal cuando hayas terminado. Sin embargo, si bien puedes escribir la lógica de subprocesos, este patrón es tan común cuando se usa MediaPlayer que el framework proporciona una forma conveniente de realizar esta tarea con el método prepareAsync(). Este método comienza a preparar los medios en segundo plano y los muestra de inmediato. Cuando termina la preparación de los medios, se llama al método onPrepared() de MediaPlayer.OnPreparedListener, configurado a través de setOnPreparedListener().

Cómo administrar estados

Otro aspecto de un MediaPlayer que debes tener en cuenta es que se basa en el estado. Es decir, MediaPlayer tiene un estado interno que siempre debes tener en cuenta cuando escribes el código, ya que ciertas operaciones solo son válidas cuando el reproductor está en estados específicos. Si realizas una operación en el estado incorrecto, el sistema puede generar una excepción o causar otros comportamientos no deseados.

En la documentación de la clase MediaPlayer, se muestra un diagrama de estado completo en el que se aclara qué métodos mueven la MediaPlayer de un estado a otro. Por ejemplo, cuando creas un MediaPlayer nuevo, se encuentra en el estado Inactivo. En ese momento, debes inicializarlo llamando a setDataSource(), lo que cambia su estado a Initialized. Luego, debes prepararlo con el método prepare() o prepareAsync(). Cuando MediaPlayer termina de prepararse, entra en el estado Prepared, lo que significa que puedes llamar a start() para que reproduzca el contenido multimedia. En ese momento, como se muestra en el diagrama, puedes moverte entre los estados Started, Pausado y PlaybackCompleted llamando a métodos como start(), pause() y seekTo(), entre otros. Sin embargo, cuando llames a stop(), ten en cuenta que no podrás volver a llamar a start() hasta que vuelvas a preparar la MediaPlayer.

Siempre ten en cuenta el diagrama de estado cuando escribas código que interactúe con un objeto MediaPlayer, ya que llamar a sus métodos desde el estado incorrecto es una causa común de errores.

Cómo retirar MediaPlayer

Un MediaPlayer puede consumir recursos valiosos del sistema. Por lo tanto, siempre debes tomar precauciones adicionales para asegurarte de no conservar una instancia MediaPlayer más de lo necesario. Cuando termines, siempre debes llamar a release() para asegurarte de que todos los recursos del sistema asignados se liberen de forma correcta. Por ejemplo, si usas un objeto MediaPlayer y tu actividad recibe una llamada a onStop(), debes liberar el MediaPlayer, ya que no tiene sentido mantenerlo mientras tu actividad no interactúa con el usuario (a menos que reproduzcas contenido multimedia en segundo plano, lo cual se analiza en la siguiente sección). Por supuesto, cuando se reanude o reinicie tu actividad, deberás crear un MediaPlayer nuevo y volver a prepararlo antes de reanudar la reproducción.

A continuación, se muestra cómo debes retirar y, luego, anular tu MediaPlayer:

Kotlin

mediaPlayer?.release()
mediaPlayer = null

Java

mediaPlayer.release();
mediaPlayer = null;

Como ejemplo, considera los problemas que podrían ocurrir si olvidas liberar el elemento MediaPlayer cuando se detiene la actividad, pero crea uno nuevo cuando la actividad vuelve a comenzar. Como ya debes saber, cuando el usuario cambia la orientación de la pantalla (o cambia la configuración del dispositivo de otra manera), el sistema se encarga de eso reiniciando la actividad (de forma predeterminada), por lo que podrías consumir rápidamente todos los recursos del sistema a medida que el usuario rota el dispositivo hacia adelante y hacia atrás entre el modo vertical y el horizontal, ya que en cada cambio de orientación se crea un nuevo MediaPlayer que nunca liberas. (Para obtener más información sobre los reinicios durante el tiempo de ejecución, consulta Cómo controlar los cambios en el tiempo de ejecución).

Quizás te preguntes qué sucede si quieres continuar reproduciendo "contenido multimedia en segundo plano" incluso cuando el usuario abandona tu actividad, de manera similar a como se comporta la aplicación de música integrada. En este caso, lo que necesitas es un MediaPlayer controlado por un servicio, como se explica en la siguiente sección.

Cómo usar MediaPlayer en un servicio

Si deseas que tu contenido multimedia se reproduzca en segundo plano incluso cuando tu aplicación no está en pantalla, es decir, deseas que se siga reproduciendo mientras el usuario interactúa con otras aplicaciones, debes iniciar un servicio y controlar la instancia MediaPlayer desde allí. Debes incorporar el MediaPlayer en un servicio MediaBrowserServiceCompat y hacer que interactúe con un MediaBrowserCompat en otra actividad.

Debes tener cuidado con esta configuración de cliente/servidor. Existen expectativas sobre la forma en que un jugador que se ejecuta en un servicio en segundo plano interactúa con el resto del sistema. Si tu aplicación no cumple con esas expectativas, el usuario podría tener una mala experiencia. Consulta Cómo compilar una app de audio para obtener todos los detalles.

En esta sección, se incluyen instrucciones especiales para administrar un MediaPlayer cuando se implementa dentro de un servicio.

Ejecución asíncrona

En primer lugar, al igual que con Activity, todo el trabajo en un Service se realiza en un solo subproceso de forma predeterminada. De hecho, si ejecutas una actividad y un servicio desde la misma aplicación, se usa el mismo subproceso (el "subproceso principal") de forma predeterminada. Por lo tanto, los servicios deben procesar intents entrantes con rapidez y nunca realizar cálculos largos cuando responden a ellos. Si se espera algún trabajo pesado o llamadas de bloqueo, debes realizar esas tareas de forma asíncrona, ya sea desde otro subproceso que implementes por tu cuenta o usando las numerosas herramientas del framework para el procesamiento asíncrono.

Por ejemplo, cuando uses un MediaPlayer desde tu subproceso principal, deberás llamar a prepareAsync() en lugar de prepare() e implementar un MediaPlayer.OnPreparedListener para recibir una notificación cuando se complete la preparación y puedas comenzar a reproducir contenido. Por ejemplo:

Kotlin

private const val ACTION_PLAY: String = "com.example.action.PLAY"

class MyService: Service(), MediaPlayer.OnPreparedListener {

    private var mMediaPlayer: MediaPlayer? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        ...
        val action: String = intent.action
        when(action) {
            ACTION_PLAY -> {
                mMediaPlayer = ... // initialize it here
                mMediaPlayer?.apply {
                    setOnPreparedListener(this@MyService)
                    prepareAsync() // prepare async to not block main thread
                }

            }
        }
        ...
    }

    /** Called when MediaPlayer is ready */
    override fun onPrepared(mediaPlayer: MediaPlayer) {
        mediaPlayer.start()
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final String ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mediaPlayer = ... // initialize it here
            mediaPlayer.setOnPreparedListener(this);
            mediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

Cómo manejar errores asíncronos

En operaciones síncronas, los errores normalmente se señalan con una excepción o un código de error, pero siempre que uses recursos asíncronos, debes asegurarte de que tu aplicación reciba la notificación de los errores de manera adecuada. En el caso de un MediaPlayer, puedes lograrlo implementando un MediaPlayer.OnErrorListener y configurándolo en tu instancia de MediaPlayer:

Kotlin

class MyService : Service(), MediaPlayer.OnErrorListener {

    private var mediaPlayer: MediaPlayer? = null

    fun initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer?.setOnErrorListener(this)
    }

    override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Es importante recordar que, cuando se produce un error, el MediaPlayer pasa al estado Error (consulta la documentación de la clase MediaPlayer para ver el diagrama de estado completo) y debes restablecerlo antes de volver a usarlo.

Cómo usar bloqueos de activación

Cuando se diseñan aplicaciones que reproducen contenido multimedia en segundo plano, el dispositivo puede suspenderse mientras se ejecuta tu servicio. Como el sistema Android intenta ahorrar batería mientras el dispositivo está suspendido, intenta desactivar cualquiera de las funciones del teléfono que no son necesarias, como la CPU y el hardware Wi-Fi. Sin embargo, si el servicio está reproduciendo o transmitiendo música, debes evitar que el sistema interfiera en la reproducción.

Para garantizar que tu servicio continúe ejecutándose en esas condiciones, debes usar "bloqueos de activación". Un bloqueo de activación es una forma de indicarle al sistema que tu aplicación está usando alguna función que debería permanecer disponible incluso si el teléfono está inactivo.

Aviso: Siempre debes usar los bloqueos de activación con moderación y retenerlos solo durante el tiempo que sea realmente necesario, ya que reducen significativamente la duración de la batería del dispositivo.

Para asegurarte de que la CPU siga funcionando mientras se reproduce tu MediaPlayer, llama al método setWakeMode() cuando inicialices tu MediaPlayer. Una vez que lo hagas, MediaPlayer retiene el bloqueo especificado durante la reproducción y libera el bloqueo cuando se pausa o se detiene:

Kotlin

mediaPlayer = MediaPlayer().apply {
    // ... other initialization here ...
    setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}

Java

mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

Sin embargo, el bloqueo de activación de este ejemplo solo garantiza que la CPU permanezca activa. Si transmites contenido multimedia a través de la red y usas Wi-Fi, es probable que también quieras mantener un WifiLock, que debes adquirir y liberar de forma manual. Por lo tanto, cuando comiences a preparar el MediaPlayer con la URL remota, deberás crear y adquirir el bloqueo de Wi-Fi. Por ejemplo:

Kotlin

val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
    wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")

wifiLock.acquire()

Java

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

Cuando pausas o detienes el contenido multimedia, o cuando ya no necesitas la red, debes liberar el bloqueo:

Kotlin

wifiLock.release()

Java

wifiLock.release();

Cómo realizar la limpieza

Como se mencionó antes, un objeto MediaPlayer puede consumir una cantidad significativa de recursos del sistema, por lo que debes conservarlo solo el tiempo que necesites y llamar a release() cuando termines. Es importante llamar a este método de limpieza de forma explícita en lugar de depender de la recolección de elementos no utilizados del sistema, ya que podría pasar un tiempo hasta que el recolector de elementos no utilizados reclame el MediaPlayer, ya que solo es sensible a las necesidades de memoria y no a la escasez de otros recursos relacionados con el contenido multimedia. Por lo tanto, en el caso de que uses un servicio, siempre debes anular el método onDestroy() para asegurarte de liberar el MediaPlayer:

Kotlin

class MyService : Service() {

    private var mediaPlayer: MediaPlayer? = null
    // ...

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
    }
}

Java

public class MyService extends Service {
   MediaPlayer mediaPlayer;
   // ...

   @Override
   public void onDestroy() {
       super.onDestroy();
       if (mediaPlayer != null) mediaPlayer.release();
   }
}

Siempre debes buscar otras oportunidades para liberar tu MediaPlayer, además de retirarlo cuando se cierra. Por ejemplo, si esperas no poder reproducir contenido multimedia por un período prolongado (después de perder el foco de audio, por ejemplo), sin duda debes liberar tu MediaPlayer existente y volver a crearlo más tarde. Por otro lado, si solo esperas detener la reproducción por un tiempo muy breve, es probable que debas conservar tu MediaPlayer para evitar la sobrecarga que se produce cuando se crea y prepara de nuevo.

Administración de derechos digitales (DRM)

A partir de Android 8.0 (nivel de API 26), MediaPlayer incluye APIs que admiten la reproducción de material protegido por DRM. Son similares a la API de bajo nivel que proporciona MediaDrm, pero funcionan a un nivel superior y no exponen los objetos de extractor, drm y criptografía subyacentes.

Aunque 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/control remoto protegidos por Widevine

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

Para administrar el contenido multimedia controlado por DRM, debes incluir los métodos nuevos junto con el flujo habitual de llamadas de MediaPlayer, como se muestra a continuación:

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 configura 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 con setOnDrmConfigHelper().
  2. Llamar a prepare()
  3. Llamar 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. Prepara la configuración de DRM para la fuente actual llamando a prepareDrm().
    • Si creaste y registraste una devolución de llamada OnDrmConfigHelper, se la llama mientras se ejecuta 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 red.
  3. Para obtener un array 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 recibida del 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 restablecer las claves en una sesión nueva.
    • Si la respuesta es para una solicitud de transmisión o retiro, el resultado es nulo.

Cómo ejecutar prepareDrm() de manera 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() maneja de forma interna, y puede tardar un poco en completarse debido a la operación de red involucrada. Para evitar el bloqueo en prepareDrm(), define y configura un MediaPlayer.OnDrmPreparedListener.

Cuando configuras un OnDrmPreparedListener, prepareDrm() realiza el aprovisionamiento (si es necesario) y la preparación en segundo plano. Cuando finalizan el aprovisionamiento y la preparación, se llama al objeto de escucha. No debes hacer ninguna suposición sobre la secuencia de llamada o el subproceso en el que se ejecuta el objeto de escucha (a menos que esté registrado con un subproceso de controlador). Se 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 a continuación:

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 administrar medios encriptados

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 a 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.

Cómo recuperar contenido multimedia de un ContentResolver

Otra función que puede ser útil en una app de reproducción multimedia es la capacidad de recuperar música que el usuario tiene en el dispositivo. Puedes hacerlo consultando los medios externos en el ContentResolver:

Kotlin

val resolver: ContentResolver = contentResolver
val uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor: Cursor? = resolver.query(uri, null, null, null, null)
when {
    cursor == null -> {
        // query failed, handle error.
    }
    !cursor.moveToFirst() -> {
        // no media on the device
    }
    else -> {
        val titleColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE)
        val idColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID)
        do {
            val thisId = cursor.getLong(idColumn)
            val thisTitle = cursor.getString(titleColumn)
            // ...process entry...
        } while (cursor.moveToNext())
    }
}
cursor?.close()

Java

ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
    // query failed, handle error.
} else if (!cursor.moveToFirst()) {
    // no media on the device
} else {
    int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
    int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
    do {
       long thisId = cursor.getLong(idColumn);
       String thisTitle = cursor.getString(titleColumn);
       // ...process entry...
    } while (cursor.moveToNext());
}

Para usar esta función con el MediaPlayer, puedes hacer lo siguiente:

Kotlin

val id: Long = /* retrieve it from somewhere */
val contentUri: Uri =
    ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id )

mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(applicationContext, contentUri)
}

// ...prepare and start...

Java

long id = /* retrieve it from somewhere */;
Uri contentUri = ContentUris.withAppendedId(
        android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);

mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(getApplicationContext(), contentUri);

// ...prepare and start...

Más información

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