Вставка объявлений

ExoPlayer можно использовать для вставки объявлений как на стороне клиента, так и на стороне сервера.

Клиентская вставка рекламы

При вставке объявлений на стороне клиента проигрыватель загружает медиафайлы из разных URL, когда переключается между контентом и рекламой. Информация о рекламе загружается отдельно от медиаконтента, например из XML-тега объявления VAST или VMAP. Это могут быть позиции рекламных меток относительно начала контента, фактические URI медиафайлов объявлений и метаданные, например информация о том, можно ли пропустить данное объявление.

При использовании AdsMediaSource ExoPlayer для вставки объявлений на стороне клиента у проигрывателя есть информация о рекламе, которую нужно воспроизвести. Это дает следующие преимущества:

  • Проигрыватель может предоставлять метаданные и функции, связанные с рекламой, с помощью своего API.
  • Компоненты пользовательского интерфейса ExoPlayer могут автоматически показывать маркеры позиций объявлений и менять свое поведение в зависимости от того, воспроизводится ли объявление.
  • Проигрыватель может поддерживать постоянный буфер при переходах между объявлениями и контентом.

При такой настройке проигрыватель сам переключается между объявлениями и контентом, а значит, приложениям не нужно управлять несколькими отдельными проигрывателями для объявлений и контента.

При подготовке видеоконтента и тегов объявлений для использования с клиентской вставкой рекламы рекомендуется размещать объявления в точках синхронизации (ключевых кадрах) видеоконтента, чтобы проигрыватель мог возобновить воспроизведение контента без задержек.

Поддержка декларативных объявлений

URI тега объявления можно указать при создании 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();

Чтобы включить поддержку мультимедийных объектов, в которых указаны теги объявлений, необходимо создать и внедрить DefaultMediaSourceFactory, настроенный с помощью AdsLoader.Provider и AdViewProvider, при создании проигрывателя:

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

Внутренний код DefaultMediaSourceFactory поместит источник медиаконтента в AdsMediaSource. AdsMediaSource получит AdsLoader от AdsLoader.Provider и использует его для вставки объявлений, как определено тегом объявления мультимедийного объекта.

Класс PlayerView ExoPlayer реализует интерфейс AdViewProvider. Библиотека ExoPlayer IMA предоставляет класс AdsLoader, который описан ниже.

Плейлисты с рекламой

При воспроизведении плейлиста с несколькими мультимедийными объектами, режим работы по умолчанию — запрашивать тег объявления и сохранять состояние воспроизведения объявления один раз для каждой комбинации идентификатора мультимедийного объекта, URI контента и URI тега объявления. Это означает, что пользователи будут видеть рекламу для каждого мультимедийного объекта с рекламой, у которого есть отдельный идентификатор мультимедийного объекта или URI контента, даже если URI тегов объявлений совпадают. Если мультимедийный объект повторяется, пользователь увидит соответствующие объявления только один раз (состояние воспроизведения объявления сохраняет информацию о том, воспроизводились ли объявления, поэтому они пропускаются после первого вхождения).

Это поведение можно настроить, передав непрозрачный идентификатор объявления, с которым связано состояние воспроизведения объявления для данного мультимедийного объекта, на основе равенства объектов. В примере ниже статус воспроизведения объявления связан только с URI тега объявления, а не с комбинацией идентификатора мультимедийного объекта и URI тега объявления, поскольку в качестве идентификатора объявления передается URI тега объявления. В результате объявления будут загружены только один раз, и пользователь не увидит рекламу во втором видео, если будет смотреть плейлист от начала до конца.

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

Вставка объявлений на стороне клиента под управлением сервера

В ExoPlayer есть HlsInterstitialsAdsLoader, который поддерживает автоматическую вставку на стороне клиента объявлений, определенных в плейлисте HLS. Ознакомьтесь с разделом о HlsInterstitialsAdsLoader на странице HLS.

Библиотека IMA для ExoPlayer

Библиотека ExoPlayer IMA предоставляет ImaAdsLoader для интеграции клиентской вставки объявлений в приложение. Она включает функции клиентского IMA SDK для поддержки вставки объявлений VAST/VMAP. Инструкции по использованию библиотеки, в том числе по обработке фонового режима и возобновлению воспроизведения, приведены в файле README.

В демонстрационном приложении используется библиотека IMA, а в списке примеров есть несколько тегов объявлений VAST/VMAP.

Особенности интерфейса

По умолчанию PlayerView скрывает элементы управления во время показа рекламы, но приложения могут изменить это поведение, вызвав setControllerHideDuringAds. Во время воспроизведения объявления IMA SDK будет показывать дополнительные элементы поверх проигрывателя (например, ссылку "Подробнее" и кнопку пропуска, если она предусмотрена).

