Jetpack Media3 define una interfaz Player
que describe la funcionalidad básica para la reproducción de archivos de video y audio. ExoPlayer
es la implementación predeterminada de esta interfaz en Media3. Recomendamos usar ExoPlayer, ya que proporciona un conjunto completo 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 de dispositivos y SO para que el código funcione de manera coherente en todo el ecosistema de Android. ExoPlayer incluye lo siguiente:
- Compatibilidad con playlists
- Compatibilidad con una variedad de formatos de transmisión progresivos y adaptables
- Compatibilidad con la inserción de anuncios del cliente y del servidor
- Compatibilidad con la reproducción protegida por DRM
En esta página, se explican algunos de los pasos clave para compilar una app de reproducción. Si deseas obtener más detalles, puedes consultar nuestras guías completas en Media3 ExoPlayer.
Cómo empezar
Para comenzar, agrega una dependencia en ExoPlayer, la IU y los módulos comunes de Jetpack Media3:
implementation "androidx.media3:media3-exoplayer:1.3.1" implementation "androidx.media3:media3-ui:1.3.1" implementation "androidx.media3:media3-common:1.3.1"
Según el 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.3.1
por tu versión preferida de la biblioteca. Puedes consultar las notas de la versión para conocer la versión más reciente.
Cómo crear un reproductor multimedia
Con Media3, puedes usar la implementación incluida de la interfaz de Player
, ExoPlayer
, o puedes compilar tu propia implementación personalizada.
Crea 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 del ciclo de vida de onCreate()
de Activity
, Fragment
o Service
donde se encuentra.
El objeto Builder
incluye una variedad de opciones de personalización que podrían interesarte, como las siguientes:
setAudioAttributes()
para configurar el control del foco de audiosetHandleAudioBecomingNoisy()
para configurar el comportamiento de reproducción cuando se desconecta un dispositivo de salida de audiosetTrackSelector()
para configurar la selección de pista
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 el 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 una Activity
o Fragment
, esto implica prepararlo en el método de ciclo de vida onStart()
en el nivel de API 24 y versiones posteriores, o en el método de ciclo de vida onResume()
en el nivel de API 23 y versiones anteriores. Para un jugador que está en un Service
, puedes prepararlo en onCreate()
.
Cómo controlar el reproductor
Una vez que se haya preparado el reproductor, puedes controlar la reproducción llamando a métodos en el reproductor, como los siguientes:
play()
ypause()
para iniciar y pausar la reproducciónseekTo()
para buscar una posición dentro del elemento multimedia actualseekToNextMediaItem()
yseekToPreviousMediaItem()
para navegar por la playlist
Los componentes de IU, como PlayerView
o PlayerControlView
, se actualizarán en consecuencia cuando se vinculen a un reproductor.
Cómo liberar el reproductor
La reproducción puede requerir recursos limitados, como decodificadores de video, por lo que es importante llamar a release()
en el reproductor para liberar recursos cuando ya no se necesite.
Si tu reproductor está en un Activity
o Fragment
, libéralo 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 reproductor que está 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 más allá de los límites del proceso. La conexión de una sesión multimedia a tu reproductor te permite anunciar la reproducción de contenido multimedia externamente y recibir comandos de reproducción de fuentes externas, por ejemplo, para integrarla con los controles multimedia del sistema en dispositivos móviles y con pantallas grandes.
Para usar sesiones multimedia, agrega una dependencia en el módulo Media3 Session:
implementation "androidx.media3:media3-session:1.3.1"
Cómo crear una sesión multimedia
Puedes crear un MediaSession
después de inicializar un jugador 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.
Otórgales control a otros clientes
Las apps cliente pueden implementar un controlador multimedia para controlar la reproducción de la sesión multimedia. Para recibir estas solicitudes, configura un objeto de devolución de llamada cuando compiles tu MediaSession
.
Cuando un controlador 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 rechaza la solicitud. Consulta un ejemplo de esto en la app de demostración de Media3 Session.
Una vez conectado, un control puede enviar comandos de reproducción a la sesión. Luego, la sesión delega esos comandos al jugador. La sesión controla automáticamente los comandos de reproducción y playlist definidos en la interfaz de Player
.
Otros métodos de devolución de llamada te permiten controlar, por ejemplo, las solicitudes de comandos de reproducción personalizados y la modificación de listas de reproducción. De manera similar, estas devoluciones de llamada incluyen un objeto ControllerInfo
para que puedas determinar el control de acceso solicitud por solicitud.
Reproducción de contenido multimedia en segundo plano
Para seguir reproduciendo contenido multimedia cuando la app no está en primer plano (por ejemplo, para reproducir música, audiolibros o podcasts incluso cuando el usuario no tiene la app abierta), Player
y MediaSession
deben encapsularse en un servicio en primer plano. Para ello, Media3 proporciona la interfaz MediaSessionService
.
Cómo implementar un MediaSessionService
Crea una clase que extienda MediaSessionService
y crea una instancia de MediaSession
en el método del ciclo de vida de 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 de los clientes a tu sesión multimedia. Muestra un MediaSession
para aceptar la solicitud de conexión o muestra null
para rechazar la solicitud.
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; }
Conéctate a tu IU
Ahora que tu sesión multimedia está en un Service
separado del Activity
o Fragment
en el que 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 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 de onStop()
de un Activity
, llamando a MediaController.releaseFuture()
.
Publica una notificación
Los servicios en primer plano deben publicar una notificación mientras están activos. Un MediaSessionService
creará automáticamente una notificación de MediaStyle
para ti en forma de 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
.
Cómo anunciar tu biblioteca de contenido
Un MediaLibraryService
se basa en un MediaSessionService
y permite que las apps cliente exploren el contenido multimedia que proporciona tu app. Las apps cliente implementan un MediaBrowser
para interactuar con tu MediaLibraryService
.
La implementación de un MediaLibraryService
es similar a la de un MediaSessionService
, con la excepción de que en onGetSession()
debes mostrar un MediaLibrarySession
en lugar de un MediaSession
. En comparación con una MediaSession.Callback
, la MediaLibrarySession.Callback
incluye métodos adicionales que permiten a un cliente de navegador navegar por el contenido que ofrece tu servicio de biblioteca.
Al igual que con MediaSessionService
, declara el 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" />
En el ejemplo anterior, se incluye un filtro de intents para MediaLibraryService
y, para la retrocompatibilidad, para 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 una sola raíz MediaItem
. 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 muestras a un cliente que busca una lista de elementos multimedia recomendados podría contener solo el MediaItem
raíz y un solo nivel de nodos MediaItem
secundarios, mientras que el árbol que muestras a una app cliente diferente puede representar una biblioteca de contenido más completa.
Cómo crear un MediaLibrarySession
Un MediaLibrarySession
extiende la API de MediaSession
para agregar las 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 laMediaItem
raíz de un árbol de contenidoonGetChildren()
para cuando un cliente solicita los elementos secundarios de unMediaItem
en el árbol de contenidoonGetSearchResult()
para cuando un cliente solicita resultados de la búsqueda del árbol de contenido para una consulta determinada
Los métodos de devolución de llamada relevantes incluirán un objeto LibraryParams
con indicadores adicionales sobre el tipo de árbol de contenido que le interesa a una app cliente.