Inserción de anuncios

ExoPlayer se puede usar para la inserción de anuncios del cliente y del servidor.

Inserción de anuncios del cliente

En la inserción de anuncios del cliente, el reproductor alterna entre la carga de contenido multimedia de diferentes URLs y la transición entre la reproducción de contenido y los anuncios. La información sobre los anuncios se carga por separado del contenido multimedia, por ejemplo, desde una etiqueta de anuncio XML VAST o VMAP. Esto puede incluir posiciones de inserción de anuncios relativas al inicio del contenido, los URI de contenido multimedia de anuncios y los metadatos reales, por ejemplo, si se puede omitir un anuncio determinado.

Cuando se usa el objeto AdsMediaSource de ExoPlayer para la inserción de anuncios del cliente, el reproductor tiene información sobre los anuncios que se reproducirán. Esto tiene varios beneficios, como los siguientes:

  • El reproductor puede usar su API para exponer metadatos y funcionalidades relacionados con los anuncios.
  • Los componentes de la IU de ExoPlayer pueden mostrar marcadores para las posiciones del anuncio automáticamente y cambiar su comportamiento según si el anuncio se está reproduciendo o no.
  • De forma interna, el reproductor puede mantener un búfer coherente en las transiciones entre anuncios y contenido.

En esta configuración, el reproductor se encarga de alternar entre los anuncios y el contenido, lo que significa que las apps no necesitan controlar varios reproductores separados en segundo plano o en primer plano para los anuncios y el contenido.

Cuando prepares videos de contenido y etiquetas de anuncios para usar con la inserción de anuncios del cliente, lo ideal es que los anuncios se ubiquen en las muestras de sincronización (fotogramas clave) del video de contenido para que el reproductor pueda reanudar la reproducción de contenido sin problemas.

Compatibilidad con anuncios declarativos

Se puede especificar un URI de etiqueta de anuncio cuando se compila un 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();

A fin de habilitar la compatibilidad del reproductor para elementos multimedia que especifican etiquetas de anuncios, es necesario compilar e insertar un DefaultMediaSourceFactory configurado con un AdsLoader.Provider y un AdViewProvider cuando se crea el reproductor:

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();

De forma interna, DefaultMediaSourceFactory unirá la fuente multimedia del contenido en una AdsMediaSource. AdsMediaSource obtendrá un AdsLoader del AdsLoader.Provider y lo usará para insertar anuncios según lo define la etiqueta de anuncio del elemento multimedia.

El PlayerView de ExoPlayer implementa AdViewProvider. La biblioteca de IMA de ExoPlayer proporciona un AdsLoader fácil de usar, como se describe a continuación.

Playlists con anuncios

Cuando se reproduce una playlist con varios elementos multimedia, el comportamiento predeterminado es solicitar la etiqueta de anuncio y almacenar el estado de reproducción del anuncio una vez por cada combinación de ID multimedia, URI de contenido y URI de etiqueta de anuncio. Esto significa que los usuarios verán anuncios para cada elemento multimedia con anuncios que tengan un ID de contenido multimedia o un URI de contenido distintos, incluso si los URI de la etiqueta del anuncio coinciden. Si se repite un elemento multimedia, el usuario verá los anuncios correspondientes solo una vez (el estado de reproducción de anuncios almacena si se reprodujeron anuncios, por lo que se omiten después de su primer caso).

Para personalizar este comportamiento, puedes pasar un identificador de anuncios opacos con el que esté vinculado el estado de reproducción de anuncios de un elemento multimedia determinado, en función de la igualdad de objetos. A continuación, se muestra un ejemplo en el que el estado de reproducción de anuncios se vincula solo al URI de la etiqueta de anuncio, en lugar de a la combinación del ID de medios y del URI de la etiqueta de anuncio, pasando el URI de la etiqueta de anuncio como identificador de anuncios. Como resultado, los anuncios se cargarán solo una vez, y el usuario no verá anuncios en el segundo elemento cuando reproduzca la lista de reproducción de principio a fin.

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 de ExoPlayer

La biblioteca de IMA de ExoPlayer proporciona ImaAdsLoader, lo que facilita la integración de la inserción de anuncios del cliente en tu app. Incluye la funcionalidad del SDK de IMA del cliente para admitir la inserción de anuncios VAST/VMAP. Para obtener instrucciones sobre cómo usar la biblioteca, incluido cómo controlar la ejecución en segundo plano y reanudar la reproducción, consulta el archivo README.

La aplicación de demostración usa la biblioteca de IMA y, además, incluye varias etiquetas de anuncios VAST/VMAP de muestra en la lista correspondiente.

Consideraciones de la IU

De forma predeterminada, PlayerView oculta sus controles de transporte durante la reproducción de anuncios, pero las apps pueden activar o desactivar este comportamiento llamando a setControllerHideDuringAds. El SDK de IMA mostrará vistas adicionales en la parte superior del reproductor mientras se reproduce un anuncio (por ejemplo, un vínculo "Más información" y un botón Omitir, si corresponde).

