Descripción general de MediaPlayer

El marco de trabajo de contenido multimedia de Android admite la reproducción de diversos tipos de contenido multimedia comunes para que puedas integrar audio, video e imágenes con facilidad en tus apps. Puedes reproducir audio o video desde archivos multimedia almacenados en los recursos de tu app (recursos sin procesar), desde archivos independientes del sistema de archivos o desde un flujo de datos que llega a través de una conexión de red, todo mediante diferentes API de MediaPlayer.

En este documento, se muestra cómo escribir una app de reproducción de contenido multimedia que interactúa con el usuario y el sistema para obtener un buen rendimiento y una experiencia del usuario agradable.

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 app 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 app debe solicitar acceso a la red.
        <uses-permission android:name="android.permission.INTERNET" />
        
  • Permiso de bloqueo de activación: si la app del reproductor debe evitar que la pantalla se oscurezca o que el procesador entre en suspensión, o si utiliza 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 marco de trabajo 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 de medios compatibles con Android, consulta la página Formatos de medios compatibles.

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

Kotlin

    var mediaPlayer: 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 formato. Debe ser un archivo multimedia con el formato y la codificación adecuados en uno de los formatos admitidos.

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? = MediaPlayer().apply {
        setAudioStreamType(AudioManager.STREAM_MUSIC)
        setDataSource(applicationContext, myUri)
        prepare()
        start()
    }
    

Java

    Uri myUri = ....; // initialize Uri here
    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    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? = MediaPlayer().apply {
        setAudioStreamType(AudioManager.STREAM_MUSIC)
        setDataSource(url)
        prepare() // might take long! (for buffering, etc)
        start()
    }
    

Java

    String url = "http://........"; // your URL here
    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mediaPlayer.setDataSource(url);
    mediaPlayer.prepare(); // might take long! (for buffering, etc)
    mediaPlayer.start();
    

Nota: Si transmites una URL para transmitir un archivo multimedia en línea, el archivo debe poder descargarse progresivamente.

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

Preparación asíncrona

En principio, usar MediaPlayer puede ser sencillo. Sin embargo, es importante tener en cuenta que se necesitan algo más para integrarlo correctamente con una app para Android típica. Por ejemplo, la ejecución de la llamada a prepare() puede tardar mucho tiempo, ya que puede implicar la obtenció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 app. Si haces eso, la IU se bloquea hasta que el método muestre resultados, lo que constituye una experiencia de usuario muy mala y puede causar un error ANR (aplicación no responde). Incluso si esperas que tu recurso se cargue con rapidez, 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 app es lenta.

Para evitar colgar el subproceso de IU, genera otro para preparar el MediaPlayer y notifica al subproceso principal cuando haya terminado. Sin embargo, si bien puedes escribir por tu cuenta la lógica de subprocesos, este patrón es tan común cuando se usa MediaPlayer que el marco de trabajo proporciona una manera conveniente de realizar esta tarea mediante 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, que se configuró a través de setOnPreparedListener().

Cómo administrar el estado

Otro aspecto de un MediaPlayer que debes tener en cuenta es que está basado en el estado. Es decir, el MediaPlayer tiene un estado interno que siempre debes tener en cuenta cuando escribas tu código, porque algunas 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 generar otros comportamientos no deseados.

En la documentación de la clase MediaPlayer, se muestra un diagrama de estado completo donde se aclara qué métodos cambian el estado del . Por ejemplo, cuando creas un nuevo MediaPlayer, está en el estado Idle. En ese momento, debes inicializarlo llamando a setDataSource(), lo que cambia su estado a Initialized. Luego, debes prepararlo utilizando el método prepare() o prepareAsync(). Cuando MediaPlayer termina de prepararse, ingresa al estado Prepared, lo que significa que puedes llamar a start() para que reproduzca el contenido. En ese momento, como se ilustra en el diagrama, puedes cambiar entre los estados Started, Paused y PlaybackCompleted llamando a métodos como start() , pause() y seekTo(), entre otros. Sin embargo, cuando llames a stop(), observa que no puedes volver a llamar a start() hasta que vuelvas a preparar el MediaPlayer.

Siempre debes tener 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 valiosos recursos del sistema. Por lo tanto, siempre debes tomar precauciones adicionales para asegurarte de no prolongar una instancia de MediaPlayer más de lo necesario. Cuando termines con esto, siempre debes llamar a release() para asegurarse de que se retiren correctamente todos los recursos del sistema asignados. Por ejemplo, si estás usando un MediaPlayer y su actividad recibe una llamada a onStop(), debes retirar el MediaPlayer, ya que tiene poco sentido retenerlo mientras tu actividad no está interactuando con el usuario (a menos que esté reproduciendo contenido multimedia en segundo plano, lo que se analiza en la siguiente sección). Desde ya, cuando se reanude o reinicie tu actividad, deberás crear un nuevo MediaPlayer 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, piensa en los problemas que podrían ocurrir si te olvidaras de retirar MediaPlayer cuando se detiene tu actividad, y crearas uno nuevo cuando la actividad vuelve a comenzar. Como ya sabes, cuando el usuario cambia la orientación de la pantalla (o cambia otra configuración del dispositivo), el sistema reinicia la actividad (de manera predeterminada), por lo que puedes consumir rápidamente todos los recursos del sistema a medida que el usuario gira el dispositivo entre la orientación vertical y la horizontal, ya que con cada cambio de orientación, creas un nuevo MediaPlayer que nunca retiras. (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).

