Transmissão ao vivo

O ExoPlayer reproduz a maioria das transmissões ao vivo adaptáveis imediatamente, sem nenhuma configuração especial. Consulte a página Formatos compatíveis para mais detalhes.

As transmissões ao vivo adaptáveis oferecem uma janela de mídia disponível que é atualizada em intervalos regulares para se mover de acordo com o tempo real atual. Isso significa que a posição da reprodução sempre estará em algum lugar nessa janela, na maioria dos casos próxima ao tempo real atual em que o stream está sendo produzido. A diferença entre o tempo real atual e a posição da reprodução é chamada de deslocamento em tempo real.

Como detectar e monitorar reproduções ao vivo

Sempre que uma janela ativa for atualizada, as instâncias Player.Listener registradas vão receber um evento onTimelineChanged. Você pode extrair detalhes sobre a reprodução ao vivo atual consultando vários métodos Player e Timeline.Window, conforme listado abaixo e mostrado na figura abaixo.

Janela ativa

  • Player.isCurrentWindowLive indica se o item de mídia em reprodução é uma transmissão ao vivo. Esse valor ainda é verdadeiro, mesmo que a transmissão ao vivo tenha terminado.
  • Player.isCurrentWindowDynamic indica se o item de mídia em reprodução no momento ainda está sendo atualizado. Isso geralmente acontece com transmissões ao vivo que ainda não terminaram. Essa sinalização também é verdadeira para transmissões que não são ao vivo em alguns casos.
  • Player.getCurrentLiveOffset retorna o deslocamento entre o tempo real atual e a posição da reprodução (se disponível).
  • Player.getDuration retorna a duração da janela ao vivo atual.
  • Player.getCurrentPosition retorna a posição de reprodução em relação ao início da janela ao vivo.
  • Player.getCurrentMediaItem retorna o item de mídia atual, em que MediaItem.liveConfiguration contém substituições fornecidas pelo app para os parâmetros de ajuste de deslocamento em tempo real e em tempo real.
  • Player.getCurrentTimeline retorna a estrutura de mídia atual em um Timeline. O Timeline.Window atual pode ser recuperado do Timeline usando Player.getCurrentWindowIndex e Timeline.getWindow. No Window:
    • Window.liveConfiguration contém os parâmetros de ajuste e deslocamento em tempo real desejado. Esses valores são baseados nas informações na mídia e em todas as substituições fornecidas pelo app definidas em MediaItem.liveConfiguration.
    • Window.windowStartTimeMs é o tempo desde a era Unix em que a janela ativa é iniciada.
    • Window.getCurrentUnixTimeMs é o tempo desde a era Unix do tempo real atual. Esse valor pode ser corrigido por uma diferença de relógio conhecida entre o servidor e o cliente.
    • Window.getDefaultPositionMs é a posição na janela ao vivo em que o player iniciará a reprodução por padrão.

Procurando em transmissões ao vivo

É possível procurar qualquer lugar na janela ao vivo usando Player.seekTo. A posição de busca transmitida é relativa ao início da janela ativa. Por exemplo, seekTo(0) buscará o início da janela ativa. O jogador vai tentar manter o mesmo deslocamento em tempo real da posição de destino após uma busca.

A janela ao vivo também tem uma posição padrão em que a reprodução precisa ser iniciada. Essa posição geralmente fica em algum lugar perto da borda em tempo real. Procure a posição padrão chamando Player.seekToDefaultPosition.

interface de reprodução ao vivo

Os componentes de interface padrão do ExoPlayer mostram a duração da janela ao vivo e a posição de reprodução atual dentro dela. Isso significa que a posição vai parecer pular para trás sempre que a janela ao vivo for atualizada. Se você precisar de um comportamento diferente, como mostrar o horário Unix ou o deslocamento em tempo real atual, é possível bifurcar PlayerControlView e modificá-lo para atender às suas necessidades.

Como configurar parâmetros de reprodução ao vivo

O ExoPlayer usa alguns parâmetros para controlar o deslocamento da posição de reprodução em relação à borda ativa e o intervalo de velocidades de reprodução que podem ser usadas para ajustar esse deslocamento.

O ExoPlayer recebe valores para esses parâmetros de três lugares, em ordem de prioridade decrescente (o primeiro valor encontrado é usado):

  • Valores por MediaItem transmitidos para MediaItem.Builder.setLiveConfiguration.
  • Valores padrão globais definidos em DefaultMediaSourceFactory.
  • Os valores são lidos diretamente da mídia.

Kotlin

// Global settings.
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000))
    .build()

// Per MediaItem settings.
val mediaItem =
  MediaItem.Builder()
    .setUri(mediaUri)
    .setLiveConfiguration(
      MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build()
    )
    .build()
player.setMediaItem(mediaItem)

Java

// Global settings.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000))
        .build();

// Per MediaItem settings.
MediaItem mediaItem =
    new MediaItem.Builder()
        .setUri(mediaUri)
        .setLiveConfiguration(
            new MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build())
        .build();
player.setMediaItem(mediaItem);

