Inserção de anúncios

O ExoPlayer pode ser usado para inserção de anúncios do lado do cliente e do servidor.

Inserção de anúncios do lado do cliente

Na inserção de anúncios do lado do cliente, o player alterna entre o carregamento de mídia de diferentes URLs à medida que faz a transição entre a reprodução de conteúdo e anúncios. As informações sobre anúncios são carregadas separadamente da mídia, como de uma tag de anúncio XML VAST ou VMAP. Isso pode incluir posições de indicação de anúncio em relação ao início do conteúdo, os URIs de mídia de anúncio reais e metadados, como se um determinado anúncio pode ser pulado.

Ao usar o AdsMediaSource do ExoPlayer para inserção de anúncios do lado do cliente, o player tem informações sobre os anúncios a serem veiculados. Essa mudança gera vários benefícios:

  • O player pode expor metadados e funcionalidades relacionadas a anúncios usando a API dele.
  • Os componentes da interface do ExoPlayer podem mostrar marcadores para posições de anúncios automaticamente e mudar o comportamento dependendo se o anúncio está sendo veiculado.
  • Internamente, o player pode manter um buffer consistente nas transições entre anúncios e conteúdo.

Nessa configuração, o player cuida da troca entre anúncios e conteúdo, o que significa que os apps não precisam controlar vários players separados em segundo plano/primeiro plano para anúncios e conteúdo.

Ao preparar vídeos de conteúdo e tags de anúncio para uso com a inserção de anúncios do lado do cliente, os anúncios devem ser posicionados em amostras de sincronização (frames-chave) no vídeo de conteúdo para que o player possa retomar a reprodução de conteúdo sem problemas.

Suporte a anúncios declarativos

Um URI de tag de anúncio pode ser especificado ao criar um MediaItem:

Kotlin

val mediaItem =
  MediaItem.Builder()
    .setUri(videoUri)
    .setAdsConfiguration(MediaItem.AdsConfiguration.Builder(adTagUri).build())
    .build()

Java

MediaItem mediaItem =
    new MediaItem.Builder()
        .setUri(videoUri)
        .setAdsConfiguration(new MediaItem.AdsConfiguration.Builder(adTagUri).build())
        .build();

Para ativar o suporte do player a itens de mídia que especificam tags de anúncio, é necessário criar e injetar um DefaultMediaSourceFactory configurado com um AdsLoader.Provider e um AdViewProvider ao criar o player:

Kotlin

val mediaSourceFactory: MediaSource.Factory =
  DefaultMediaSourceFactory(context)
    .setLocalAdInsertionComponents(adsLoaderProvider, playerView)
val player = ExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build()

Java

MediaSource.Factory mediaSourceFactory =
    new DefaultMediaSourceFactory(context)
        .setLocalAdInsertionComponents(adsLoaderProvider, /* adViewProvider= */ playerView);
ExoPlayer player =
    new ExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build();

Internamente, DefaultMediaSourceFactory vai encapsular a origem de mídia de conteúdo em um AdsMediaSource. O AdsMediaSource vai receber um AdsLoader do AdsLoader.Provider e usá-lo para inserir anúncios conforme definido pela tag de anúncio do item de mídia.

O PlayerView do ExoPlayer implementa AdViewProvider. A biblioteca IMA do ExoPlayer fornece um AdsLoader fácil de usar, conforme descrito abaixo.

Playlists com anúncios

Ao reproduzir uma playlist com vários itens de mídia, o comportamento padrão é solicitar a tag de anúncio e armazenar o estado de reprodução uma vez para cada combinação de ID de mídia, URI de conteúdo e URI de tag de anúncio. Isso significa que os usuários vão ver anúncios para cada item de mídia com anúncios que tenha um ID de mídia ou URI de conteúdo distinto, mesmo que os URIs de tag de anúncio sejam iguais. Se um item de mídia for repetido, o usuário verá os anúncios correspondentes apenas uma vez. O estado de reprodução do anúncio armazena se os anúncios foram veiculados, para que sejam pulados após a primeira ocorrência.