Tal vez te preguntes qué sucede si deseas continuar reproduciendo "contenido en segundo plano", incluso cuando el usuario deja tu actividad, de una manera similar al comportamiento de la app de música incorporada. 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 tus medios se reproduzcan en segundo plano, incluso cuando tu app no está en la pantalla (es decir, deseas continuar la reproducción mientras el usuario interactúa con otras apps), debes iniciar un servicio y controlar la instancia de 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 acerca de cómo un reproductor que se ejecuta en un servicio en segundo plano debería interactuar con el resto del sistema. Si tu app no cumple con esas expectativas, el usuario puede tener una mala experiencia. Consulta Cómo crear 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, como en el caso de una Activity, todo el trabajo de un Service se realiza en un solo subproceso de forma predeterminada; de hecho, si ejecutas una actividad y un servicio desde la misma app, ambos usan el mismo subproceso (el "subproceso principal") de forma predeterminada. Por lo tanto, los servicios deben procesar intents entrantes con rapidez y nunca realizan cálculos prolongados cuando responden a ellos. Si se espera algún trabajo pesado o llamadas de bloqueo, debes realizar esas tareas de forma asíncrona: desde otro subproceso que implementes por tu cuenta o mediante las diversas herramientas del marco de trabajo para el procesamiento asíncrono.

Por ejemplo, cuando uses un MediaPlayer desde el subproceso principal, deberías llamar a prepareAsync(), en lugar de prepare(), e implementar un MediaPlayer.OnPreparedListener a fin de recibir una notificación cuando se complete la preparación y puedas comenzar la reproducción. 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

Por lo general, en operaciones síncronas, los errores 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 app reciba notificaciones de errores de forma 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 para ver el diagrama de estado completo), por lo que debes restablecerlo antes de poder usarlo de nuevo.

Cómo usar bloqueos de activación

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

Para garantizar que el 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 app está usando una 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 en gran medida la duración de la batería del dispositivo.

Para asegurarte de que la CPU continúe ejecutándose mientras se reproduce el MediaPlayer, llama al método setWakeMode() cuando inicialices el . Cuando lo hagas, el MediaPlayer retendrá el bloqueo especificado durante la reproducción y retirará el bloqueo cuando se ponga en pausa o detenga la reproducción:

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 estás transmitiendo contenido multimedia a través de la red y utilizas Wi-Fi, tal vez también desees mantener un WifiLock, que debes adquirir y retirar de manera manual. Por lo tanto, cuando comiences a preparar el MediaPlayer con la URL remota, debes 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 pongas en pausa o detengas la reproducción del contenido, o cuando ya no necesites la red, deberás retirar el bloqueo:

Kotlin

    wifiLock.release()
    

Java

    wifiLock.release();
    

Cómo realizar la limpieza

Como se mencionó anteriormente, un objeto MediaPlayer puede consumir una cantidad importante de recursos del sistema, por lo que debes conservarlo solo el tiempo necesario 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, porque el recolector de elementos no utilizados reclama el MediaPlayer, ya que solo es sensible a las necesidades de memoria y no a otros recursos relacionados con medios. Entonces, si estás usando un servicio, siempre debes anular el método onDestroy() para asegurarte de estar retirando 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 retirar tu MediaPlayer, además de retirarlo cuando se cierra. Por ejemplo, si no podrás reproducir archivos multimedia durante un período prolongado (después de perder el enfoque de audio, por ejemplo), sin duda debes retirar el MediaPlayer existente y crearlo de nuevo más tarde. Por otro lado, si solo detendrás la reproducción por un tiempo muy corto, tal vez deberías mantener el 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 (API nivel 26), MediaPlayer incluye API que son compatibles con la reproducción de material protegido por DRM. Son similares a las API de nivel bajo proporcionadas por 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 no proporciona la funcionalidad completa de MediaDrm, admite los casos prácticos más comunes. La implementación actual puede controlar los siguientes tipos de contenidos:

  • 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 demuestra cómo usar los nuevos métodos de DRM de MediaPlayer en una implementación síncrona simple.

Para administrar medios controlados por DRM, debes incluir los nuevos métodos 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 establece la 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 existe MediaPlayer.DrmInfo:

  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 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 retiro, el resultado es nulo.

Cómo ejecutar prepareDrm() de manera asíncrona

De forma predeterminada, se ejecuta prepareDrm() 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 internamente 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.

Cuando estableces un OnDrmPreparedListener, prepareDrm() realiza el aprovisionamiento (si es necesario) y la preparación en segundo plano. Cuando finaliza 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 prepareDrm() muestre resultados.

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 (API nivel 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.

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 la 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 {
        setAudioStreamType(AudioManager.STREAM_MUSIC)
        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.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mediaPlayer.setDataSource(getApplicationContext(), contentUri);

    // ...prepare and start...
    

Ejemplo de código

En los ejemplos de BasicMediaDecoder y DeviceOwner, se demuestra el uso de las API que se analizan en esta página.

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.