広告の挿入

ExoPlayer は、クライアントサイドとサーバーサイドの両方の広告挿入に使用できます。

クライアントサイド広告挿入

クライアント側の広告挿入では、プレーヤーは、コンテンツと広告を再生しながら、異なる URL からのメディアの読み込みを切り替えます。広告に関する情報は、XML の VAST 広告タグまたは VMAP 広告タグなどからメディアとは別に読み込まれます。これには、コンテンツの開始に対する相対的な広告のキューの位置、実際の広告メディア URI、メタデータ(特定の広告がスキップ可能かどうかなど)を含めることができます。

クライアント側の広告挿入に ExoPlayer の AdsMediaSource を使用する場合、プレーヤーには再生される広告に関する情報があります。これには、さまざまなメリットがあります。

  • プレーヤーは、API を使用して広告に関連するメタデータと機能を公開できます。
  • ExoPlayer UI コンポーネントは、広告位置のマーカーを自動的に表示し、広告が再生されているかどうかに応じて動作を変更できます。
  • 内部的には、プレーヤーは広告とコンテンツ間の遷移で一貫したバッファを維持できます。

この設定では、プレーヤーが広告とコンテンツの切り替えを行います。つまり、アプリは広告とコンテンツ用に複数のバックグラウンド/フォアグラウンド プレーヤーを個別に制御する必要はありません。

クライアントサイドの広告挿入で使用するコンテンツ動画と広告タグを準備する場合は、プレーヤーがコンテンツ再生をシームレスに再開できるように、コンテンツ動画の同期サンプル(キーフレーム)に広告を配置することが理想的です。

宣言型広告のサポート

広告タグ 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();

広告タグを指定するメディア アイテムのプレーヤー サポートを有効にするには、プレーヤーの作成時に、AdsLoader.ProviderAdViewProvider で構成された DefaultMediaSourceFactory をビルドして挿入する必要があります。

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 にラップします。AdsMediaSourceAdsLoader.Provider から AdsLoader を取得し、それを使用して、メディア アイテムの広告タグで定義された広告を挿入します。

ExoPlayer の PlayerViewAdViewProvider を実装します。ExoPlayer IMA ライブラリには、以下で説明するように、使いやすい AdsLoader が用意されています。

広告が表示される再生リスト

複数のメディア アイテムを含むプレイリストを再生する場合、デフォルトの動作では、メディア ID、コンテンツ URI、広告タグ URI の組み合わせごとに 1 回、広告タグがリクエストされ、広告の再生状態が保存されます。つまり、広告タグ URI が一致しても、個別のメディア ID またはコンテンツ URI を持つ広告を持つすべてのメディア アイテムの広告がユーザーに表示されます。メディア アイテムが繰り返される場合、ユーザーには対応する広告が 1 回だけ表示されます(広告の再生状態には広告が再生されたかどうかが保存されるため、初回発生後はスキップされます)。

この動作は、オブジェクトの等価性に基づいて、特定のメディア アイテムの広告再生状態がリンクされる不透明な広告識別子を渡すことでカスタマイズできます。次の例では、広告タグ URI を広告 ID として渡して、メディア ID と広告タグ URI の組み合わせではなく、広告の再生状態を広告タグ URI にのみリンクしています。その結果、再生リストを最初から最後まで再生する際、広告は 1 回だけ読み込まれ、2 番目のアイテムには広告が表示されません。

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 IMA ライブラリ

ExoPlayer IMA ライブラリには ImaAdsLoader が用意されており、クライアントサイドの広告挿入をアプリに簡単に統合できます。また、クライアントサイドの IMA SDK の機能をラップして VAST/VMAP 広告の挿入をサポートしています。バックグラウンド処理や再生の再開の処理方法など、ライブラリの使用方法については、README をご覧ください。

このデモ アプリケーションは IMA ライブラリを使用し、サンプルリストにはサンプルの VAST/VMAP 広告タグが含まれています。

UI に関する考慮事項

PlayerView はデフォルトで、広告の再生中にトランスポート コントロールを非表示にします。ただし、アプリは setControllerHideDuringAds を呼び出すことで、この動作を切り替えることができます。IMA SDK では、広告が再生されている間、プレーヤー上に追加のビューが表示されます(「詳細」リンクやスキップボタンなど)。

IMA SDK では、プレーヤー上に表示されるアプリ提供のビューで、広告が隠れているかどうかを報告できます。再生の制御に不可欠なビューをオーバーレイする必要があるアプリでは、それらを IMA SDK に登録して、視認性の計算から除外できるようにする必要があります。PlayerViewAdViewProvider として使用すると、そのコントロール オーバーレイが自動的に登録されます。カスタム プレーヤー UI を使用するアプリは、オーバーレイ ビューを AdViewProvider.getAdOverlayInfos から返すことで登録する必要があります。

