Interface do player

Um player é o componente do app que facilita a reprodução de itens de mídia. A interface Player do Media3 define um esboço para a funcionalidade geralmente processada por um player. Isso inclui:

  • Afetando os controles de reprodução, como tocar, pausar e procurar
  • Consultar propriedades da mídia em reprodução, como a posição de reprodução
  • Gerenciar uma playlist/fila de itens de mídia
  • Configurar propriedades de reprodução, como ordem aleatória, repetição, velocidade e volume
  • Renderizar vídeos na tela

O Media3 também oferece uma implementação da interface Player, chamada ExoPlayer.

Uma interface comum entre componentes

Vários componentes no Media3 implementam a interface do player, por exemplo:

Componente Descrição e notas sobre o comportamento
ExoPlayer Uma API de player de mídia e a implementação padrão da interface Player.
MediaController Interage com um MediaSession para enviar comandos de reprodução. Se o Player e o MediaSession estiverem em um Service separado do Activity ou do Fragment em que a interface do jogador está, você poderá atribuir o MediaController como o jogador da interface PlayerView. As chamadas de método de reprodução e playlist são enviadas para o Player pelo MediaSession.
MediaBrowser Além da funcionalidade oferecida por um MediaController, interage com um MediaLibrarySession para navegar pelo conteúdo de mídia disponível.
ForwardingPlayer Uma implementação de Player que encaminha chamadas de método para outro Player. Use essa classe para suprimir ou modificar operações específicas substituindo os respectivos métodos.
SimpleBasePlayer Uma implementação de Player que reduz o número de métodos a serem implementados ao mínimo. Útil ao usar um player personalizado que você quer conectar a um MediaSession.
CastPlayer Uma implementação de Player que se comunica com um app receptor do Google Cast. O comportamento depende da sessão de transmissão.

Embora um MediaSession não implemente a interface Player, ele exige um Player ao criar um. O objetivo é fornecer acesso ao Player de outros processos ou threads.

Arquitetura de reprodução do Media3

Se você tiver acesso a um Player, chame os métodos dele diretamente para emitir comandos de reprodução. É possível anunciar a reprodução e conceder o controle de reprodução de fontes externas implementando um MediaSession. Essas fontes externas implementam um MediaController, que facilita a conexão com uma sessão de mídia e a emissão de solicitações de comando de reprodução.

Ao reproduzir mídia em segundo plano, é necessário armazenar a sessão de mídia e o player em um MediaSessionService ou MediaLibraryService que é executado como um serviço em primeiro plano. Se você fizer isso, poderá separar o player da atividade no app que contém a interface do controle de reprodução. Isso pode exigir que você use um controlador de mídia.

Um diagrama mostrando como os componentes de reprodução do Media3 se encaixam em uma arquitetura de app de mídia.
Figura 1: a interface Player desempenha um papel importante na arquitetura do Media3.

Estado do player

O estado de um media player que implementa a interface Player consiste principalmente em quatro categorias de informações:

  1. Estado da reprodução
  2. Playlist de itens de mídia
  3. Propriedades de reproduzir/pausar, como:
    • playWhenReady: uma indicação de se o usuário quer que a mídia seja reproduzida quando possível ou se quer que ela fique pausada.
    • Motivo da supressão da reprodução: uma indicação de por que a reprodução foi suprimida, se aplicável, mesmo que playWhenReady seja true.
    • isPlaying: uma indicação de se o player está sendo reproduzido, que será true apenas se o estado de reprodução for STATE_READY, playWhenReady for true e a reprodução não for suprimida.
  4. Posição da reprodução, incluindo:

Além disso, a interface Player permite o acesso às faixas disponíveis, metadados de mídia, velocidade de reprodução, volume e outras propriedades auxiliares da reprodução.

Detectar mudanças

Use um Player.Listener para detectar mudanças em um Player. Consulte a documentação do ExoPlayer sobre Eventos do player para saber como criar e usar um listener.

A interface do listener não inclui callbacks para acompanhar a progressão de reprodução normal. Para monitorar continuamente o progresso da reprodução, como configurar uma interface de barra de progresso, consulte a posição atual em intervalos adequados.

Kotlin

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

Java

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

Controlar a reprodução

A interface Player oferece várias maneiras de manipular o estado e controlar a reprodução:

Implementações personalizadas de Player

Para criar um player personalizado, estenda o SimpleBasePlayer incluído no Media3. Essa classe oferece uma implementação básica da interface Player para reduzir ao mínimo o número de métodos que você precisa implementar.

Comece modificando o método getState(). Esse método precisa preencher o estado atual do jogador quando chamado, incluindo:

  • O conjunto de comandos disponíveis
  • Propriedades de reprodução, como se o player precisa começar a reprodução quando o estado de reprodução é STATE_READY, o índice do item de mídia que está sendo reproduzido e a posição de reprodução dentro do item atual

Kotlin

class CustomPlayer : SimpleBasePlayer(looper) {
  override fun getState(): State {
    return State.Builder()
      .setAvailableCommands(...) // 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

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

  @Override
  protected State getState() {
    return new State.Builder()
      .setAvailableCommands(...) // 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 vai exigir que o State seja criado com uma combinação válida de valores de estado. Ele também processa listeners e informa os listeners sobre mudanças de estado. Se você precisar acionar manualmente uma atualização de estado, chame invalidateState().

Além do método getState(), você só precisa implementar métodos usados para comandos que o jogador declara como disponíveis. Encontre o método de manipulador substituível que corresponde à funcionalidade que você quer implementar. Por exemplo, substitua o método handleSeek() para oferecer suporte a operações como COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM e COMMAND_SEEK_TO_NEXT_MEDIA_ITEM.