Controlar mídias pelo MediaSession

Última atualização: 22/01/2020

Quais são os benefícios de adicionar um MediaSession à reprodução do vídeo?

As sessões de mídia vinculam a plataforma Android e os apps de mídia de forma integral. De um lado, este vínculo informa ao Android que a mídia está sendo reproduzida, assim as ações de mídia podem ser encaminhadas para a sessão correta. Do outro, ele também informa a plataforma sobre o que está sendo reproduzido e como isso pode ser controlado.

A exposição de um MediaSession pelo aplicativo traz vários benefícios aos usuários. Veja alguns ótimos exemplos.

Google Assistente

Os usuários podem interagir facilmente com a mídia no seu app por comandos de voz, como "Pausar", "Retomar" e "Próximo". Os metadados da sua mídia também podem ser usados para conseguir respostas sobre o que está sendo reproduzido no momento.

Android TV

Em experiências de tela grande, o app Android TV pode usar controles remotos convencionais para usuários com TVs compatíveis com HDMI-CEC. Comandos emitidos pelos botões de reprodução/pausar, parar, próximo e anterior são retransmitidos para seu aplicativo.

Controles de mídia na tela

A partir do Android 4.0 (API de nível 14), o sistema pode acessar o estado de reprodução de uma sessão de mídia e os metadados. Essa funcionalidade permite que a tela de bloqueio mostre controles de mídia e obras de arte. Esse comportamento varia de acordo com a versão do Android.

Mídia em segundo plano

A mídia pode ser controlada em qualquer um desses cenários mesmo se o aplicativo que está reproduzindo a mídia estiver sendo executado em segundo plano.

Computação no ambiente

A exposição da sua mídia com dados sobre o que está sendo reproduzido e como ela pode ser controlada pode funcionar entre dispositivos para que os usuários possam interagir com ela de várias maneiras.

O que você criará

Neste codelab, você ampliará a amostra existente do Exoplayer para adicionar suporte à sessão de mídia. Seu app irá:

  • refletir corretamente o estado ativo da sessão de mídia;
  • redirecionar os controles de mídia para o ExoPlayer;
  • transmitir os metadados dos itens na fila para a sessão de mídia.

O que você aprenderá

  • Por qual razão as sessões de mídia oferecem aos usuários uma experiência melhor
  • Como criar uma sessão de mídia e gerenciar o estado dela
  • Como conectar uma sessão de mídia ao ExoPlayer
  • Como incluir metadados de itens da fila de reprodução na sessão de mídia
  • Como adicionar mais ações (personalizadas)

Este codelab se concentra no SDK do MediaSession. Conceitos não-relevantes e blocos de código (incluindo detalhes sobre a implementação do ExoPlayer) são fornecidos para você copiar e colar, mas não serão abordados.

Pré-requisitos

  • Uma versão recente do Android Studio (3.5 ou mais recente)
  • Conhecimento básico sobre o desenvolvimento de apps Android

Qual é nosso ponto de partida?

Nosso ponto de partida é a principal demonstração do ExoPlayer. Esta demonstração contém vídeos com controles de reprodução na tela, mas não usa sessões de mídia prontas para uso. É um ótimo lugar para entrar e adicioná-las!

Receba a amostra do ExoPlayer

Para começar, vamos usar a amostra do ExoPlayer. Clone o repositório do GitHub neste link:

Clone o exemplo do ExoPlayer

Abrir a demonstração

No Android Studio, abra o projeto de demonstração principal, localizado em demos/main.

O Android Studio solicitará que você defina o caminho do SDK. Siga as recomendações para atualizar as ferramentas e o ambiente de desenvolvimento integrado se encontrar problemas.

Se for solicitado que você use a versão mais recente do Gradle, atualize-a.

Reserve um tempo para entender melhor como o app foi projetado. Observe que há duas atividades: SampleChooserActivity e PlayerActivity. Passaremos o restante do codelab em PlayerActivity, onde a mídia realmente é reproduzida, então abra esta classe e vá para a próxima seção.

Criar a sessão de mídia

Abra o PlayerActivity.java Essa classe cria o ExoPlayer e gerencia as funções dele, como renderizar vídeos na tela. Nesta atividade, conectaremos o ExoPlayer a uma sessão de mídia.

