HLS

O ExoPlayer oferece suporte ao HLS com vários formatos de contêiner. Os formatos de amostra de áudio e vídeo contidos também precisam ser compatíveis. Consulte a seção Formatos de amostra para saber mais. Recomendamos que os produtores de conteúdo HLS gerem transmissões HLS de alta qualidade, conforme descrito nesta postagem do blog.

Recurso Compatível Comentários
Contêineres
MPEG-TS SIM
FMP4/CMAF SIM
ADTS (AAC) SIM
MP3 SIM
Legendas ocultas / legendas
CEA-608 SIM
CEA-708 SIM
WebVTT SIM
Metadados
ID3 SIM
SCTE-35 NÃO
Proteção de conteúdo
AES-128 SIM
Exemplo de AES-128 NÃO
Widevine SIM API 19+ (esquema "cenc") e 25+ (esquema "cbcs")
PlayReady SL2000 SIM Somente no Android TV
Controle do servidor
Atualizações delta SIM
Bloquear a recarga da playlist SIM
Como bloquear o carregamento de dicas de pré-carregamento SIM Exceto para byteranges com comprimentos indefinidos
Inserção de anúncios
Inserção de anúncios guiada pelo servidor (intersticiais) Parcialmente Somente VOD com X-ASSET-URI. As transmissões ao vivo e X-ASSET-LIST serão adicionadas mais tarde.
Anúncios do lado do servidor e do lado do cliente do IMA SIM Guia de inserção de anúncios
Reprodução ao vivo
Reprodução regular ao vivo SIM
HLS de baixa latência (Apple) SIM
HLS de baixa latência (Comunidade) NÃO
Dados comuns do cliente de mídia CMCD SIM Guia de integração do CMCD

Como usar o MediaItem

Para reproduzir uma transmissão HLS, você precisa depender do módulo HLS.

Kotlin

implementation("androidx.media3:media3-exoplayer-hls:1.6.0")

Groovy

implementation "androidx.media3:media3-exoplayer-hls:1.6.0"

Em seguida, crie um MediaItem para um URI de playlist HLS e transmita-o ao reprodutor.

Kotlin

// Create a player instance.
val player = ExoPlayer.Builder(context).build()
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri))
// Prepare the player.
player.prepare()

Java

// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri));
// Prepare the player.
player.prepare();

Se o URI não terminar com .m3u8, você poderá transmitir MimeTypes.APPLICATION_M3U8 para setMimeType de MediaItem.Builder para indicar explicitamente o tipo de conteúdo.

O URI do item de mídia pode apontar para uma playlist de mídia ou uma playlist multivariante. Se o URI apontar para uma playlist multivariante que declare várias tags #EXT-X-STREAM-INF, o ExoPlayer vai se adaptar automaticamente entre variantes, levando em conta a largura de banda disponível e os recursos do dispositivo.

Como usar o HlsMediaSource

Para mais opções de personalização, crie uma HlsMediaSource e transmita-a diretamente ao player em vez de uma MediaItem.

Kotlin

// Create a data source factory.
val dataSourceFactory: DataSource.Factory = DefaultHttpDataSource.Factory()
// Create a HLS media source pointing to a playlist uri.
val hlsMediaSource =
  HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri))
// Create a player instance.
val player = ExoPlayer.Builder(context).build()
// Set the HLS media source as the playlist with a single media item.
player.setMediaSource(hlsMediaSource)
// Prepare the player.
player.prepare()

Java

// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
// Create a HLS media source pointing to a playlist uri.
HlsMediaSource hlsMediaSource =
    new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the HLS media source as the playlist with a single media item.
player.setMediaSource(hlsMediaSource);
// Prepare the player.
player.prepare();

Como acessar o manifesto

