Crea una app básica de reproductor multimedia con Media3 ExoPlayer

Jetpack Media3 define una interfaz Player que describe la funcionalidad básica para la reproducción de archivos de audio y video. ExoPlayer es la implementación predeterminada de esta interfaz en Media3. Te recomendamos que uses ExoPlayer, ya que proporciona un conjunto integral de funciones que abarcan la mayoría de los casos de uso de reproducción y se puede personalizar para controlar cualquier caso de uso adicional que puedas tener. ExoPlayer también abstrae la fragmentación del dispositivo y el SO para que tu código funcione de manera coherente en todo el ecosistema de Android. ExoPlayer incluye lo siguiente:

En esta página, se explican algunos de los pasos clave para compilar una app de reproducción. Para obtener más detalles, puedes consultar nuestras guías completas sobre Media3 ExoPlayer.

Cómo comenzar

Para comenzar, agrega una dependencia en los módulos de ExoPlayer, IU y Common de Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.7.1"
implementation "androidx.media3:media3-ui:1.7.1"
implementation "androidx.media3:media3-common:1.7.1"

Según tu caso de uso, es posible que también necesites módulos adicionales de Media3, como exoplayer-dash para reproducir transmisiones en formato DASH.

Asegúrate de reemplazar 1.7.1 por la versión de la biblioteca que prefieras. Puedes consultar las notas de la versión para ver la versión más reciente.

Cómo crear un reproductor multimedia

Con Media3, puedes usar la implementación incluida de la interfaz Player, ExoPlayer, o bien puedes compilar tu propia implementación personalizada.

Cómo crear un ExoPlayer

La forma más sencilla de crear una instancia de ExoPlayer es la siguiente:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

Puedes crear tu reproductor multimedia en el método de ciclo de vida onCreate() del Activity, Fragment o Service en el que se encuentra.

Builder incluye una variedad de opciones de personalización que te pueden interesar, como las siguientes:

Media3 proporciona un componente de IU PlayerView que puedes incluir en el archivo de diseño de tu app. Este componente encapsula un PlayerControlView para los controles de reproducción, un SubtitleView para mostrar subtítulos y un Surface para renderizar video.

Cómo preparar el reproductor

Agrega elementos multimedia a una playlist para reproducirlos con métodos como setMediaItem() y addMediaItem(). Luego, llama a prepare() para comenzar a cargar contenido multimedia y adquirir los recursos necesarios.

No debes realizar estos pasos antes de que la app esté en primer plano. Si tu reproductor está en un Activity o Fragment, esto significa preparar el reproductor en el método de ciclo de vida onStart() en el nivel de API 24 y versiones posteriores, o bien el método de ciclo de vida onResume() en el nivel de API 23 y versiones anteriores. En el caso de un jugador que se encuentra en un Service, puedes prepararlo en onCreate().

Cómo controlar el reproductor

Después de preparar el reproductor, puedes controlar la reproducción llamando a métodos en el reproductor, como los siguientes:

Los componentes de la IU, como PlayerView o PlayerControlView, se actualizarán según corresponda cuando se vinculen a un reproductor.

Cómo liberar al jugador

La reproducción puede requerir recursos que son limitados, como los decodificadores de video, por lo que es importante llamar a release() en tu reproductor para liberar recursos cuando ya no se necesite.

Si tu reproductor está en un Activity o Fragment, libera el reproductor en el método de ciclo de vida onStop() en el nivel de API 24 y versiones posteriores, o en el método onPause() en el nivel de API 23 y versiones anteriores. En el caso de un jugador que se encuentra en un Service, puedes liberarlo en onDestroy().

Cómo administrar la reproducción con una sesión multimedia

En Android, las sesiones multimedia proporcionan una forma estandarizada de interactuar con un reproductor multimedia a través de los límites del proceso. Conectar una sesión multimedia a tu reproductor te permite anunciar la reproducción de contenido multimedia de forma externa y recibir comandos de reproducción de fuentes externas, por ejemplo, para integrarte con los controles multimedia del sistema en dispositivos móviles y de pantalla grande.

Para usar sesiones multimedia, agrega una dependencia en el módulo de sesión de Media3:

implementation "androidx.media3:media3-session:1.7.1"

Crea una sesión multimedia

Puedes crear un objeto MediaSession después de inicializar un reproductor de la siguiente manera:

Kotlin

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Media3 sincroniza automáticamente el estado de Player con el estado de MediaSession. Esto funciona con cualquier implementación de Player, incluidas ExoPlayer, CastPlayer o una implementación personalizada.

Otorga control a otros clientes

Las apps cliente pueden implementar un controlador multimedia para controlar la reproducción de tu sesión multimedia. Para recibir estas solicitudes, configura un objeto de devolución de llamada cuando compiles tu MediaSession.

Cuando un control está a punto de conectarse a tu sesión multimedia, se llama al método onConnect(). Puedes usar el ControllerInfo proporcionado para decidir si aceptar o rechazar la solicitud. Puedes ver un ejemplo de esto en la app de demostración de Media3 Session.

