Descripción general de MediaPlayer

El framework multimedia de Android admite la reproducción de diversos tipos de medios comunes. que te permite integrar fácilmente audio, video e imágenes en tus aplicaciones. Puedes reproducir audio o video de archivos multimedia almacenados en los recursos de tu aplicación (recursos sin procesar), de 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 APIs de MediaPlayer.

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

Nota: Solo puedes reproducir los datos de audio en la salida estándar. dispositivo. Por el momento, este es la bocina del dispositivo móvil o los auriculares Bluetooth. No puedes reproducir 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, comprueba que tu manifiesto cuente con las declaraciones adecuadas para permitir el uso de funciones relacionadas.

  • Permiso de Internet: si estás usando MediaPlayer para transmitir con tu aplicación debe solicitar acceso a la red.
    <uses-permission android:name="android.permission.INTERNET" />
    
  • Permiso de bloqueo de activación: Indica si la aplicación del reproductor debe conservar la pantalla. de atenuación o que el procesador entre en estado de suspensión, o utiliza los parámetros 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 el 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 Contenido multimedia compatible Formatos de la página.

Aquí hay un ejemplo sobre cómo reproducir audio disponible como recurso local sin procesar (guardado en el archivo res/raw/):

var mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1)
mediaPlayer.start() // no need to call prepare(); create() does that for you
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 producto "sin procesar" recurso es un archivo que el sistema no analizar de alguna manera en particular. Sin embargo, el contenido de este recurso no debe audio sin procesar. Debe ser un archivo multimedia con el formato y la codificación adecuados de los formatos admitidos.

Así es como puedes reproducir desde un URI disponible localmente en el sistema (la cual obtuviste mediante un agente de resolución de contenido, por ejemplo):

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()
}
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:

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()
}
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 envías una URL para transmitir un archivo multimedia en línea, el archivo debe tener descarga progresiva.

Precaución: Debes atrapar o pasar IllegalArgumentException y IOException cuando se usan setDataSource(), porque es posible que no exista el archivo al que te refieres.

Preparación asíncrona

Usar MediaPlayer puede ser sencillo en . Sin embargo, es importante tener en cuenta que algunas cosas más necesario para integrarla correctamente a una aplicación Android típica. Para Por ejemplo, la llamada a prepare() puede tardar mucho tiempo en ejecutarse, porque puede implicar recuperar y decodificar datos de medios. Al igual que con cualquier otra que pueda tardar mucho en ejecutarse, nunca debes llamarlo desde tu subproceso de IU de la app. Eso hace que la IU se bloquee hasta que el método muestre resultados. lo que supone una muy mala experiencia del usuario y puede causar un error ANR ("Aplicación no responde"). Incluso si Si esperas que tu recurso se cargue rápido, recuerda que todo lo que tarda más de una de segundo para responder en la IU causa una pausa notable y da al usuario la impresión de que tu aplicación es lenta.

Para evitar colgar tu subproceso de IU, genera otro subproceso en preparar el MediaPlayer y notificar al subproceso principal cuando esté listo. Sin embargo, aunque puedes escribir la lógica de subprocesos este patrón es tan común cuando usas MediaPlayer que el framework proporciona una forma conveniente de realizar esta tarea mediante el prepareAsync(). Este método comienza a preparar los medios en segundo plano y regresa de inmediato. Cuando los medios terminó de preparar, el onPrepared() de MediaPlayer.OnPreparedListener, que se configura mediante Se llama a setOnPreparedListener().

Cómo administrar estados

Otro aspecto de un MediaPlayer que debes tener en cuenta es que está basada 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 mientras estado incorrecto, el sistema podría generar una excepción o provocar otros comportamientos no deseados.

La documentación en la La clase MediaPlayer muestra un diagrama completo de estado. que aclara qué métodos mueven el MediaPlayer de un estado a otro. Por ejemplo, cuando creas un MediaPlayer nuevo, estará en Idle. para cada estado. En ese momento, debes inicializarlo llamando setDataSource(), lo que aporta al estado Inicializada. Después, debes prepararlo con el prepare() o prepareAsync(). Cuándo MediaPlayer termina de prepararse, entra en Prepared. estado, lo que significa que puedes llamar a start() para que reproduzca el contenido multimedia. En ese punto, como se ilustra en el diagrama, Puedes cambiar entre los estados Started, Pause y PlaybackCompleted, llamando a métodos como start(), pause() y seekTo(), entre otros. Cuando llama a stop(); sin embargo, ten en cuenta que no puedes volver a llamar a start() hasta que preparar el MediaPlayer de nuevo.

Mantén siempre el diagrama de estado cuando escribas código que interactúe con un MediaPlayer, porque llamar a sus métodos desde el estado incorrecto es un causa común de errores.