オーバーレイ ビューについて詳しくは、IMA SDK の Open Measurement をご覧ください。

コンパニオン広告

一部の広告タグには追加のコンパニオン広告が含まれており、アプリの UI の「スロット」に表示できます。これらのスロットは ImaAdsLoader.Builder.setCompanionAdSlots(slots) を介して渡すことができます。詳細については、コンパニオン広告を追加するをご覧ください。

スタンドアロンの広告

IMA SDK はメディア コンテンツに広告を挿入するためのものです。スタンドアロンの広告を再生するためではありません。そのため、IMA ライブラリではスタンドアロン広告の再生はサポートされていません。この場合は、代わりに Google Mobile Ads SDK を使用することをおすすめします。

第三者広告 SDK を使用する

サードパーティの広告 SDK を介して広告を読み込む必要がある場合は、ExoPlayer の統合がすでに提供されているかどうかを確認することをおすすめします。そうでない場合は、上記の AdsMediaSource のメリットがあるため、第三者広告 SDK をラップするカスタムの AdsLoader を実装することをおすすめします。ImaAdsLoader は実装例として機能します。

または、ExoPlayer のプレイリスト サポートを使用して、一連の広告とコンテンツ クリップを作成することもできます。

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

サーバーサイドの広告挿入

サーバーサイドの広告挿入(ダイナミック広告挿入(DAI)とも呼ばれます)では、メディア ストリームに広告とコンテンツの両方が含まれます。DASH マニフェストは、場合によっては別々の期間に、コンテンツと広告セグメントの両方を参照することがあります。HLS については、再生リストへの広告の組み込みに関する Apple のドキュメントをご覧ください。

サーバーサイドの広告挿入を使用する場合、クライアントは、合成されたストリームを取得するためにメディア URL を動的に解決したり、UI に広告オーバーレイを表示したり、広告 SDK または広告サーバーにイベントを報告したりする必要があります。

ExoPlayer の DefaultMediaSourceFactory は、ssai:// スキームを使用して、これらすべてのタスクを URI のサーバーサイド広告挿入 MediaSource に委任できます。

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

ExoPlayer IMA ライブラリ

ExoPlayer IMA ライブラリには ImaServerSideAdInsertionMediaSource が用意されているため、IMA のサーバーサイドで挿入された広告ストリームとアプリを簡単に統合できます。また、IMA DAI SDK for Android の機能がラップされ、提供された広告メタデータがプレーヤーに完全に統合されます。たとえば、Player.isPlayingAd() などのメソッドを使用し、コンテンツから広告への遷移をリッスンし、プレーヤーで広告再生ロジック(再生済みの広告のスキップなど)を処理できます。

このクラスを使用するには、次のように ImaServerSideAdInsertionMediaSource.AdsLoaderImaServerSideAdInsertionMediaSource.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);

ImaServerSideAdInsertionUriBuilder で URL を作成し、IMA アセットキーまたはコンテンツ ソース ID と動画 ID を読み込みます。

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

UI に関する考慮事項

クライアントサイド広告挿入の場合と同じ UI の考慮事項は、サーバーサイドの広告挿入にも適用されます。

コンパニオン広告

一部の広告タグには追加のコンパニオン広告が含まれており、アプリの UI の「スロット」に表示できます。これらのスロットは ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots) を介して渡すことができます。詳しくは、コンパニオン広告を追加するをご覧ください。

第三者広告 SDK を使用する

サードパーティの広告 SDK を使用して広告を読み込む必要がある場合は、ExoPlayer の統合がすでに提供されているかどうかを確認することをおすすめします。そうでない場合は、ImaServerSideAdInsertionMediaSource のような ssai:// スキームを使用して、URI を受け入れるカスタム MediaSource を指定することをおすすめします。

広告構造を作成する実際のロジックは、汎用の ServerSideAdInsertionMediaSource に委任できます。これはストリーム MediaSource をラップし、広告メタデータを表す AdPlaybackState をユーザーが設定および更新できるようにします。

多くの場合、サーバーサイドに挿入される広告ストリームには、広告メタデータについてプレーヤーに通知する時間制限付きイベントが含まれています。ExoPlayer でサポートされている時間指定メタデータ形式については、サポートされている形式をご覧ください。カスタム広告 SDK の MediaSource の実装では、Player.Listener.onMetadata を使用して、プレーヤーからの時間制限付きメタデータ イベントをリッスンできます。