El SDK de IMA puede informar si las vistas provistas por la aplicación que se renderizan sobre el reproductor ocultan los anuncios. Las apps que necesitan superponer vistas que son esenciales para controlar la reproducción deben registrarlas con el SDK de IMA para que se puedan omitir en los cálculos de visibilidad. Cuando uses PlayerView como AdViewProvider, registrará automáticamente sus superposiciones de control. Las apps que usan una IU de reproductor personalizada deben registrar vistas superpuestas devolviéndolas desde AdViewProvider.getAdOverlayInfos.

Para obtener más información sobre las vistas superpuestas, consulta Open Measurement en el SDK de IMA.

Anuncios complementarios

Algunas etiquetas de anuncios contienen anuncios complementarios adicionales que se pueden mostrar en "ranuras" en una IU de la app. Estas ranuras se pueden pasar a través de ImaAdsLoader.Builder.setCompanionAdSlots(slots). Para obtener más información, consulta Cómo agregar anuncios complementarios.

Anuncios independientes

El SDK de IMA está diseñado para insertar anuncios en contenido multimedia, no para reproducir anuncios independientes. Por lo tanto, la biblioteca de IMA no admite la reproducción de anuncios independientes. En este caso de uso, recomendamos utilizar el SDK de anuncios de Google para dispositivos móviles.

Cómo usar un SDK de anuncios de terceros

Si necesitas cargar anuncios a través de un SDK de anuncios de terceros, vale la pena verificar si ya proporciona una integración con ExoPlayer. De lo contrario, se recomienda implementar un AdsLoader personalizado que una el SDK de anuncios de terceros, ya que proporciona los beneficios de AdsMediaSource descritos anteriormente. ImaAdsLoader actúa como una implementación de ejemplo.

De manera alternativa, puedes utilizar la compatibilidad con listas de reproducción de ExoPlayer para crear una secuencia de anuncios y clips de contenido:

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);

Inserción de anuncios por parte del servidor

En la inserción de anuncios del servidor (también llamada inserción de anuncios dinámicos o DAI), la transmisión multimedia contiene anuncios y contenido. Un manifiesto de DASH puede apuntar a segmentos de anuncios y contenido, posiblemente en períodos separados. Para HLS, consulta la documentación de Apple sobre cómo incorporar anuncios a una lista de reproducción.

Cuando se usa la inserción de anuncios del servidor, es posible que el cliente deba resolver la URL de contenido multimedia de forma dinámica para obtener la transmisión unida. También es posible que deba mostrar superposiciones de anuncios en la IU o que deba informar eventos a un SDK de anuncios o a un servidor de anuncios.

El DefaultMediaSourceFactory de ExoPlayer puede delegar todas estas tareas a un MediaSource de inserción de anuncios del servidor para URI mediante el 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 de ExoPlayer

La biblioteca de IMA de ExoPlayer proporciona ImaServerSideAdInsertionMediaSource, lo que facilita la integración con las transmisiones de anuncios de IMA insertadas por el servidor en tu app. Incluye la funcionalidad del SDK de IMA de IMA para Android y, además, integra por completo los metadatos de anuncios proporcionados en el reproductor. Por ejemplo, esto te permite usar métodos como Player.isPlayingAd(), escuchar transiciones de anuncios de contenido y dejar que el reproductor controle la lógica de reproducción de anuncios, como la omisión de los anuncios que ya se reprodujeron.

Para usar esta clase, debes configurar ImaServerSideAdInsertionMediaSource.AdsLoader y ImaServerSideAdInsertionMediaSource.Factory, y conectarlos al reproductor:

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);

Para cargar la clave del recurso de IMA o el ID de la fuente del contenido y el ID de video, crea una URL con 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 último, libera el cargador de anuncios cuando ya no se utilice:

Kotlin

adsLoader.release()

Java

adsLoader.release();

Consideraciones de la IU

Las mismas consideraciones de la IU que se aplican a la inserción de anuncios del cliente también se aplican a la inserción de anuncios del servidor.

Anuncios complementarios

Algunas etiquetas de anuncios contienen anuncios complementarios adicionales que se pueden mostrar en "ranuras" en una IU de la app. Estas ranuras se pueden pasar a través de ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots). Para obtener más información, consulta Cómo agregar anuncios complementarios.

Cómo usar un SDK de anuncios de terceros

Si necesitas cargar anuncios con un SDK de anuncios de terceros, vale la pena verificar si ya proporciona una integración con ExoPlayer. De lo contrario, se recomienda proporcionar un MediaSource personalizado que acepte URI con el esquema ssai:// similar a ImaServerSideAdInsertionMediaSource.

La lógica real para crear la estructura del anuncio se puede delegar al ServerSideAdInsertionMediaSource de uso general, que une una MediaSource de transmisión y permite al usuario establecer y actualizar la AdPlaybackState que representa los metadatos del anuncio.

A menudo, las transmisiones de anuncios que se insertan en el servidor contienen eventos temporizados para notificar al jugador sobre los metadatos de anuncios. Consulta los formatos compatibles para obtener información sobre los formatos de metadatos temporizados que admite ExoPlayer. Las implementaciones de MediaSource del SDK de anuncios personalizados pueden escuchar eventos de metadatos temporizados del reproductor mediante Player.Listener.onMetadata.