Declare os dois campos a seguir na parte superior da classe. Usaremos esses campos ao longo desta seção.

private MediaSessionCompat mediaSession;
private MediaSessionConnector mediaSessionConnector;

Será necessário adicionar a dependência do projeto "extension-mediasession" ao build.gradle do módulo para "Module: demo":

implementation project(path: ':extension-mediasession')

O Android Studio pode ajudar a adicionar essa dependência automaticamente se você passar o mouse sobre o erro de resolução do MediaSessionConnector:

Por fim, resolva as importações de classe adicionando o seguinte:

import android.support.v4.media.session.MediaSessionCompat;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;

Ao criar a atividade, criaremos uma sessão de mídia e um conector de sessão de mídia que atua como intermediário entre a sessão e o ExoPlayer.

O lugar ideal para inserir isso é onde o ExoPlayer também é criado. Em nosso app de demonstração, é possível anexar nosso código ao final de initializePlayer(). Adicione essa lógica depois que o player for instanciado.

private void initializePlayer() {
  if (player == null) {
    ...
    player = ...
    ...
    mediaSession = new MediaSessionCompat(this, "sample");
    mediaSessionConnector = new MediaSessionConnector(mediaSession);
    mediaSessionConnector.setPlayer(player);
  }
  ...
}

Liberar a sessão de mídia

Libere a sessão de mídia quando ela não for mais necessária. Quando lançamos o ExoPlayer em releasePlayer(), também podemos incluir o seguinte código para fazer isso:

private void releasePlayer() {
  if (mediaSession != null) {
    mediaSession.release();
  }
  ...
}

Gerenciar o estado da sessão de mídia

Agora que instanciamos a sessão de mídia, precisamos garantir que o estado dela seja refletido corretamente à medida que o usuário interage com a atividade.

Quando o usuário inicia a atividade, a sessão de mídia precisa se tornar ativa:

@Override
public void onStart() {
  ...
  if (mediaSession != null) {
    mediaSession.setActive(true);
  }
}

Como nosso aplicativo não reproduz mídia em segundo plano, é essencial garantir que a sessão de mídia fique inativa conforme o usuário sai da atividade:

@Override
public void onStop() {
  super.onStop();
  if (mediaSession != null) {
    mediaSession.setActive(false);
  }
  ...
}

Vamos executar a demonstração

  1. Anexe um dispositivo Android ou inicie um emulador.
  2. Verifique se a opção "demonstração" está selecionada para execução na barra de ferramentas do Android Studio.
  3. Clique em na barra de ferramentas do Android Studio.
  4. Quando o app for iniciado no dispositivo, selecione um stream de vídeo para assistir.
  5. Quando a reprodução começar, veja os seguintes comandos adb para controlar a sessão de mídia:
    adb shell media dispatch pause
    adb shell media dispatch play
    adb shell media dispatch play-pause
    adb shell media dispatch fast-forward
    adb shell media dispatch rewind
  1. Explore também como o Android vê sua sessão de mídia executando:
    adb shell dumpsys media_session
  2. Se você estiver usando um dispositivo físico com um microfone, tente invocar o Google Assistente e emitir comandos de voz, como:
    "Pausar."
    "Retomar."
    "Avançar 1 minuto."


Exemplo do ExoPlayer em execução no Android TV.

Agora, podemos ampliar os recursos compatíveis da nossa sessão de mídia, onde criamos o MediaSessionConnector no initializePlayer().

Como adicionar um TimelineQueueNavigator

O ExoPlayer representa a estrutura de mídia como uma linha do tempo. Para ver detalhes sobre como isso funciona, leia sobre o objeto Timeline do ExoPlayer. Ao tocar na estrutura, podemos ser avisados quando o conteúdo mudar e expor os metadados do que está sendo reproduzido quando solicitado.

Para isso, vamos definir um TimelineQueueNavigator. Localize a instanciação do MediaSessionConnector em initializePlayer() e adicione uma implementação de TimelineQueueNavigator depois da mediaSession ser inicializada.