Cómo retirar MediaPlayer

Un MediaPlayer puede consumir recursos los recursos del sistema. Por lo tanto, toma siempre precauciones adicionales para asegurarte de no estar permanecen en una instancia MediaPlayer más tiempo del necesario. Cuando no terminaste, siempre debes llamar release() para asegurarte de que los recursos del sistema asignados se liberan correctamente. Por ejemplo, si estás con un MediaPlayer y tu actividad recibe una llamada a onStop(), debes liberar el MediaPlayer. porque no tiene mucho sentido aferrarse a ella mientras la actividad no está interactuando con al usuario (a menos que estés reproduciendo contenido multimedia en segundo plano, como se explica en la siguiente sección). Por supuesto, cuando se reanude o reinicie tu actividad, Crea un MediaPlayer nuevo y vuelve a prepararlo antes de reanudar la reproducción.

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

mediaPlayer?.release()
mediaPlayer = null
mediaPlayer.release();
mediaPlayer = null;

Como ejemplo, considera los problemas que podrían ocurrir te olvidaste de liberar el MediaPlayer cuando se detuvo tu actividad, pero crea un uno nuevo cuando la actividad comience de nuevo. Como sabrás, cuando el usuario cambia 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), de modo que puedas consumen todos los recursos del sistema el dispositivo rota entre el modo vertical y el horizontal, ya que en cada cambio de orientación, creas un nuevo elemento MediaPlayer que nunca lanzamiento. (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).

Es posible que te preguntes qué sucede si quieres seguir jugando “contenido multimedia en segundo plano” incluso cuando el usuario abandona tu actividad, casi en el mismo la forma en que se comporta la aplicación integrada de música. En este caso, lo que necesitas es un MediaPlayer controlado por un Servicio, como que analizamos en la próxima sección

Cómo usar MediaPlayer en un servicio

Si quieres que tu contenido multimedia se reproduzca en segundo plano incluso cuando tu app no está en la pantalla, es decir, quieres que siga reproduciéndose mientras el usuario interactuando con otras aplicaciones, debes comenzar un Sitúa y controla el MediaPlayer a partir de allí. Debes incorporar MediaPlayer en un servicio de MediaBrowserServiceCompat y tienen interactúan con una MediaBrowserCompat en otra actividad.

Debes tener cuidado con esta configuración de cliente/servidor. Hay expectativas sobre cómo un reproductor que se ejecuta en un servicio en segundo plano interactúa con el resto del en un sistema de archivos. Si tu aplicación no cumple con esas expectativas, el usuario puede tengan una mala experiencia. Leído Cómo compilar una app de audio para conocer 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 un Activity, todos funcionan en una Service se realiza en un solo subproceso predeterminada; de hecho, si ejecutas una actividad y un servicio desde la misma aplicación, estos usar el mismo subproceso (el "subproceso principal") de forma predeterminada. Por lo tanto, los servicios deben procesar intents entrantes con rapidez y nunca realicen cálculos largos cuando respondan a ellos. Si hay mucho trabajo o de bloqueo, debes realizar esas tareas de forma asíncrona: ya sea desde otro subproceso que implementes por tu cuenta o con las diversas instalaciones para el procesamiento asíncrono.

Por ejemplo, cuando usas un MediaPlayer de tu subproceso principal, deberías llamar a prepareAsync() en lugar de prepare() y, luego, implementa un MediaPlayer.OnPreparedListener para recibir una notificación cuando se complete la preparación y puedas comenzar a jugar. Por ejemplo:

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()
    }
}
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ñalizar con una excepción o un código de error, pero siempre que uses debes asegurarte de que tu aplicación reciba notificaciones los errores de forma adecuada. En el caso de un MediaPlayer, puedes lograrlo implementando una MediaPlayer.OnErrorListener y configúralo en tu instancia MediaPlayer:

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!
    }
}
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 del clase MediaPlayer para el diagrama de estado completo) por lo que debes restablecerla antes de volver a usarla.

Cómo usar bloqueos de activación

Cuando se diseñan aplicaciones que reproducen contenido multimedia en segundo plano, es posible que el dispositivo entre en modo de suspensión. mientras se ejecuta el servicio. Como el sistema Android intenta conservar mientras el dispositivo está suspendido, el sistema intenta apagar de las funciones del teléfono que son incluso la CPU y el hardware de Wi-Fi. Sin embargo, si tu servicio reproduce o transmite música, debes evitar que el sistema interfiera en la reproducción.

Para garantizar que tu servicio siga funcionando estas condiciones, debes usar "bloqueos de activación". Un bloqueo de activación es una señal de al sistema de que tu aplicación está usando alguna función que debería seguirán disponibles aunque 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, porque reducen significativamente la duración de la batería del dispositivo.

