Transmissão ao vivo

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

Detecção e monitoramento de reproduções ao vivo

Sempre que uma janela em tempo real é 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 mais adiante e mostrado na figura a seguir.

API de consulta de janela em tempo real
Figura 1. Janela em tempo real.
  • 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 é verdadeiro para transmissões ao vivo que ainda não terminaram. Essa flag também é verdadeira para transmissões nã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 o tamanho 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 o deslocamento em tempo real de destino e os parâmetros de ajuste de deslocamento 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.getCurrentMediaItemIndex e Timeline.getWindow. No Window:
    • Window.liveConfiguration contém os parâmetros de ajuste de deslocamento em tempo real e deslocamento em tempo real de destino. Esses valores são baseados em informações na mídia e em todas as substituições fornecidas pelo app definidas em MediaItem.liveConfiguration.
    • Window.windowStartTimeMs é o tempo desde a época Unix em que a janela ao vivo começa.
    • Window.getCurrentUnixTimeMs é o tempo desde a época 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 vai iniciar a reprodução por padrão.

Busca em transmissões ao vivo

É possível buscar qualquer lugar na janela ao vivo usando Player.seekTo. A posição de busca transmitida é relativa ao início da janela ao vivo. Por exemplo, seekTo(0) vai buscar o início da janela ao vivo. O player vai tentar manter o mesmo deslocamento em tempo real que a posição buscada 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 próxima à borda em tempo real. É possível buscar a posição padrão chamando Player.seekToDefaultPosition.

Interface de reprodução ao vivo

Os componentes de IU 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 parece voltar cada vez que a janela ao vivo é atualizada. Se você precisar de um comportamento diferente, por exemplo, mostrar o horário da época Unix ou o deslocamento atual, faça uma bifurcação de PlayerControlView e modifique-o de acordo com 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 ativa e o intervalo de velocidades do vídeo que podem ser usadas 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 em tempo real de destino. O player vai tentar se aproximar desse deslocamento em tempo real durante a reprodução, se possível.
  • minOffsetMs: o deslocamento mínimo permitido para transmissões ao vivo. 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 vai tentar exceder esse deslocamento durante a reprodução.
  • minPlaybackSpeed: a velocidade mínima do vídeo que o player pode usar para voltar ao tentar alcançar o deslocamento de transmissão ao vivo desejado.
  • maxPlaybackSpeed: a velocidade máxima do vídeo que o player pode usar para acompanhar ao tentar alcançar o deslocamento em tempo real desejado.

Ajuste da velocidade do vídeo

Ao reproduzir uma transmissão ao vivo de baixa latência, o ExoPlayer ajusta o deslocamento ao vivo mudando um pouco a velocidade do vídeo. O player vai tentar corresponder ao deslocamento em tempo real fornecido pela mídia ou pelo app, mas também vai tentar reagir às mudanças nas condições da rede. Por exemplo, se ocorrerem novos armazenamentos em buffer durante a reprodução, o player vai desacelerar um pouco a reprodução para se afastar do ponto de transmissão ao vivo. Se a rede se tornar estável o suficiente para oferecer suporte à reprodução mais próxima da extremidade ativa novamente, o player vai acelerar a reprodução para voltar ao deslocamento de transmissão ao vivo desejado.

Se o ajuste automático da velocidade do vídeo não for necessário, ele poderá ser desativado definindo as propriedades minPlaybackSpeed e maxPlaybackSpeed como 1.0f. Da mesma forma, é possível ativar o recurso para transmissões ao vivo que não são de baixa latência definindo-as explicitamente como valores diferentes de 1.0f. Consulte a seção de configuração para mais detalhes sobre como essas propriedades podem ser definidas.

Como 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 mínima e máxima de reprodução 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 em tempo real de destino sempre que ocorre um novo armazenamento em buffer, 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 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 melhores condições de rede, enquanto um valor menor significa que a estimativa se ajustará mais rapidamente com um risco maior de sofrer com o rebuffer.

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 estiver pausado ou armazenando em buffer por um período longo o suficiente. Se isso acontecer, a reprodução vai falhar, e uma exceção com o código do erro ERROR_CODE_BEHIND_LIVE_WINDOW será informada usando Player.Listener.onPlayerError. Para lidar com esses erros, retome 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
  }
}