mediaSessionConnector.setQueueNavigator(new TimelineQueueNavigator(mediaSession) {
  @Override
  public MediaDescriptionCompat getMediaDescription(Player player, int windowIndex) {
    return new MediaDescriptionCompat.Builder()
            .setTitle("MediaDescription title")
            .setDescription("MediaDescription description for " + windowIndex)
            .setSubtitle("MediaDescription subtitle")
            .build();
  }
});

Para resolver as importações de classe, adicione:

import android.support.v4.media.MediaDescriptionCompat;
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator;

Observe que o parâmetro windowIndex corresponde ao item desse índice na fila de reprodução.

Agora que você adicionou alguns metadados, teste se o Assistente entende o que está tocando. Ao reproduzir um vídeo no Android TV, invoque o Assistente e pergunte "O que está tocando?"

É possível que seu player não seja compatível com algumas ações, ou você queira incluir compatibilidade para outras opções. Agora vamos aprofundar a sessão de mídia em que criamos o MediaSessionConnector no initializePlayer().

Como declarar ações compatíveis

Tente usar o mediaSessionConnector.setEnabledPlaybackActions() para personalizar as ações que você quer que a sessão de mídia aceite.

Observe que o conjunto completo é:

mediaSessionConnector.setEnabledPlaybackActions(
        PlaybackStateCompat.ACTION_PLAY_PAUSE
                | PlaybackStateCompat.ACTION_PLAY
                | PlaybackStateCompat.ACTION_PAUSE
                | PlaybackStateCompat.ACTION_SEEK_TO
                | PlaybackStateCompat.ACTION_FAST_FORWARD
                | PlaybackStateCompat.ACTION_REWIND
                | PlaybackStateCompat.ACTION_STOP
                | PlaybackStateCompat.ACTION_SET_REPEAT_MODE
                | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
);

Vejamos novamente como esses dados são expostos à plataforma:

  1. Como antes, inicie um vídeo.
  2. Para ver como o Android vê os metadados da sessão de mídia, execute:
    adb shell dumpsys media_session
  3. Localize a linha que contém metadados e observe que o título e a descrição estão incluídos e associados a com.google.android.exoplayer2.demo/sample.

Adicionar outras ações

É possível ampliar nossa sessão de mídia com algumas ações adicionais. Nesta seção, adicionaremos compatibilidade apenas para legendas.

Compatibilidade com legendas

Adicionar compatibilidade a legendas em sessões de mídia permite que os usuários as ativem ou desativem por voz. Onde você inicializou o conector de sessão de mídia, adicione o seguinte:

mediaSessionConnector.setCaptionCallback(new MediaSessionConnector.CaptionCallback() {
      @Override
      public void onSetCaptioningEnabled(Player player, boolean enabled) {
        Log.d("MediaSession", "onSetCaptioningEnabled: enabled=" + enabled);
      }

      @Override
      public boolean hasCaptions(Player player) {
        return true;
      }

      @Override
      public boolean onCommand(Player player, ControlDispatcher controlDispatcher, String command, Bundle extras, ResultReceiver cb) {
        return false;
      }
    }
);

Por fim, resolva todas as importações ausentes.

É possível testar isso invocando o Google Assistente no Android TV e dizendo "Ativar legendas". Verifique o Logcat para ver mensagens e saber como isso é chamado no seu código.

Parabéns! Você adicionou sessões de mídia à amostra com sucesso.

Você acrescentou uma enorme quantidade de funcionalidades ao:

  • adicionar uma sessão de mídia;
  • conectar sessões de mídia a uma instância do ExoPlayer;
  • adicionar metadados e outras ações.

Agora você sabe as principais etapas necessárias para melhorar um aplicativo de mídia e oferecer aos usuários uma experiência mais versátil!

Uma última observação

Este codelab foi construído com base em um exemplo do código-fonte do ExoPlayer. Não é necessário usar o ExoPlayer do código-fonte. Recomendamos que você extraia as dependências do ExoPlayer e do MediaSessionConnector para facilitar a atualização com as versões mais recentes.

Para isso, basta substituir as dependências do projeto, como:

implementation project(modulePrefix + 'library-core')
implementation project(path: ':extension-mediasession')

para extrair de repositórios Maven, como:

implementation 'com.google.android.exoplayer:exoplayer-core:2.+'
implementation 'com.google.android.exoplayer:extension-mediasession:2.+'

Documentos de referência