Para asegurarte de que la CPU continúe ejecutándose mientras tu MediaPlayer esté en reproducción, llama al método setWakeMode() cuando inicialices tu MediaPlayer. Una vez que lo hagas, MediaPlayer mantiene el bloqueo especificado durante la reproducción y libera el bloqueo. cuando se ponen en pausa o se detienen:

mediaPlayer = MediaPlayer().apply {
    // ... other initialization here ...
    setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}
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 y estás usando Wi-Fi, quizás quieras mantener una WifiLock como que debes adquirir y lanzar manualmente. Entonces, cuando empieces a preparar MediaPlayer por la URL remota, debes crear y adquirir el bloqueo de Wi-Fi. Por ejemplo:

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

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

wifiLock.acquire();

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

wifiLock.release()
wifiLock.release();

Cómo realizar la limpieza

Como se mencionó antes, un objeto MediaPlayer puede consumir una cantidad significativa cantidad de recursos del sistema, por lo que debes conservarla solo durante el tiempo que necesites y llamar release() cuando termines. Es importante llamar a este método de limpieza de manera explícita en lugar de depender de la recolección de elementos no utilizados del sistema, ya que puede llevar un tiempo hasta que el recolector de elementos no utilizados reclama el MediaPlayer, ya que solo es sensible a las necesidades de memoria y no a la escasez de otros recursos relacionados con los medios. Así que, cuando uses un servicio, siempre debes anular el Método onDestroy() para asegurarte de que realizarás el lanzamiento MediaPlayer:

class MyService : Service() {

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

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
    }
}
public class MyService extends Service {
   MediaPlayer mediaPlayer;
   // ...

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

Siempre debes buscar otras oportunidades para lanzar tu MediaPlayer además de liberarlo cuando se cierra. Por ejemplo, si esperas que no poder reproducir contenido multimedia durante un período prolongado (después de perder el foco de audio, por ejemplo) definitivamente debes liberar tu MediaPlayer existente y crearlo de nuevo más adelante. En la Por otro lado, si esperas detener la reproducción por muy poco tiempo, probablemente deberías Conserva tu MediaPlayer para evitar la sobrecarga de crearlo y prepararlo. de nuevo.

Administración de derechos digitales (DRM)

A partir de Android 8.0 (nivel de API 26), MediaPlayer incluye APIs que admitir la reproducción de material protegido por DRM. Son similares a la API de bajo nivel proporcionada por MediaDrm, pero operan a un nivel superior y no exponen los objetos extractor, drm y criptográficos subyacentes.

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

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

El siguiente fragmento de código demuestra cómo usar el nuevo MediaPlayer de DRM 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:

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()
}
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 un OnDrmConfigHelper y conéctala a la jugador usando setOnDrmConfigHelper()
  2. Llamar a prepare()
  3. Llamar a getDrmInfo() Si la fuente tiene DRM content, el método devuelve un valor Valor 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 un OnDrmConfigHelper, se llama mientras que prepareDrm() se está ejecutando. Esto te permite realizar una configuración personalizada de la DRM las propiedades antes de abrir la sesión de DRM. Se llama a la devolución de llamada de forma síncrona en el subproceso que llamó prepareDrm() Para acceder a las propiedades de DRM, llamar getDrmPropertyString() y setDrmPropertyString() Evita realizar operaciones prolongadas.
    • Si el dispositivo aún no se aprovisionó, prepareDrm() también accede al servidor de aprovisionamiento para aprovisionar el dispositivo. Esto puede tardar de tiempo variable, 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 getKeyRequest()
  4. Para informar al motor de DRM de la respuesta clave recibida del servidor de licencias, llama 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 a un nuevo 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, prepareDrm() se ejecuta de forma síncrona y se bloquea hasta que finaliza la preparación. Sin embargo, La primera preparación de la DRM en un nuevo dispositivo también puede requerir aprovisionamiento, lo cual manejadas internamente por prepareDrm() y puede tardar un poco en completarse debido a la operación de red involucrada. Puedes evitar bloqueos en prepareDrm() el definir y configurar una MediaPlayer.OnDrmPreparedListener

Cuando estableces un OnDrmPreparedListener, prepareDrm() realiza el aprovisionamiento (si es necesario) y la preparación en segundo plano. Cuándo el aprovisionamiento y la preparación, se llama al objeto de escucha. Deberías no hagas suposiciones 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 prepareDrm() de retorno.

Cómo configurar la DRM de forma asíncrona

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

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()
}
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 cifrado común (CENC) Medios encriptados a nivel de muestra de HLS (METHOD=SAMPLE-AES) para los tipos de transmisión elementales 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 aplicación de reproductor 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:

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

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