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 durante a transição entre o conteúdo e os anúncios. As informações sobre anúncios são carregadas separadamente da mídia, como por uma tag de anúncio XML VAST ou VMAP. Isso pode incluir posições de indicação do anúncio relacionadas ao início do conteúdo, aos URIs e metadados reais da mídia do anúncio, por exemplo, se um determinado anúncio é pulável.

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 que serão reproduzidos. Essa mudança gera vários benefícios:

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

Nessa configuração, o player é responsável por alternar 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 precisam ser posicionados em amostras de sincronização (frames-chave) no conteúdo em vídeo 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 ao player para 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 envolver a fonte de mídia de conteúdo em um AdsMediaSource. O AdsMediaSource recebe um AdsLoader da AdsLoader.Provider e o usa para inserir anúncios conforme definido pela tag de anúncio do item de mídia.

O PlayerView do ExoPlayer implementa o 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 do anúncio uma vez para cada ID de mídia, URI de conteúdo e combinação de URI da 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 tenham um ID de mídia ou URI de conteúdo diferente, mesmo que os URIs da tag de anúncio sejam correspondentes. 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 reproduzidos, portanto, eles são ignorados após a primeira ocorrência.

É possível personalizar esse comportamento transmitindo um identificador de anúncios opaco ao qual o estado de reprodução do anúncio de determinado item de mídia está vinculado, com base na igualdade do objeto. Veja um exemplo em que o estado de reprodução do anúncio é vinculado somente 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. Assim, 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);

Biblioteca de IMA do ExoPlayer

A biblioteca do IMA do ExoPlayer fornece ImaAdsLoader, facilitando a integração da inserção de anúncios do lado do cliente no 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 processar o segundo plano e retomar a reprodução, consulte o README (em inglês).

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

Considerações sobre a interface

PlayerView oculta os controles de transporte durante a reprodução de anúncios por padrão, mas os apps podem alternar esse comportamento chamando setControllerHideDuringAds. O SDK do IMA vai mostrar mais visualizações na parte de cima do player durante a reprodução de um anúncio. 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 são obscurecidos pelas visualizações fornecidas pelo aplicativo renderizadas sobre o player. Os apps que precisam sobrepor visualizações essenciais para controlar a reprodução precisam registrá-las no SDK do IMA para que sejam 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 de player personalizada precisam registrar visualizações de sobreposição retornando-as de AdViewProvider.getAdOverlayInfos.

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

Anúncios complementares

Algumas tags de anúncio contêm anúncios complementares que podem ser exibidos em "espaços" na interface do aplicativo. Esses slots podem ser transmitidos por ImaAdsLoader.Builder.setCompanionAdSlots(slots). Para mais informações, consulte Adicionar anúncios complementares.

Anúncios autônomos

O SDK do IMA foi projetado para inserir anúncios em conteúdo de mídia, e não para reproduzir anúncios independentes por conta própria. Portanto, a reprodução de anúncios independentes não é compatível com a biblioteca do IMA. Recomendamos usar o SDK dos anúncios para dispositivos móveis do Google nesse caso de uso.

Uso de 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á fornece uma integração do ExoPlayer. Caso contrário, a implementação de um AdsLoader personalizado que envolva o SDK de anúncios de terceiros é a abordagem recomendada, já que ela oferece os benefícios da AdsMediaSource descritos acima. ImaAdsLoader atua como um exemplo de implementação.

Como alternativa, você pode usar 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 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úncios, possivelmente em períodos separados. Para 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 de mídia dinamicamente para receber o stream agrupado, pode ser necessário mostrar sobreposições de anúncios na interface ou relatar eventos para um SDK 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 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 de IMA do ExoPlayer

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

Para usar essa classe, você precisa configurar ImaServerSideAdInsertionMediaSource.AdsLoader e 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 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 que podem ser exibidos em "espaços" na interface do aplicativo. Esses slots podem ser transmitidos por ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots). Para mais informações, consulte Adicionar anúncios complementares.

Uso de 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á fornece uma integração do ExoPlayer. Caso contrário, é recomendável fornecer um MediaSource personalizado que aceite URIs com o esquema ssai:// semelhante a ImaServerSideAdInsertionMediaSource.

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

Muitas vezes, os streams de anúncios inseridos do lado do servidor contêm eventos com tempo determinado para notificar o jogador sobre os metadados do anúncio. Consulte os formatos compatíveis para mais informações sobre quais formatos de metadados com marcação de tempo oferecem suporte ao ExoPlayer. As implementações de MediaSource do SDK de anúncios personalizados podem detectar eventos de metadados com marcação de tempo do player usando Player.Listener.onMetadata.