É possível extrair o manifesto atual chamando Player.getCurrentManifest. Para HLS, você precisa transmitir o objeto retornado para HlsManifest. O callback onTimelineChanged de Player.Listener também é chamado sempre que o manifesto é carregado. Isso vai acontecer uma vez para conteúdo on demand e talvez muitas vezes para conteúdo ao vivo. O snippet de código abaixo mostra como um app pode fazer algo sempre que o manifesto é carregado.

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onTimelineChanged(timeline: Timeline, @TimelineChangeReason reason: Int) {
      val manifest = player.currentManifest
      if (manifest is HlsManifest) {
        // Do something with the manifest.
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onTimelineChanged(
          Timeline timeline, @Player.TimelineChangeReason int reason) {
        Object manifest = player.getCurrentManifest();
        if (manifest != null) {
          HlsManifest hlsManifest = (HlsManifest) manifest;
          // Do something with the manifest.
        }
      }
    });

Reproduzir transmissões HLS com anúncios intersticiais

A especificação HLS define os intersticiais HLS, que podem ser usados para incluir informações intersticiais em uma playlist de mídia. O ExoPlayer ignora esses intersticiais por padrão. O suporte pode ser adicionado usando HlsInterstitialsAdsLoader. Não oferecemos suporte a todos os recursos da especificação desde o início. Se você não tiver suporte para seu stream, registre um problema no GitHub e envie seu URI para que possamos adicionar suporte.

Usar um MediaItem com a API playlist

A maneira mais conveniente de reproduzir fluxos HLS com intersticiais é criar uma instância do ExoPlayer com um HlsInterstitialsAdsLoader.AdsMediaSourceFactory. Isso permite usar a API de playlist baseada em MediaItem da interface Player para reproduzir anúncios intersticiais HLS.

O MediaSource.Factory do ExoPlayer pode ser injetado no builder ao criar a instância do player:

Kotlin

hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
var hlsMediaSourceFactory =
  HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
    hlsInterstitialsAdsLoader,
    playerView,
    DefaultMediaSourceFactory(context),
  )

// Build player with interstitials media source factory
player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(hlsMediaSourceFactory)
    .build()

// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player)
playerView.setPlayer(player)

Java

hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Create a MediaSource.Factory for HLS streams with interstitials.
MediaSource.Factory hlsMediaSourceFactory =
      new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
          hlsInterstitialsAdsLoader, playerView, new DefaultMediaSourceFactory(context));

// Build player with interstitials media source factory
player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(hlsMediaSourceFactory)
        .build();

// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player);
playerView.setPlayer(player);

Com essa configuração, a reprodução de intersticiais HLS é apenas sobre definir um item de mídia com um AdsConfiguration no player:

Kotlin

player.setMediaItem(
  MediaItem.Builder()
    .setUri("https://www.example.com/media.m3u8")
    .setAdsConfiguration(
      AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
        .setAdsId("ad-tag-0") // must be unique within playlist
        .build())
    .build())

player.prepare();
player.play();

Java

player.setMediaItem(
    new MediaItem.Builder()
        .setUri("https://www.example.com/media.m3u8")
        .setAdsConfiguration(
            new AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
                .setAdsId("ad-tag-0") // must be unique within playlist
                .build())
        .build());
player.prepare();
player.play();

Usar a API baseada na fonte de mídia

Como alternativa, a instância do ExoPlayer pode ser criada sem substituir a fábrica de origem de mídia padrão. Para oferecer suporte a intersticiais, um app pode usar HlsInterstitialsAdsLoader.AdsMediaSourceFactory diretamente para criar um MediaSource e fornecê-lo ao ExoPlayer usando a API de playlist baseada na fonte de mídia:

Kotlin

hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
var hlsMediaSourceFactory =
  HlsInterstitialsAdsLoader.AdsMediaSourceFactory(hlsInterstitialsAdsLoader, playerView, context)

// Build player with default media source factory.
player = new ExoPlayer.Builder(context).build();

// Create an media source from an HLS media item with ads configuration.
val mediaSource =
  hlsMediaSourceFactory.createMediaSource(
    MediaItem.Builder()
      .setUri("https://www.example.com/media.m3u8")
      .setAdsConfiguration(
        MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
          .setAdsId("ad-tag-0")
          .build()
      )
      .build()
  )