Os valores de configuração disponíveis são:

  • targetOffsetMs: o deslocamento em tempo real desejado. O player tentará se aproximar desse deslocamento em tempo real durante a reprodução, se possível.
  • minOffsetMs: o deslocamento em tempo real mínimo permitido. Mesmo ao ajustar o deslocamento para as condições atuais da rede, o player não vai tentar ficar abaixo desse deslocamento durante a reprodução.
  • maxOffsetMs: o deslocamento em tempo real máximo permitido. Mesmo ao ajustar o deslocamento para as condições atuais da rede, o player não tentará ultrapassar esse deslocamento durante a reprodução.
  • minPlaybackSpeed: a velocidade mínima de reprodução que o player pode usar para voltar ao tentar alcançar o deslocamento em tempo real desejado.
  • maxPlaybackSpeed: a velocidade máxima de reprodução que o player pode usar para atualizar ao tentar alcançar o deslocamento em tempo real desejado.

Ajuste da velocidade da reprodução

Ao reproduzir uma transmissão ao vivo de baixa latência, o ExoPlayer ajusta o deslocamento ao vivo mudando um pouco a velocidade de reprodução. O player tentará corresponder ao deslocamento em tempo real de destino fornecido pela mídia ou pelo app, mas também tentará reagir às mudanças nas condições da rede. Por exemplo, se ocorrerem novos buffers durante a reprodução, o player vai desacelerar um pouco a reprodução para se afastar da borda ao vivo. Se a rede se tornar estável o suficiente para permitir a reprodução mais perto da borda ativa novamente, o player vai acelerar a reprodução para voltar ao compensação em tempo real de destino.

Se não quiser o ajuste automático de velocidade de reprodução, ele pode ser desativado configurando as propriedades minPlaybackSpeed e maxPlaybackSpeed como 1.0f. Da mesma forma, ela pode ser ativada para transmissões ao vivo de não baixa latência configurando explicitamente com valores diferentes de 1.0f. Consulte a seção de configuração acima para mais detalhes sobre como essas propriedades podem ser definidas.

Personalizar o algoritmo de ajuste da velocidade de reprodução

Se o ajuste de velocidade estiver ativado, uma LivePlaybackSpeedControl define os ajustes feitos. É possível implementar um LivePlaybackSpeedControl personalizado ou personalizar a implementação padrão, que é DefaultLivePlaybackSpeedControl. Em ambos os casos, uma instância pode ser definida ao criar o player:

Kotlin

val player =
  ExoPlayer.Builder(context)
    .setLivePlaybackSpeedControl(
      DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.04f).build()
    )
    .build()

Java

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setLivePlaybackSpeedControl(
            new DefaultLivePlaybackSpeedControl.Builder()
                .setFallbackMaxPlaybackSpeed(1.04f)
                .build())
        .build();

Os parâmetros de personalização relevantes de DefaultLivePlaybackSpeedControl são:

  • fallbackMinPlaybackSpeed e fallbackMaxPlaybackSpeed: as velocidades mínima e máxima de reprodução que podem ser usadas para ajuste se nem a mídia nem a MediaItem fornecida pelo app definirem limites.
  • proportionalControlFactor: controla a fluidez do ajuste de velocidade. Um valor alto torna os ajustes mais repentinos e reativos, mas também aumenta a probabilidade de eles serem audíveis. Um valor menor resulta em uma transição mais suave entre as velocidades, mas fica mais lenta.
  • targetLiveOffsetIncrementOnRebufferMs: esse valor é adicionado ao deslocamento em tempo real desejado sempre que ocorrer um novo buffer para prosseguir com mais cautela. Esse recurso pode ser desativado definindo o valor como 0.
  • minPossibleLiveOffsetSmoothingFactor: um fator de suavização exponencial que é usado para rastrear o deslocamento em tempo real mínimo possível com base na mídia armazenada em buffer no momento. Um valor muito próximo de 1 significa que a estimativa é mais cautelosa e pode levar mais tempo para se ajustar às condições aprimoradas da rede, enquanto um valor menor significa que a estimativa será ajustada mais rapidamente, com um risco maior de encontrar novos buffers.

Atrás daLiveWindowException e de ERROR_CODE_ automática - LIVE_WINDOW

A posição de reprodução pode ficar atrasada em relação à janela ao vivo, por exemplo, se o player estiver pausado ou armazenando em buffer por um período suficiente. Se isso acontecer, a reprodução vai falhar, e uma exceção com o código de erro ERROR_CODE_BEHIND_LIVE_WINDOW será informada por Player.Listener.onPlayerError. O código do aplicativo pode querer processar esses erros retomando a reprodução na posição padrão. A PlayerActivity do app de demonstração exemplifica essa abordagem.

Kotlin

override fun onPlayerError(error: PlaybackException) {
  if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
    // Re-initialize player at the live edge.
    player.seekToDefaultPosition()
    player.prepare()
  } else {
    // Handle other errors
  }
}

Java

@Override
public void onPlayerError(PlaybackException error) {
  if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
    // Re-initialize player at the live edge.
    player.seekToDefaultPosition();
    player.prepare();
  } else {
    // Handle other errors
  }
}