Criar um app de player de mídia básico usando o ExoPlayer da Media3

O Jetpack Media3 define uma interface Player que descreve a funcionalidade básica. para reprodução de arquivos de vídeo e áudio. ExoPlayer é a implementação padrão. da interface na Media3. Recomendamos usar o ExoPlayer, porque ele fornece uma abrangente de recursos que abrangem a maioria dos casos de uso de reprodução e é e personalizáveis para outros casos de uso. O ExoPlayer também abstrai a fragmentação de dispositivo e SO para que seu código funcione de maneira consistente em todo o ecossistema Android. O ExoPlayer inclui:

Esta página guia você por algumas das principais etapas para criar uma reprodução e, para mais detalhes, consulte nossos guias completos sobre ExoPlayer da Media3:

Primeiros passos

Para começar, adicione uma dependência no ExoPlayer, na IU e nos módulos comuns do Jetpack Media3:

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

Dependendo do caso de uso, você também pode precisar de outros módulos da Media3, como exoplayer-dash para abrir transmissões no formato DASH.

Substitua 1.4.1 pela versão de sua preferência do biblioteca. Consulte as notas da versão para conferir a versão mais recente.

Como criar um player de mídia

Com a Media3, é possível usar a implementação incluída do Player ExoPlayer, ou crie sua própria implementação personalizada.

Como criar um ExoPlayer

A maneira mais simples de criar uma instância de ExoPlayer é a seguinte:

Kotlin

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

Java

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

É possível criar seu player de mídia no método de ciclo de vida onCreate() do Activity, Fragment ou Service onde ele reside.

O Builder inclui uma variedade de opções de personalização que podem ser do seu interesse, como:

A Media3 oferece um componente de interface PlayerView que pode ser incluído no arquivo arquivo de layout. Este componente encapsula um PlayerControlView para reprodução controles, SubtitleView para exibir legendas e Surface para renderização vídeo.

Como preparar o player

Adicione itens de mídia a uma playlist para a reprodução com métodos como setMediaItem() e addMediaItem(). Em seguida, chame prepare() para começar a carregar mídia e adquirir os recursos necessários.

Não siga essas etapas antes de o app estar em primeiro plano. Se as jogador está em um Activity ou Fragment, isso significa preparar o jogador na Método de ciclo de vida onStart() no nível 24 da API e mais recentes ou no onResume() Lifecycle no nível 23 da API e anteriores. Para um jogador que está em um Service, você pode prepará-lo em onCreate().

Controlar o player

Após a preparação do player, você pode controlar a reprodução chamando métodos no player, como:

Componentes da interface, como PlayerView ou PlayerControlView, serão atualizados da maneira adequada quando vinculados a um jogador.

Solte o player

A reprodução pode exigir recursos com fornecimento limitado, como vídeos por isso, é importante chamar release() no seu player para liberar recursos quando ele não for mais necessário.

Se o player estiver em uma Activity ou Fragment, libere-o na Método de ciclo de vida onStop() no nível 24 da API e mais recentes ou no onPause() no nível 23 da API e anteriores. Para um jogador que esteja em um Service, é possível lançaremos em onDestroy().

Gerenciar a reprodução com uma sessão de mídia

No Android, as sessões de mídia oferecem uma maneira padronizada de interagir com uma mídia um jogador através dos limites do processo. Como conectar uma sessão de mídia ao player permite que você anuncie sua reprodução de mídia externamente e receba a reprodução comandos de fontes externas, por exemplo, para integrar com controles de mídia do sistema em dispositivos móveis e grandes e dispositivos de tela.

Para usar sessões de mídia, adicione uma dependência ao módulo de sessão da Media3:

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

Criar uma sessão de mídia

Você pode criar um MediaSession após inicializar um player da seguinte maneira:

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

A Media3 sincroniza automaticamente o estado do Player com o estado da MediaSession. Isso funciona com qualquer implementação de Player, incluindo ExoPlayer, CastPlayer ou um implementação personalizada.

Conceder controle a outros clientes

Os apps clientes podem implementar um controlador de mídia. para controlar a reprodução da sua sessão de mídia. Para receber essas solicitações, defina um callback quando criar seu MediaSession.

Quando um controlador está prestes a se conectar à sua sessão de mídia, o onConnect() é chamado. Você pode usar o ControllerInfo fornecido para decidir se você quer aceitar ou rejeitar da solicitação. Confira um exemplo no app de demonstração Media3 Session (link em inglês).

Uma vez conectado, um controlador pode enviar comandos de reprodução para a sessão. A sessão e delega esses comandos para o player. Reprodução e playlist Os comandos definidos na interface Player são processados automaticamente pelo sessão.