// Set the media source on the player.
player.setMediaSource(mediaSource)
player.prepare()
player.play()

Java

HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Create a MediaSource.Factory for HLS streams with interstitials.
MediaSource.Factory hlsMediaSourceFactory =
    new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
      hlsInterstitialsAdsLoader, playerView, context);

// Build player with default media source factory.
player = new ExoPlayer.Builder(context).build();

// Create an media source from an HLS media item with ads configuration.
MediaSource mediaSource =
    hlsMediaSourceFactory.createMediaSource(
      new MediaItem.Builder()
        .setUri("https://www.example.com/media.m3u8")
        .setAdsConfiguration(
            new MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
                .setAdsId("ad-tag-0")
                .build())
        .build());

// Set the media source on the player.
exoPlayer.setMediaSource(mediaSource);
exoPlayer.prepare();
exoPlayer.play();

Ouvir eventos de anúncios

Um Listener pode ser adicionado a HlsInterstitialsAdsLoader para monitorar eventos relacionados a mudanças de status relacionadas à reprodução de intersticiais HLS. Isso permite que um app ou SDK rastreie anúncios veiculados, listas de recursos sendo carregadas, origens de mídia de anúncios sendo preparadas ou detecte erros de carregamento da lista de recursos e de preparação de anúncios. Além disso, os metadados emitidos pelas origens de mídia de anúncios podem ser recebidos para verificação detalhada da reprodução de anúncios ou para acompanhar o progresso da reprodução.

Kotlin

class AdsLoaderListener : HlsInterstitialsAdsLoader.Listener {

  override fun onStart(mediaItem: MediaItem, adsId: Any, adViewProvider: AdViewProvider) {
    // Do something when HLS media item with interstitials is started.
  }

  override fun onMetadata(
    mediaItem: MediaItem,
    adsId: Any,
    adGroupIndex: Int,
    adIndexInAdGroup: Int,
    metadata: Metadata,
  ) {
    // Do something with metadata that is emitted by the ad media source of the given ad.
  }

  override fun onAdCompleted(
    mediaItem: MediaItem,
    adsId: Any,
    adGroupIndex: Int,
    adIndexInAdGroup: Int,
  ) {
    // Do something when ad completed playback.
  }

  // ... See JavaDoc for further callbacks of HlsInterstitialsAdsLoader.Listener.

  override fun onStop(mediaItem: MediaItem, adsId: Any, adPlaybackState: AdPlaybackState) {
    // Do something with the resulting ad playback state when stopped.
  }
}

Java

private class AdsLoaderListener
    implements HlsInterstitialsAdsLoader.Listener {

  // implement HlsInterstitialsAdsLoader.Listener

  @Override
  public void onStart(MediaItem mediaItem, Object adsId, AdViewProvider adViewProvider) {
    // Do something when HLS media item with interstitials is started.
  }

  @Override
  public void onMetadata(
      MediaItem mediaItem,
      Object adsId,
      int adGroupIndex,
      int adIndexInAdGroup,
      Metadata metadata) {
    // Do something with metadata that is emitted by the ad media source of the given ad.
  }

  @Override
  public void onAdCompleted(
      MediaItem mediaItem, Object adsId, int adGroupIndex, int adIndexInAdGroup) {
    // Do something when ad completed playback.
  }

  // ... See JavaDoc for further callbacks of HlsInterstitialsAdsLoader.Listener.

  @Override
  public void onStop(MediaItem mediaItem, Object adsId, AdPlaybackState adPlaybackState) {
    // Do something with the resulting ad playback state when stopped.
  }
}

O listener pode ser adicionado ao carregador de anúncios:

Kotlin

var listener  = AdsLoaderListener();
// Add the listener to the ads loader to receive ad loader events.
hlsInterstitialsAdsLoader.addListener(listener);

Java

AdsLoaderListener listener = new AdsLoaderListener();
// Add the listener to the ads loader to receive ad loader events.
hlsInterstitialsAdsLoader.addListener(listener);

Ciclo de vida do HlsInterstitialsAdsLoader