IMA SDK может сообщать, закрыты ли объявления представлениями, которые созданы приложением и отображаются поверх проигрывателя. Приложения, в которых необходимо накладывать представления, важные для управления воспроизведением, должны зарегистрировать их в IMA SDK, чтобы они не учитывались при расчете видимости. Если вы используете PlayerView в качестве AdViewProvider, оверлеи управления будут зарегистрированы автоматически. Приложения, в которых используется собственный интерфейс проигрывателя, должны регистрировать оверлеи, возвращая их из AdViewProvider.getAdOverlayInfos.

Дополнительную информацию о представлениях оверлеев можно найти в статье Open Measurement в IMA SDK.

Сопутствующие баннеры

Некоторые теги объявлений содержат дополнительные сопутствующие объявления, которые могут показываться в рекламных местах в интерфейсе приложения. Эти слоты можно передавать с помощью ImaAdsLoader.Builder.setCompanionAdSlots(slots). Подробнее о том, как добавлять сопутствующие объявления…

Отдельные объявления

IMA SDK предназначен для вставки объявлений в медиаконтент, а не для воспроизведения отдельных объявлений. Поэтому библиотека IMA не поддерживает воспроизведение отдельных объявлений. В этом случае мы рекомендуем использовать Google Mobile Ads SDK.

Использование стороннего рекламного SDK

Если вы загружаете объявления с помощью стороннего рекламного SDK, проверьте, поддерживает ли он интеграцию с ExoPlayer. Если нет, рекомендуем реализовать собственный класс AdsLoader, который будет служить оболочкой для стороннего рекламного SDK. Это позволит вам воспользоваться преимуществами AdsMediaSource, описанными выше. ImaAdsLoader – это пример реализации.

Вы также можете использовать поддержку плейлистов ExoPlayer, чтобы создать последовательность объявлений и видеороликов:

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

Вставка объявлений на стороне сервера

При серверной вставке объявлений (также называемой динамической вставкой объявлений) медиапоток содержит и объявления, и контент. Манифест DASH может указывать как на сегменты контента, так и на сегменты объявлений, возможно, в разных периодах. Если вы используете HLS, ознакомьтесь с документацией Apple о том, как добавлять объявления в плейлист.

При использовании динамической вставки объявлений на сервере клиенту может потребоваться динамически разрешать URL медиаконтента, чтобы получить объединенный поток, показывать оверлеи объявлений в интерфейсе или передавать данные о событиях в рекламный SDK или на сервер объявлений.

Класс DefaultMediaSourceFactory ExoPlayer может делегировать все эти задачи серверной вставке объявлений MediaSource для URI, использующих схему 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();

Библиотека IMA для ExoPlayer

Библиотека ExoPlayer IMA предоставляет класс ImaServerSideAdInsertionMediaSource, который позволяет интегрировать в приложение рекламные потоки, вставленные на сервере IMA. Она включает функции IMA DAI SDK для Android и полностью интегрирует предоставленные метаданные объявлений в проигрыватель. Например, вы можете использовать такие методы, как Player.isPlayingAd(), отслеживать переходы между контентом и рекламой и позволить проигрывателю управлять логикой воспроизведения объявлений, например пропускать уже показанные.

Чтобы использовать этот класс, необходимо настроить ImaServerSideAdInsertionMediaSource.AdsLoader и ImaServerSideAdInsertionMediaSource.Factory и подключить их к проигрывателю:

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

Загрузите ключ объекта IMA или идентификатор источника контента и идентификатор видео, создав URL с помощью 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));

Наконец, освободите загрузчик объявлений, когда он больше не используется:

Kotlin

adsLoader.release()

Java

adsLoader.release();

Особенности интерфейса

При вставке объявлений на стороне сервера действуют те же требования к интерфейсу, что и при вставке на стороне клиента.

Сопутствующие баннеры

Некоторые теги объявлений содержат дополнительные сопутствующие объявления, которые могут показываться в рекламных местах в интерфейсе приложения. Эти слоты можно передавать с помощью ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots). Подробнее о добавлении сопутствующих объявлений…

Использование стороннего рекламного SDK

Если вы загружаете объявления с помощью стороннего рекламного SDK, проверьте, поддерживает ли он интеграцию с ExoPlayer. Если нет, рекомендуется предоставить собственный MediaSource, который принимает URI со схемой ssai://, аналогичной ImaServerSideAdInsertionMediaSource.

Логику создания структуры объявления можно делегировать общему классу ServerSideAdInsertionMediaSource, который содержит поток MediaSource и позволяет пользователю задавать и обновлять метаданные объявления AdPlaybackState.

Часто потоки объявлений, вставленных на стороне сервера, содержат временные события, которые уведомляют проигрыватель о метаданных объявления. Информацию о том, какие форматы временных метаданных поддерживаются ExoPlayer, можно найти в этой статье. Специальные реализации рекламного SDK MediaSource могут прослушивать события временных метаданных из проигрывателя с помощью Player.Listener.onMetadata.