広告の挿入

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

ExoPlayer の PlayerView は AdViewProvider を実装します。ExoPlayer IMA ライブラリには、後述する AdsLoader が用意されています。

プレイリスト(広告あり)

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

この動作をカスタマイズするには、オブジェクトの等価性に基づいて、特定のメディア アイテムの広告再生状態がリンクされている不透明な広告識別子を渡します。次の例では、広告タグ URI を広告識別子として渡すことで、広告再生状態がメディア 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 には、HLS プレイリストで定義された広告をクライアント側で自動的に挿入できる HlsInterstitialsAdsLoader が付属しています。HLS ページの HlsInterstitialsAdsLoader に関するセクションをご覧ください。

ExoPlayer IMA ライブラリ

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

デモ アプリケーションでは IMA ライブラリが使用されており、サンプルリストには VAST/VMAP 広告タグのサンプルがいくつか含まれています。

UI に関する考慮事項

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

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

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

コンパニオン広告

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

スタンドアロン広告

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

サードパーティの広告 SDK を使用している

サードパーティの広告 SDK を使用して広告を読み込む必要がある場合は、その SDK が ExoPlayer との統合をすでに提供しているかどうかを確認することをおすすめします。そうでない場合は、サードパーティの広告 SDK をラップするカスタム AdsLoader を実装することをおすすめします。これにより、前述の 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);

サーバーサイド広告挿入

サーバーサイド広告挿入(ダイナミック広告挿入、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 ライブラリには、IMA のサーバーサイド挿入広告ストリームをアプリに統合するための ImaServerSideAdInsertionMediaSource が用意されています。このライブラリは、Android 向け IMA DAI SDK の機能をラップし、提供された広告メタデータをプレーヤーに完全に統合します。たとえば、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);

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 を使用して広告を読み込む必要がある場合は、その SDK が ExoPlayer との統合をすでに提供しているかどうかを確認することをおすすめします。そうでない場合は、ImaServerSideAdInsertionMediaSource と同様に ssai:// スキームを持つ URI を受け入れるカスタム MediaSource を指定することをおすすめします。

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

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