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 o carregamento de mídia de URLs diferentes à medida que transita entre a exibição de conteúdo e anúncios. As informações sobre os anúncios são carregadas separadamente da mídia, por exemplo, de uma tag de anúncio XML VAST ou VMAP. Isso pode incluir posições de comando 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 relacionados a anúncios usando a API.
  • Os componentes da interface do ExoPlayer podem mostrar marcadores para posições de anúncio automaticamente e mudar o comportamento dependendo se o anúncio está sendo reproduzido.
  • Internamente, o player pode manter um buffer consistente nas transições entre anúncios e conteúdo.

Nessa configuração, o player cuida da alternância entre anúncios e conteúdo, o que significa que os apps não precisam controlar vários players em segundo plano/primeiro plano separados 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, o ideal é posicionar os anúncios em amostras de sincronização (frames-chave) no vídeo de conteúdo para que o player possa retomar a reprodução do conteúdo sem problemas.

Suporte a anúncios declarativos

Um URI da 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 para itens de mídia que especificam tags de anúncios, é 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 agrupar 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 do item de mídia.

O PlayerView do ExoPlayer implementa AdViewProvider. A biblioteca IMA do ExoPlayer oferece 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 do anúncio uma vez para cada ID de mídia, URI de conteúdo e combinação de URI de tag de anúncio. Isso significa que os usuários vão receber anúncios de cada item de mídia com anúncios que tenham um ID de mídia ou URI de conteúdo distinto, mesmo que os URIs da tag de anúncio sejam iguais. Se um item de mídia for repetido, o usuário vai ver os anúncios correspondentes apenas uma vez. O estado de reprodução de anúncios armazena se os anúncios foram exibidos, então eles são pulados após a primeira ocorrência.

É possível personalizar esse comportamento transmitindo um identificador de anúncios opaco com o estado de reprodução de anúncios para um determinado item de mídia vinculado, com base na igualdade do objeto. Confira um exemplo em que o estado de reprodução do anúncio é vinculado apenas ao URI da tag de anúncio, em vez da combinação do ID de 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 são carregados apenas uma vez e o usuário não vê 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 HlsInterstitialsAdsLoader, que oferece suporte a anúncios definidos na playlist HLS para serem inseridos no lado do cliente automaticamente. Consulte a seção sobre HlsInterstitialsAdsLoader na página HLS.

Biblioteca IMA do ExoPlayer

A biblioteca IMA do ExoPlayer oferece ImaAdsLoader, facilitando a integração da inserção de anúncios do lado do cliente no 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 processar a execução em 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 na lista de exemplo.

Considerações sobre a interface

Por padrã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 do IMA vai mostrar outras visualizações na parte de cima do player enquanto um anúncio está sendo reproduzido (por exemplo, um link "Mais informações" e um botão de pular, se aplicável).

O SDK do IMA pode informar se os anúncios estão obscurecidos 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 no SDK do IMA para que possam ser omitidas dos cálculos de visibilidade. Ao usar PlayerView como AdViewProvider, ele registra 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 Medição aberta no SDK do IMA.

Anúncios complementares

Algumas tags de anúncio contêm anúncios complementares adicionais que podem ser mostrados em "slots" na interface do app. Esses slots podem ser transmitidos por 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 exibir 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.

Como 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, a implementação de um AdsLoader personalizado que englobe o SDK de anúncios de terceiros é a abordagem recomendada, já que oferece os benefícios de AdsMediaSource descritos acima. ImaAdsLoader atua 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(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(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 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 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 no servidor

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

Ao usar a inserção de anúncios do lado do servidor, o cliente pode precisar resolver o URL da mídia dinamicamente para receber o fluxo costurado, mostrar 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úncio do lado do servidor MediaSource para URIs usando 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 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 agrupa a funcionalidade do SDK do IMA DAI para Android e integra totalmente os metadados de anúncios fornecidos ao player. Por exemplo, isso permite que você use métodos como Player.isPlayingAd(), ouça as transições de anúncio de conteúdo e deixe que o player gerencie a lógica de reprodução de anúncios, como pular anúncios já reproduzidos.

Para usar essa classe, você precisa configurar o ImaServerSideAdInsertionMediaSource.AdsLoader e o ImaServerSideAdInsertionMediaSource.Factory e conectá-los 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 a chave de recurso do IMA ou o ID da origem do conteúdo e 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" na interface do app. Esses slots podem ser transmitidos por ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots). Para mais informações, consulte Adicionar anúncios complementares.

Como 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, é recomendável fornecer um MediaSource personalizado que aceite URIs com o esquema ssai:// semelhante ao ImaServerSideAdInsertionMediaSource.

A lógica real de criação da estrutura do anúncio pode ser delegada à ServerSideAdInsertionMediaSource de propósito geral, que envolve um MediaSource de stream 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 servidor contêm eventos programados para notificar o jogador sobre os metadados do anúncio. Consulte formatos compatíveis para informações sobre quais formatos de metadados temporizados são aceitos pelo ExoPlayer. As implementações do SDK de anúncios personalizados MediaSource podem detectar eventos de metadados cronometrados do player usando Player.Listener.onMetadata.