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 cambia entre la carga de contenido multimedia desde diferentes URLs a medida que realiza la transición entre la reproducción de contenido y anuncios. La información sobre los anuncios se carga por separado del contenido multimedia, por ejemplo, desde una etiqueta de anuncio VAST o VMAP en formato XML. Esto puede incluir las posiciones de las marcas publicitarias en relación con el inicio del contenido, los URIs de los medios publicitarios reales y los metadatos, como si un anuncio determinado se puede omitir.

Cuando se usa 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 exponer metadatos y funcionalidades relacionados con los anuncios a través de su API.
  • Los componentes de la IU de ExoPlayer pueden mostrar marcadores para las posiciones de los anuncios automáticamente y cambiar su comportamiento según si se está reproduciendo un anuncio.
  • Internamente, el reproductor puede mantener un búfer coherente en las transiciones entre anuncios y contenido.

En esta configuración, el reproductor se encarga de cambiar 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 usarlos con la inserción de anuncios del cliente, lo ideal es que los anuncios se posicionen en muestras de sincronización (fotogramas clave) del video de contenido para que el reproductor pueda reanudar la reproducción del 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();

Para habilitar la compatibilidad del reproductor con 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();

Internamente, DefaultMediaSourceFactory encapsulará la fuente de medios de contenido en un AdsMediaSource. El objeto AdsMediaSource obtendrá un objeto AdsLoader del objeto AdsLoader.Provider y lo usará para insertar anuncios según lo defina la etiqueta del 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 del anuncio y almacenar el estado de reproducción del anuncio una vez para cada combinación de ID de contenido multimedia, URI de contenido y URI de etiqueta del anuncio. Esto significa que los usuarios verán anuncios para cada elemento multimedia con anuncios que tenga un ID de medios o un URI de contenido distintos, incluso si los URIs de las etiquetas de anuncios 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 primera aparición).

Es posible personalizar este comportamiento pasando un identificador de anuncios opaco con el que se vincula el estado de reproducción de anuncios para un elemento multimedia determinado, según la igualdad de objetos. A continuación, se muestra un ejemplo en el que el estado de reproducción del anuncio se vincula solo al URI de la etiqueta del anuncio, en lugar de a la combinación del ID de los medios y el URI de la etiqueta del anuncio, pasando el URI de la etiqueta del anuncio como el identificador de anuncios. El efecto es que los anuncios se cargarán solo una vez y el usuario no los verá en el segundo elemento cuando reproduzca la playlist 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);

Inserción de anuncios del cliente guiada por el servidor

ExoPlayer incluye HlsInterstitialsAdsLoader que admite la inserción automática del lado del cliente de anuncios definidos en la playlist de HLS. Consulta la sección sobre HlsInterstitialsAdsLoader en la página de HLS.

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. Envuelve 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 reproducción en segundo plano y reanudarla, consulta el archivo README.

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

Consideraciones sobre la IU

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

El SDK de IMA puede informar si las vistas proporcionadas por la aplicación y renderizadas sobre el reproductor ocultan los anuncios. Las apps que necesitan superponer vistas esenciales para controlar la reproducción deben registrarlas con el SDK de IMA para que se puedan omitir de los cálculos de visibilidad. Cuando se usa PlayerView como AdViewProvider, se registrarán automáticamente sus superposiciones de control. Las apps que usan una IU del reproductor personalizada deben registrar las vistas de superposición 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 "espacios" en la IU de una 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 sí mismos. Por lo tanto, la biblioteca de IMA no admite la reproducción de anuncios independientes. Para este caso de uso, te recomendamos que uses el SDK de anuncios de Google para dispositivos móviles.

Usa 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 de ExoPlayer. De lo contrario, la implementación de un AdsLoader personalizado que encapsule el SDK de anuncios de terceros es el enfoque recomendado, ya que proporciona los beneficios de AdsMediaSource que se describieron anteriormente. ImaAdsLoader actúa como ejemplo de implementación.

Como alternativa, puedes usar la compatibilidad con playlists 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(
      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);

Inserción de anuncios del servidor

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

Cuando se usa la inserción de anuncios del servidor, es posible que el cliente deba resolver la URL de los medios de forma dinámica para obtener la transmisión unida, mostrar superposiciones de anuncios en la IU o 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 los URIs que usan 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 los flujos de anuncios insertados del servidor de IMA en tu app. Envuelve la funcionalidad del SDK de DAI 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 las transiciones de anuncios de contenido y dejar que el reproductor controle la lógica de reproducción de anuncios, como omitir 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);

Carga la clave de recurso de IMA o el ID de la fuente de contenido y el ID de video creando 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 use:

Kotlin

adsLoader.release()

Java

adsLoader.release();

Consideraciones sobre la IU

Las mismas consideraciones de 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 "espacios" en la IU de una 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.

Usa 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 de ExoPlayer. De lo contrario, se recomienda proporcionar un MediaSource personalizado que acepte URIs con el esquema ssai://, similar a ImaServerSideAdInsertionMediaSource.

La lógica real de la creación de la estructura del anuncio se puede delegar en el ServerSideAdInsertionMediaSource de propósito general, que encapsula un flujo MediaSource y permite al usuario establecer y actualizar el AdPlaybackState que representa los metadatos del anuncio.

A menudo, los flujos de anuncios insertados del servidor contienen eventos cronometrados para notificar al reproductor sobre los metadatos de los anuncios. Consulta los formatos admitidos para obtener información sobre los formatos de metadatos cronometrados que admite ExoPlayer. Las implementaciones personalizadas del SDK de anuncios MediaSource pueden escuchar eventos de metadatos cronometrados del reproductor con Player.Listener.onMetadata.