Transmissão ao vivo

O ExoPlayer reproduz a maioria das transmissões ao vivo adaptáveis sem configuração especial. Consulte a página de 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 acompanhar o tempo real atual. Isso significa que a posição de reprodução sempre estará em algum lugar dessa janela, na maioria dos casos, perto do tempo real atual em que o stream está sendo produzido. A diferença entre a posição atual em tempo real e a posição de reprodução é chamada de deslocamento ao vivo.

Detectar e monitorar reproduções ao vivo

Sempre que uma janela dinâmica é atualizada, as instâncias Player.Listener registradas recebem um evento onTimelineChanged. É possível recuperar detalhes sobre a reprodução ao vivo atual consultando vários métodos Player e Timeline.Window, conforme listado abaixo e mostrado na figura a seguir.

Janela ativa

  • Player.isCurrentWindowLive indica se o item de mídia em reprodução é uma transmissão ao vivo. Esse valor ainda será verdadeiro mesmo que a transmissão ao vivo tenha terminado.
  • Player.isCurrentWindowDynamic indica se o item de mídia em reprodução ainda está sendo atualizado. Isso geralmente é válido para transmissões ao vivo que ainda não terminaram. Essa flag 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 de 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 relativa 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 e de posição ao vivo de destino.
  • Player.getCurrentTimeline retorna a estrutura de mídia atual em um Timeline. O Timeline.Window atual pode ser recuperado do Timeline usando Player.getCurrentMediaItemIndex e Timeline.getWindow. No Window:
    • Window.liveConfiguration contém os parâmetros de ajuste de posição ao vivo e posição ao vivo de destino. Esses valores são baseados nas informações da mídia e em substituições fornecidas pelo app definidas em MediaItem.liveConfiguration.
    • Window.windowStartTimeMs é o tempo desde a época do Unix em que a janela de transmissão ao vivo começa.
    • Window.getCurrentUnixTimeMs é o tempo desde a época do 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 da transmissão ao vivo em que o player vai iniciar a reprodução por padrão.

Como buscar em transmissões ao vivo

Você pode procurar qualquer lugar na janela ao vivo usando Player.seekTo. A posição de busca transmitida é relativa ao início da janela de transmissão ao vivo. Por exemplo, seekTo(0) vai para o início da janela de transmissão ao vivo. O player vai tentar manter o mesmo deslocamento ao vivo da posição procurada após uma busca.

A janela ao vivo também tem uma posição padrão em que a reprodução deve começar. Essa posição geralmente fica perto da borda ativa. Para buscar a posição padrão, chame Player.seekToDefaultPosition.

Interface de reprodução ao vivo

Os componentes de UI padrão do ExoPlayer mostram a duração da janela ao vivo e a posição de reprodução atual nela. 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, por exemplo, mostrar o horário Unix ou o deslocamento ao vivo atual, faça um fork de PlayerControlView e modifique-o 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 da borda dinâmica e o intervalo de velocidades de reprodução que podem ser usados para ajustar esse deslocamento.

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

  • Por valores de MediaItem transmitidos para MediaItem.Builder.setLiveConfiguration.
  • Valores padrão globais definidos em DefaultMediaSourceFactory.
  • Valores 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 de destino da transmissão ao vivo. O player tentará se aproximar desse deslocamento ao vivo durante a reprodução, se possível.
  • minOffsetMs: o menor valor de deslocamento ao vivo permitido. Mesmo ao ajustar o deslocamento para as condições atuais da rede, o player não tentará ficar abaixo desse deslocamento durante a reprodução.
  • maxOffsetMs: o deslocamento máximo permitido para transmissões ao vivo. Mesmo ao ajustar o deslocamento para as condições atuais da rede, o player não tentará ficar acima desse deslocamento durante a reprodução.
  • minPlaybackSpeed: a velocidade mínima do vídeo que o player pode usar como alternativa ao tentar alcançar o deslocamento de transmissão ao vivo desejado.
  • maxPlaybackSpeed: a velocidade do vídeo máxima que o player pode usar para alcançar ao tentar atingir o tempo de transmissão ao vivo desejado.

Ajuste da velocidade do vídeo

Ao reproduzir uma transmissão ao vivo de baixa latência, o ExoPlayer ajusta o deslocamento da transmissão mudando um pouco a velocidade do vídeo. O player vai tentar corresponder ao deslocamento ao vivo de destino fornecido pela mídia ou pelo app, mas também vai tentar reagir a condições de rede variáveis. Por exemplo, se ocorrerem rebufferings durante a reprodução, o player vai diminuir um pouco a velocidade para se afastar mais da borda ao vivo. Se a rede ficar estável o suficiente para permitir a reprodução mais próxima da extremidade ativa, o player vai acelerar a reprodução para voltar ao deslocamento ativo desejado.

Se você não quiser o ajuste automático da velocidade do vídeo, desative-o definindo as propriedades minPlaybackSpeed e maxPlaybackSpeed como 1.0f. Da mesma forma, é possível ativar para transmissões ao vivo sem baixa latência definindo esses valores explicitamente como 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 do vídeo

Se o ajuste de velocidade estiver ativado, um LivePlaybackSpeedControl vai definir quais ajustes serão 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 de reprodução mínima e máxima que podem ser usadas para ajuste se nem a mídia nem o MediaItem fornecido pelo app definirem limites.
  • proportionalControlFactor: controla a suavidade do ajuste de velocidade. Um valor alto torna os ajustes mais repentinos e reativos, mas também mais propensos a serem audíveis. Um valor menor resulta em uma transição mais suave entre as velocidades, mas é mais lento.
  • targetLiveOffsetIncrementOnRebufferMs: esse valor é adicionado ao deslocamento dinâmico de destino sempre que ocorre um rebuffer, para prosseguir com mais cautela. Esse recurso pode ser desativado definindo o valor como 0.
  • minPossibleLiveOffsetSmoothingFactor: um fator de suavização exponencial usado para rastrear o menor deslocamento possível em tempo real 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 a condições de rede aprimoradas. Já um valor menor significa que a estimativa será ajustada mais rapidamente, com um risco maior de encontrar rebuffers.

BehindLiveWindowException e ERROR_CODE_BEHIND_LIVE_WINDOW

A posição de reprodução pode ficar atrás da janela ao vivo, por exemplo, se o player ficar pausado ou em buffer por um período de tempo longo o 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
  }
}