Una vez conectado, un controlador puede enviar comandos de reproducción a la sesión. Luego, la sesión delega esos comandos al reproductor. La sesión controla automáticamente los comandos de reproducción y de playlist definidos en la interfaz Player.

Otros métodos de devolución de llamada te permiten controlar, por ejemplo, solicitudes de comandos de reproducción personalizados y modificar la playlist. De manera similar, estas devoluciones de llamada incluyen un objeto ControllerInfo para que puedas determinar el control de acceso en cada solicitud.

Reproducción de contenido multimedia en segundo plano

Para seguir reproduciendo contenido multimedia cuando tu app no está en primer plano, por ejemplo, para reproducir música, audiolibros o podcasts incluso cuando el usuario no tiene abierta tu app, tus Player y MediaSession deben estar encapsulados en un servicio en primer plano. Media3 proporciona la interfaz MediaSessionService para este propósito.

Implementa un MediaSessionService

Crea una clase que extienda MediaSessionService y crea una instancia de tu MediaSession en el método de ciclo de vida onCreate().

Kotlin

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Java

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

En tu manifiesto, tu clase Service con un filtro de intents MediaSessionService y solicita el permiso FOREGROUND_SERVICE para ejecutar un servicio en primer plano:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Por último, en la clase que creaste, anula el método onGetSession() para controlar el acceso del cliente a tu sesión multimedia. Devuelve un MediaSession para aceptar la solicitud de conexión o un null para rechazarla.

Kotlin

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Java

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

Cómo conectarte a tu IU

Ahora que tu sesión multimedia está en un Service separado del Activity o Fragment donde se encuentra la IU del reproductor, puedes usar un MediaController para vincularlos. En el método onStart() de Activity o Fragment con tu IU, crea un SessionToken para tu MediaSession y, luego, usa el SessionToken para compilar un MediaController. La compilación de un MediaController se realiza de forma asíncrona.

Kotlin

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Java

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController implementa la interfaz Player, por lo que puedes usar los mismos métodos, como play() y pause(), para controlar la reproducción. Al igual que con otros componentes, recuerda liberar el MediaController cuando ya no sea necesario, como el método de ciclo de vida onStop() de un Activity, llamando a MediaController.releaseFuture().

Cómo publicar una notificación

Los servicios en primer plano deben publicar una notificación mientras están activos. Un MediaSessionService creará automáticamente una MediaStyle notificación para ti en forma de un MediaNotification. Para proporcionar una notificación personalizada, crea un MediaNotification.Provider con DefaultMediaNotificationProvider.Builder o crea una implementación personalizada de la interfaz del proveedor. Agrega tu proveedor a tu MediaSession con setMediaNotificationProvider.

Publicita tu biblioteca de contenido

Un MediaLibraryService se basa en un MediaSessionService, ya que permite que las apps cliente exploren el contenido multimedia que proporciona tu app. Las apps cliente implementan un MediaBrowser para interactuar con tu MediaLibraryService.

Implementar un MediaLibraryService es similar a implementar un MediaSessionService, excepto que, en onGetSession(), debes devolver un MediaLibrarySession en lugar de un MediaSession. En comparación con un MediaSession.Callback, el MediaLibrarySession.Callback incluye métodos adicionales que permiten que un cliente del navegador navegue por el contenido que ofrece tu servicio de biblioteca.

De manera similar a MediaSessionService, declara MediaLibraryService en tu manifiesto y solicita el permiso FOREGROUND_SERVICE para ejecutar un servicio en primer plano:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

El ejemplo anterior incluye un filtro de intents para MediaLibraryService y, para la retrocompatibilidad, el MediaBrowserService heredado. El filtro de intents adicional permite que las apps cliente que usan la API de MediaBrowserCompat reconozcan tu Service.

Un MediaLibrarySession te permite entregar tu biblioteca de contenido en una estructura de árbol, con un solo MediaItem raíz. Cada MediaItem del árbol puede tener cualquier cantidad de nodos MediaItem secundarios. Puedes entregar una raíz diferente o un árbol diferente según la solicitud de la app cliente. Por ejemplo, el árbol que le devuelves a un cliente que busca una lista de elementos multimedia recomendados podría contener solo el nodo raíz MediaItem y un solo nivel de nodos secundarios MediaItem, mientras que el árbol que le devuelves a otra app cliente podría representar una biblioteca de contenido más completa.

Cómo crear un MediaLibrarySession

Un MediaLibrarySession extiende la API de MediaSession para agregar APIs de navegación de contenido. En comparación con la devolución de llamada MediaSession, la devolución de llamada MediaLibrarySession agrega métodos como los siguientes:

  • onGetLibraryRoot() para cuando un cliente solicita la raíz MediaItem de un árbol de contenido
  • onGetChildren() para cuando un cliente solicita los elementos secundarios de un MediaItem en el árbol de contenido
  • onGetSearchResult() para cuando un cliente solicita resultados de la búsqueda del árbol de contenido para una búsqueda determinada

Los métodos de devolución de llamada pertinentes incluirán un objeto LibraryParams con indicadores adicionales sobre el tipo de árbol de contenido que le interesa a una app cliente.