É possível personalizar esse comportamento transmitindo um identificador de anúncios opaco com o qual o estado de reprodução de anúncios de um determinado item de mídia está vinculado, com base na igualdade de objetos. Este é um exemplo em que o estado de reprodução do anúncio é vinculado apenas ao URI da tag de anúncio, e não à combinação do ID da mídia e do URI da tag de anúncio, transmitindo o URI da tag de anúncio como o identificador de anúncios. O efeito é que os anúncios serão carregados apenas uma vez, e o usuário não verá anúncios no segundo item ao reproduzir a playlist do início ao fim.

Kotlin

// Build the media items, passing the same ads identifier for both items,
// which means they share ad playback state so ads play only once.
val firstItem =
  MediaItem.Builder()
    .setUri(firstVideoUri)
    .setAdsConfiguration(
      MediaItem.AdsConfiguration.Builder(adTagUri).setAdsId(adTagUri).build()
    )
    .build()
val secondItem =
  MediaItem.Builder()
    .setUri(secondVideoUri)
    .setAdsConfiguration(
      MediaItem.AdsConfiguration.Builder(adTagUri).setAdsId(adTagUri).build()
    )
    .build()
player.addMediaItem(firstItem)
player.addMediaItem(secondItem)

Java

// Build the media items, passing the same ads identifier for both items,
// which means they share ad playback state so ads play only once.
MediaItem firstItem =
    new MediaItem.Builder()
        .setUri(firstVideoUri)
        .setAdsConfiguration(
            new MediaItem.AdsConfiguration.Builder(adTagUri).setAdsId(adTagUri).build())
        .build();
MediaItem secondItem =
    new MediaItem.Builder()
        .setUri(secondVideoUri)
        .setAdsConfiguration(
            new MediaItem.AdsConfiguration.Builder(adTagUri).setAdsId(adTagUri).build())
        .build();
player.addMediaItem(firstItem);
player.addMediaItem(secondItem);

Inserção de anúncios do lado do cliente guiada pelo servidor

O ExoPlayer vem com um HlsInterstitialsAdsLoader que permite inserir automaticamente no lado do cliente anúncios definidos na playlist HLS. Consulte a seção sobre HlsInterstitialsAdsLoader na página do HLS.

Biblioteca IMA do ExoPlayer

A biblioteca do IMA do ExoPlayer oferece ImaAdsLoader, facilitando a integração da inserção de anúncios do lado do cliente ao seu app. Ela envolve a funcionalidade do SDK do IMA do lado do cliente para oferecer suporte à inserção de anúncios VAST/VMAP. Para instruções sobre como usar a biblioteca, incluindo como lidar com o segundo plano e retomar a reprodução, consulte o README.

O aplicativo de demonstração usa a biblioteca IMA e inclui várias tags de anúncio VAST/VMAP de exemplo na lista de amostras.

Considerações sobre a interface

Por padrão, o PlayerView oculta os controles de transporte durante a reprodução de anúncios, mas os apps podem alternar esse comportamento chamando setControllerHideDuringAds. O SDK da IMA mostra outras visualizações na parte de cima do player enquanto um anúncio está sendo veiculado (por exemplo, um link "Mais informações" e um botão "Pular", se aplicável).

O SDK do IMA pode informar se os anúncios estão sendo ocultados por visualizações fornecidas pelo aplicativo renderizadas na parte de cima do player. Os apps que precisam sobrepor visualizações essenciais para controlar a reprodução precisam registrá-las com o SDK do IMA para que possam ser omitidas dos cálculos de visibilidade. Ao usar PlayerView como o AdViewProvider, ele vai registrar automaticamente as sobreposições de controle. Os apps que usam uma interface do player personalizada precisam registrar visualizações de sobreposição retornando-as de AdViewProvider.getAdOverlayInfos.

Para mais informações sobre visualizações de sobreposição, consulte Open Measurement no SDK IMA.

Anúncios complementares

Algumas tags de anúncio contêm anúncios complementares adicionais que podem ser mostrados em "slots" em uma interface do usuário do app. Esses slots podem ser transmitidos via ImaAdsLoader.Builder.setCompanionAdSlots(slots). Para mais informações, consulte Como adicionar anúncios complementares.

Anúncios independentes

O SDK do IMA foi criado para inserir anúncios em conteúdo de mídia, não para veicular anúncios independentes. Portanto, a reprodução de anúncios independentes não é compatível com a biblioteca IMA. Recomendamos usar o SDK dos anúncios para dispositivos móveis do Google para esse caso de uso.