Uma instância de HlsInterstitialsAdsLoader ou HlsInterstitialsAdsLoader.AdsMediaSourceFactory pode ser reutilizada para várias instâncias de player que criam várias origens de mídia para as quais os anúncios precisam ser carregados.

Uma instância pode ser criada, por exemplo, no método onCreate de um Activity e reutilizada para várias instâncias de jogadores. Isso funciona desde que esteja em uso por uma única instância de jogador ao mesmo tempo. Isso é útil para o caso de uso comum em que o app é levado para segundo plano, a instância do player é destruída e uma nova instância é criada quando o app é colocado em primeiro plano novamente.

Kotlin

// Create the ads loader instance (for example onCreate).
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context);

// Build a player and set it on the ads loader (for example onStart).
player = ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);

// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);

// Build another player and set it on the ads loader (for example onStart).
player = ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);

// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);

// Release the ads loader when not used anymore  (for example onDestroy).
hlsInterstitialsAdsLoader.release();

Java

// Create the ads loader instance (for example onCreate).
hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);

// Build a player and set it on the ads loader (for example onStart).
player = new ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);

// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);

// Build another player and set it on the ads loader (for example onStart).
player = new ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);

// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);

// Release the ads loader when not used anymore  (for example onDestroy).
hlsInterstitialsAdsLoader.release();

Em geral, libere a instância antiga do player antes de definir a próxima instância do player no carregador de anúncios. Depois que o próprio carregador de anúncios é lançado, ele não pode mais ser usado.

Personalizar a reprodução

O ExoPlayer oferece várias maneiras de personalizar a experiência de reprodução de acordo com as necessidades do app. Consulte a página de personalização para conferir exemplos.

Como desativar a preparação sem fragmentos

Por padrão, o ExoPlayer vai usar a preparação sem separações em blocos. Isso significa que o ExoPlayer usará apenas as informações na playlist de variantes múltiplas para preparar o stream, o que funciona se as tags #EXT-X-STREAM-INF contiverem o atributo CODECS.

Talvez seja necessário desativar esse recurso se os segmentos de mídia contiverem faixas de legenda munidas que não forem declaradas na playlist multivariante com uma tag #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS. Caso contrário, essas faixas de legenda não serão detectadas e reproduzidas. É possível desativar a preparação sem blocos no HlsMediaSource.Factory, conforme mostrado no snippet abaixo. Isso vai aumentar o tempo de inicialização, já que o ExoPlayer precisa fazer o download de um segmento de mídia para descobrir essas faixas adicionais. É preferível declarar as faixas de legenda na playlist de variantes múltiplas.

Kotlin

val hlsMediaSource =
  HlsMediaSource.Factory(dataSourceFactory)
    .setAllowChunklessPreparation(false)
    .createMediaSource(MediaItem.fromUri(hlsUri))

Java

HlsMediaSource hlsMediaSource =
    new HlsMediaSource.Factory(dataSourceFactory)
        .setAllowChunklessPreparation(false)
        .createMediaSource(MediaItem.fromUri(hlsUri));

Criar conteúdo HLS de alta qualidade

Para aproveitar ao máximo o ExoPlayer, siga algumas diretrizes para melhorar seu conteúdo HLS. Leia nossa postagem no Medium sobre a reprodução de HLS no ExoPlayer para uma explicação completa. Os pontos principais são:

  • Use durações precisas dos segmentos.
  • Use uma transmissão de mídia contínua. Evite mudanças na estrutura de mídia em segmentos.
  • Use a tag #EXT-X-INDEPENDENT-SEGMENTS.
  • Prefira streams desmultiplexados, em vez de arquivos que incluem vídeo e áudio.
  • Inclua todas as informações possíveis na playlist multivariante.

As seguintes diretrizes se aplicam especificamente a transmissões ao vivo:

  • Use a tag #EXT-X-PROGRAM-DATE-TIME.
  • Use a tag #EXT-X-DISCONTINUITY-SEQUENCE.
  • Ofereça uma janela de longa duração. Um minuto ou mais é ótimo.