Outros métodos de callback permitem que você lide, por exemplo, com solicitações de comandos de reprodução personalizados e modificando a playlist. Esses callbacks incluem um objeto ControllerInfo para que você determinar o controle de acesso de acordo com a solicitação.

Tocando mídia em segundo plano

Para continuar a tocar mídia quando o app não estiver em primeiro plano, por exemplo para tocar músicas, audiolivros ou podcasts mesmo quando o usuário não tiver o app aberto, seus Player e MediaSession precisam ser encapsulados em um serviço em primeiro plano. A Media3 oferece MediaSessionService para essa finalidade.

Como implementar um MediaSessionService

Crie uma classe que estenda MediaSessionService e instancie seu MediaSession no 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();
    }
}

No manifesto, a classe Service com uma intent MediaSessionService filtrar e solicitar a permissão FOREGROUND_SERVICE para executar um primeiro plano serviço:

<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 fim, na classe que você criou, modifique o método onGetSession() para controlar acesso de cliente à sessão de mídia. Retorne um MediaSession para aceitar o solicitação de conexão ou retornar null para rejeitar a solicitação.

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;
}

Como se conectar à interface

Agora que sua sessão de mídia está em um Service separado do Activity ou Fragment em que a interface do player fica, você pode usar um MediaController para vincular e juntá-los. No método onStart() da Activity ou Fragment com sua interface, crie um SessionToken para a MediaSession e use o SessionToken para criar um MediaController. A criação de um MediaController acontece de forma assí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 a interface Player, então você pode usar a mesma métodos, como play() e pause(), para controlar a reprodução. Semelhante a outro , não se esqueça de liberar a MediaController quando ela não estiver mais necessários, como o método de ciclo de vida onStop() de uma Activity, chamando MediaController.releaseFuture()

Publicar uma notificação

Os serviços em primeiro plano precisam publicar uma notificação enquanto estão ativos. Um MediaSessionService vai criar automaticamente MediaStyle notificação para você na forma de um MediaNotification. Para fornecer uma notificação personalizada, crie um MediaNotification.Provider com DefaultMediaNotificationProvider.Builder ou criando uma implementação personalizada da interface do provedor. Adicione seu provedor ao seu MediaSession com setMediaNotificationProvider.

Como anunciar sua biblioteca de conteúdo

Um MediaLibraryService se baseia em um MediaSessionService, permitindo que o cliente para navegar pelo conteúdo de mídia fornecido pelo seu app. Os apps clientes implementam uma MediaBrowser para interagir com seu MediaLibraryService.

A implementação de um MediaLibraryService é semelhante à implementação de uma MediaSessionService, exceto que, em onGetSession(), você deve retornar uma MediaLibrarySession em vez de MediaSession. Em comparação com um MediaSession.Callback, o MediaLibrarySession.Callback inclui outros métodos que permitem que um cliente de navegador navegue pelo conteúdo oferecido pelo seu serviço de biblioteca.

De forma semelhante a MediaSessionService, declare a MediaLibraryService no seu e solicitar a permissão FOREGROUND_SERVICE para executar um primeiro plano serviço:

<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" />

O exemplo acima inclui um filtro de intent para o MediaLibraryService e, para compatibilidade com versões anteriores, o MediaBrowserService legado. A o filtro de intent extra permite que apps clientes usem a API MediaBrowserCompat. reconhecer seu Service.

Um MediaLibrarySession permite exibir sua biblioteca de conteúdo em uma árvore. com uma única raiz MediaItem. Cada MediaItem da árvore pode ter qualquer número de nós MediaItem filhos. É possível veicular uma raiz diferente árvore diferente, com base na solicitação do app cliente. Por exemplo, a árvore que você retornar a um cliente que procura uma lista de itens de mídia recomendados só pode contêm a raiz MediaItem e um único nível de nós MediaItem filhos; enquanto a árvore que você retorna para um aplicativo cliente diferente pode representar uma biblioteca completa de conteúdo.

Como criar um MediaLibrarySession

Uma MediaLibrarySession estende a API MediaSession para adicionar APIs de navegação de conteúdo. Em comparação com Callback MediaSession, o callback MediaLibrarySession adiciona métodos como:

  • onGetLibraryRoot() para quando um cliente solicita o MediaItem raiz de uma árvore de conteúdo
  • onGetChildren() para quando um cliente solicita os filhos de um MediaItem na árvore de conteúdo.
  • onGetSearchResult() para quando um cliente solicita resultados de pesquisa da árvore de conteúdo para um determinado consulta

Os métodos de callback relevantes vão incluir um LibraryParams. com sinais adicionais sobre o tipo de árvore de conteúdo que um app cliente tem interesse.