Usar um SDK de anúncios de terceiros

Se você precisar carregar anúncios usando um SDK de anúncios de terceiros, verifique se ele já oferece uma integração do ExoPlayer. Caso contrário, a abordagem recomendada é implementar um AdsLoader personalizado que encapsula o SDK de anúncios de terceiros, já que ele oferece os benefícios do AdsMediaSource descritos acima. O ImaAdsLoader funciona como um exemplo de implementação.

Como alternativa, use o suporte a playlists do ExoPlayer para criar uma sequência de anúncios e clipes de conteúdo:

Kotlin

// A pre-roll ad.
val preRollAd = MediaItem.fromUri(preRollAdUri)
// The start of the content.
val contentStart =
  MediaItem.Builder()
    .setUri(contentUri)
    .setClippingConfiguration(
      MediaItem.ClippingConfiguration.Builder().setEndPositionMs(120000).build()
    )
    .build()
// A mid-roll ad.
val midRollAd = MediaItem.fromUri(midRollAdUri)
// The rest of the content
val contentEnd =
  MediaItem.Builder()
    .setUri(contentUri)
    .setClippingConfiguration(
      MediaItem.ClippingConfiguration.Builder().setStartPositionMs(120000).build()
    )
    .build()

// Build the playlist.
player.addMediaItem(preRollAd)
player.addMediaItem(contentStart)
player.addMediaItem(midRollAd)
player.addMediaItem(contentEnd)

Java

// A pre-roll ad.
MediaItem preRollAd = MediaItem.fromUri(preRollAdUri);
// The start of the content.
MediaItem contentStart =
    new MediaItem.Builder()
        .setUri(contentUri)
        .setClippingConfiguration(
            new MediaItem.ClippingConfiguration.Builder().setEndPositionMs(120_000).build())
        .build();
// A mid-roll ad.
MediaItem midRollAd = MediaItem.fromUri(midRollAdUri);
// The rest of the content
MediaItem contentEnd =
    new MediaItem.Builder()
        .setUri(contentUri)
        .setClippingConfiguration(
            new MediaItem.ClippingConfiguration.Builder().setStartPositionMs(120_000).build())
        .build();

// Build the playlist.
player.addMediaItem(preRollAd);
player.addMediaItem(contentStart);
player.addMediaItem(midRollAd);
player.addMediaItem(contentEnd);

Inserção de anúncios do lado do servidor

Na inserção de anúncios do lado do servidor (também chamada de inserção de anúncios dinâmicos ou DAI), o stream de mídia contém anúncios e conteúdo. Um manifesto DASH pode apontar para conteúdo e segmentos de anúncio, possivelmente em períodos separados. Para HLS, consulte a documentação da Apple sobre incorporação de anúncios em uma playlist.

Ao usar a inserção de anúncios do lado do servidor, o cliente pode precisar resolver o URL de mídia dinamicamente para receber o stream costurado, exibir sobreposições de anúncios na interface ou informar eventos a um SDK de anúncios ou servidor de anúncios.

O DefaultMediaSourceFactory do ExoPlayer pode delegar todas essas tarefas a uma inserção de anúncios do lado do servidor MediaSource para URIs que usam o esquema ssai://:

Kotlin

val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setServerSideAdInsertionMediaSourceFactory(ssaiFactory)
    )
    .build()

Java

Player player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context)
                .setServerSideAdInsertionMediaSourceFactory(ssaiFactory))
        .build();

Biblioteca IMA do ExoPlayer

A biblioteca do IMA do ExoPlayer fornece ImaServerSideAdInsertionMediaSource, facilitando a integração com os fluxos de anúncios inseridos do lado do servidor do IMA no seu app. Ela envolve a funcionalidade do SDK do IMA DAI para Android e integra totalmente os metadados de anúncios fornecidos ao player. Por exemplo, isso permite usar métodos como Player.isPlayingAd(), ouvir transições de conteúdo para anúncio e deixar que o player processe a lógica de reprodução de anúncios, como pular anúncios já veiculados.

Para usar essa classe, configure o ImaServerSideAdInsertionMediaSource.AdsLoader e o ImaServerSideAdInsertionMediaSource.Factory e conecte-os ao player:

Kotlin

// MediaSource.Factory to load the actual media stream.
val defaultMediaSourceFactory = DefaultMediaSourceFactory(context)
// AdsLoader that can be reused for multiple playbacks.
val adsLoader =
  ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(context, adViewProvider).build()
// MediaSource.Factory to create the ad sources for the current player.
val adsMediaSourceFactory =
  ImaServerSideAdInsertionMediaSource.Factory(adsLoader, defaultMediaSourceFactory)
// Configure DefaultMediaSourceFactory to create both IMA DAI sources and
// regular media sources. If you just play IMA DAI streams, you can also use
// adsMediaSourceFactory directly.
defaultMediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory)
// Set the MediaSource.Factory on the Player.
val player = ExoPlayer.Builder(context).setMediaSourceFactory(defaultMediaSourceFactory).build()
// Set the player on the AdsLoader
adsLoader.setPlayer(player)

Java

// MediaSource.Factory to load the actual media stream.
DefaultMediaSourceFactory defaultMediaSourceFactory = new DefaultMediaSourceFactory(context);
// AdsLoader that can be reused for multiple playbacks.
ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader =
    new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(context, adViewProvider).build();
// MediaSource.Factory to create the ad sources for the current player.
ImaServerSideAdInsertionMediaSource.Factory adsMediaSourceFactory =
    new ImaServerSideAdInsertionMediaSource.Factory(adsLoader, defaultMediaSourceFactory);
// Configure DefaultMediaSourceFactory to create both IMA DAI sources and
// regular media sources. If you just play IMA DAI streams, you can also use
// adsMediaSourceFactory directly.
defaultMediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory);
// Set the MediaSource.Factory on the Player.
Player player =
    new ExoPlayer.Builder(context).setMediaSourceFactory(defaultMediaSourceFactory).build();
// Set the player on the AdsLoader
adsLoader.setPlayer(player);

Carregue sua chave de recurso do IMA ou o ID da origem do conteúdo e o ID do vídeo criando um URL com ImaServerSideAdInsertionUriBuilder:

Kotlin

val ssaiUri =
  ImaServerSideAdInsertionUriBuilder()
    .setAssetKey(assetKey)
    .setFormat(C.CONTENT_TYPE_HLS)
    .build()
player.setMediaItem(MediaItem.fromUri(ssaiUri))

Java

Uri ssaiUri =
    new ImaServerSideAdInsertionUriBuilder()
        .setAssetKey(assetKey)
        .setFormat(C.CONTENT_TYPE_HLS)
        .build();
player.setMediaItem(MediaItem.fromUri(ssaiUri));

Por fim, libere o carregador de anúncios quando ele não for mais usado:

Kotlin

adsLoader.release()

Java

adsLoader.release();

Considerações sobre a interface

As mesmas considerações de interface da inserção de anúncios do lado do cliente também se aplicam à inserção de anúncios do lado do servidor.

Anúncios complementares

Algumas tags de anúncio contêm anúncios complementares adicionais que podem ser mostrados em "slots" em uma interface do usuário do app. Esses slots podem ser transmitidos via ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots). Para mais informações, consulte Adicionar anúncios complementares.

Usar um SDK de anúncios de terceiros

Se você precisar carregar anúncios usando um SDK de anúncios de terceiros, verifique se ele já oferece uma integração com o ExoPlayer. Caso contrário, recomendamos fornecer um MediaSource personalizado que aceite URIs com o esquema ssai://, semelhante a ImaServerSideAdInsertionMediaSource.

A lógica real de criação da estrutura de anúncio pode ser delegada ao ServerSideAdInsertionMediaSource de uso geral, que encapsula um fluxo MediaSource e permite que o usuário defina e atualize o AdPlaybackState que representa os metadados do anúncio.

Muitas vezes, os fluxos de anúncios inseridos no lado do servidor contêm eventos programados para notificar o jogador sobre metadados de anúncios. Consulte formatos compatíveis para saber quais formatos de metadados com carimbo de data/hora são aceitos pelo ExoPlayer. As implementações do SDK de anúncios personalizados MediaSource podem detectar eventos de metadados com marcação de tempo do player usando Player.Listener.onMetadata.