Interfaz del reproductor

Un reproductor es el componente de tu app que facilita la reproducción de elementos multimedia. La interfaz Player de Media3 configura un esquema para la funcionalidad que suele controlar un reproductor. Esto incluye lo siguiente:

  • Afectar los controles de reproducción, como reproducir, pausar y buscar
  • Consultar las propiedades del contenido multimedia que se está reproduciendo, como la posición de reproducción
  • Administración de una playlist o fila de elementos multimedia
  • Configurar las propiedades de reproducción, como la reproducción aleatoria, la repetición, la velocidad y el volumen
  • Renderizar video en la pantalla

Media3 también proporciona una implementación de la interfaz Player, llamada ExoPlayer.

Una interfaz común entre los componentes

Varios componentes de Media3 implementan la interfaz Player, por ejemplo:

Componente Descripción y notas de comportamiento
ExoPlayer Es una API de reproductor multimedia y la implementación predeterminada de la interfaz Player.
MediaController Interactúa con un MediaSession para enviar comandos de reproducción. Si tu Player y MediaSession están en un Service separado de la Activity o Fragment donde se encuentra la IU de tu reproductor, puedes asignar tu MediaController como el reproductor para tu componente de IU, como PlayerView o el elemento componible Player. Las llamadas de método de reproducción y de lista de reproducción se envían a tu Player a través de tu MediaSession.
MediaBrowser Además de la funcionalidad que ofrece un MediaController, interactúa con un MediaLibrarySession para explorar el contenido multimedia disponible.
SimpleBasePlayer Es una implementación de Player que reduce al mínimo la cantidad de métodos que se deben implementar. Es útil cuando usas un reproductor personalizado que quieres conectar a un MediaSession.
ForwardingSimpleBasePlayer Es una subclase de SimpleBasePlayer diseñada para reenviar operaciones de reproducción a otro Player y, al mismo tiempo, permitir las mismas personalizaciones de comportamiento coherentes que SimpleBasePlayer. Usa esta clase para suprimir o modificar operaciones de reproducción específicas.
RemoteCastPlayer Es una implementación de Player para controlar la reproducción en una app de receptor de Cast remota.
CastPlayer Es una implementación de Player para controlar la reproducción de Cast local y remota.

Aunque un MediaSession no implementa la interfaz Player, requiere un Player cuando se crea uno. Su propósito es proporcionar acceso a la Player desde otros procesos o subprocesos.

Arquitectura de reproducción de Media3

Si tienes acceso a un Player, debes llamar a sus métodos directamente para emitir comandos de reproducción. Puedes anunciar tu reproducción y otorgar control de reproducción a fuentes externas implementando un MediaSession. Estas fuentes externas implementan un MediaController, que facilita la conexión a una sesión multimedia y la emisión de solicitudes de comandos de reproducción.

Cuando reproduzcas contenido multimedia en segundo plano, deberás alojar la sesión multimedia y el reproductor en un MediaSessionService o MediaLibraryService que se ejecute como un servicio en primer plano. Si lo haces, puedes separar el reproductor de la actividad de tu app que contiene la IU para el control de reproducción. Esto puede requerir que uses un controlador de contenido multimedia.

Diagrama que muestra cómo los componentes de reproducción de Media3 se ajustan a la arquitectura de una app de contenido multimedia.
Figura 1: La interfaz Player desempeña un papel clave en la arquitectura de Media3.

Estado del reproductor

El estado de un reproductor multimedia que implementa la interfaz Player consta principalmente de 4 categorías de información:

  1. Estado de reproducción
  2. Playlist de elementos multimedia
    • Es una secuencia de instancias de MediaItem para la reproducción.
    • Recupera con getCurrentTimeline()
    • Las instancias de Player pueden proporcionar métodos de operación de playlists, como agregar o quitar un MediaItem, y métodos de conveniencia, como getCurrentMediaItem().
  3. Propiedades de reproducción y pausa, como las siguientes:
    • playWhenReady: Indica si el usuario quiere que el contenido multimedia se reproduzca cuando sea posible o que permanezca pausado.
    • Motivo de la supresión de la reproducción: Es una indicación de por qué se suprime la reproducción, si corresponde, incluso si playWhenReady es true
    • isPlaying: Es una indicación de si el reproductor está reproduciendo contenido, que solo será true si el estado de reproducción es STATE_READY, playWhenReady es true y la reproducción no se suprime.
  4. Posición de reproducción, que incluye lo siguiente:

Además, la interfaz Player permite acceder a las pistas disponibles, los metadatos de contenido multimedia, la velocidad de reproducción, el volumen y otras propiedades auxiliares de la reproducción.

Escucha los cambios

Usa un Player.Listener para detectar cambios en un Player. Consulta la documentación de ExoPlayer sobre los eventos del reproductor para obtener detalles sobre cómo crear y usar un objeto de escucha.

Ten en cuenta que la interfaz del objeto de escucha no incluye devoluciones de llamada para hacer un seguimiento del progreso normal de la reproducción. Para supervisar continuamente el progreso de la reproducción, por ejemplo, para configurar una IU de barra de progreso, debes consultar la posición actual en intervalos adecuados.

Kotlin

fun checkPlaybackPosition(delayMs: Long): Boolean =
  handler.postDelayed(
    {
      val currentPosition = player.currentPosition
      // Update UI based on currentPosition
      checkPlaybackPosition(delayMs)
    },
    delayMs,
  )

Java

boolean checkPlaybackPosition(long delayMs) {
  return handler.postDelayed(
      () -> {
        long currentPosition = player.getCurrentPosition();
        // Update UI based on currentPosition
        checkPlaybackPosition(delayMs);
      },
      delayMs);
}

Controlar la reproducción

La interfaz Player ofrece muchas formas de manipular el estado y controlar la reproducción:

Implementaciones personalizadas de Player

Para crear un reproductor personalizado, puedes extender la clase SimpleBasePlayer incluida en Media3. Esta clase proporciona una implementación base de la interfaz Player para reducir al mínimo la cantidad de métodos que debes implementar.

Para comenzar, anula el método getState(). Este método debe propagar el estado actual del reproductor cuando se lo llama, lo que incluye lo siguiente:

  • Es el conjunto de comandos disponibles.
  • Propiedades de reproducción, como si el reproductor debe comenzar a reproducir cuando el estado de reproducción es STATE_READY, el índice del elemento multimedia que se está reproduciendo y la posición de reproducción dentro del elemento actual

Kotlin

class CustomPlayer(looper: Looper) : SimpleBasePlayer(looper) {
  override fun getState(): State {
    return State.Builder()
      .setAvailableCommands(Commands.EMPTY) // Set which playback commands the player can handle
      // Configure additional playback properties
      .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
      .setCurrentMediaItemIndex(0)
      .setContentPositionMs(0)
      .build()
  }
}

Java

private static final class CustomPlayer extends SimpleBasePlayer {
  public CustomPlayer(Looper looper) {
    super(looper);
  }

  @Override
  protected State getState() {
    return new State.Builder()
        .setAvailableCommands(Commands.EMPTY) // Set which playback commands the player can handle
        // Configure additional playback properties
        .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
        .setCurrentMediaItemIndex(0)
        .setContentPositionMs(0)
        .build();
  }
}

SimpleBasePlayer aplicará que el State se cree con una combinación válida de valores de estado. También controlará los objetos de escucha y les informará sobre los cambios de estado. Si necesitas activar manualmente una actualización de estado, llama a invalidateState().

Además del método getState(), solo necesitas implementar los métodos que se usan para los comandos que tu reproductor declara que están disponibles. Busca el método de controlador anulable que corresponda a la funcionalidad que deseas implementar. Por ejemplo, anula el método handleSeek() para admitir operaciones como COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM y COMMAND_SEEK_TO_NEXT_MEDIA_ITEM.

Modifica las implementaciones de Player

En lugar de crear un Player completamente personalizado, puedes usar ForwardingSimpleBasePlayer para modificar el estado y el comportamiento de un Player existente. Consulta la guía en la página de personalización para obtener más detalles.