開始使用 ExoPlayer IMA 擴充功能

ExoPlayer 是 Android 的應用程式層級媒體播放器。本指南說明如何使用 ExoPlayer IMA 擴充功能 (包裝 IMA DAI SDK),請求及播放含有廣告和內容的媒體串流。

以下是擴充功能的幾項優點:

  • 簡化整合 IMA 與功能所需的程式碼。
  • 縮短更新至新版 IMA 所需的開發時間。

ExoPlayer IMA 擴充功能支援 HLS 和 DASH 串流通訊協定。請參考 摘要:

ExoPlayer-IMA 擴充功能串流支援
直播 VOD 串流
HLS 勾號 勾號
DASH 勾號 勾號

ExoPlayer-IMA 1.1.0 以上版本支援 DASH 直播。

本指南以 ExoPlayer 指南為基礎,說明如何建立完整應用程式並整合擴充功能。詳情請參閱 ExoPlayerExample,來源: GitHub,如需 完整的範例應用程式

必要條件

建立新的 Android Studio 專案

如要建立 Android Studio 專案,請完成下列步驟:

  • 啟動 Android Studio。
  • 選取「Start a new Android Studio project」
  • 在「Choose your project」頁面中,選取「No Activity」範本。
  • 請點選「Next」(下一步)
  • 在「Configure your project」頁面中為專案命名,並選取 Java 做為語言。

  • 按一下 [完成]。

在專案中新增 ExoPlayer IMA 擴充功能

將擴充功能的匯入項目新增至應用程式層級的 build.gradle 檔案 dependencies部分。

設定應用程式,然後啟用 multidex。由於擴充功能的大小,這項做法是必要的,且適用於 minSdkVersion 設為 Android 4.4W (API 級別 20) 以下版本的應用程式。

範例如下:

app/build.gradle

android {

  ...

  defaultConfig {
      applicationId "com.google.ads.interactivemedia.v3.samples.videoplayerapp"
      minSdkVersion 21
      targetSdkVersion 34
      multiDexEnabled true
      versionCode 1
      versionName "1.0"
  }

    ...
}
dependencies {
    implementation 'androidx.multidex:multidex:2.0.1'
    implementation 'androidx.media3:media3-ui:1.1.1'
    implementation 'androidx.media3:media3-exoplayer:1.1.1'
    implementation 'androidx.media3:media3-exoplayer-hls:1.1.1'
    implementation 'androidx.media3:media3-exoplayer-dash:1.1.1'

    // Adding the ExoPlayer IMA extension for ads will also include the IMA
    // SDK as a dependency.
    implementation 'androidx.media3:media3-exoplayer-ima:1.1.1'
}

請新增 IMA DAI SDK 請求廣告所需的使用者權限:

app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.project name">

    <!-- Required permissions for the IMA DAI SDK -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    ...

</manifest>

新增意圖宣告

如果應用程式指定 Android 11 (API 級別 30) 以上版本,則目前和近期版本的 IMA DAI SDK 需要明確宣告意圖,才能開啟網頁連結。將下列程式碼片段新增至應用程式的資訊清單檔案,以便啟用 廣告點閱 (使用者點按「瞭解詳情」按鈕)。

  <?xml version="1.0" encoding="utf-8"?>
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.project name">

      ...

    </application>

    <queries>
      <intent>
          <action android:name="android.intent.action.VIEW" />
          <data android:scheme="https" />
      </intent>
      <intent>
          <action android:name="android.intent.action.VIEW" />
          <data android:scheme="http" />
      </intent>
    </queries>
  </manifest>

設定 ExoPlayer 的 UI

建立 ExoPlayer 要使用的 PlayerView 物件。

androidx.constraintlayout.widget.ConstraintLayout 變更為 LinearLayout,這是 ExoPlayer IMA 擴充功能的建議做法。

範例如下:

app/src/main/res/layout/activity_my.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="@android:color/black"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MyActivity"
    tools:ignore="MergeRootFrame">

    <androidx.media3.ui.PlayerView
        android:id="@+id/player_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

新增串流參數

如要取得用於測試專案的樣本串流資產,請參閱 IMA 樣本串流頁面。另請參閱 Ad Manager 一節 動態廣告插播一文,瞭解設定相關資訊 您自己的串流中

本步驟將示範如何設定直播,但 ExoPlayer IMA 擴充功能也支援 DAI VOD 串流。請參閱隨選影片 (VOD) 的步驟 串流 ,瞭解應用程式處理 VOD 串流時需要進行哪些變更。

匯入 ExoPlayer IMA 擴充功能

新增 ExoPlayer 擴充功能的匯入陳述式。

將下列私人變數新增至 MyActivity.java

新增 Big Buck Bunny (Live) HLS 串流資產金鑰,以便進行測試 串流。您可以在 IMA 的範例串流頁面上測試更多串流。

建立 KEY_ADS_LOADER_STATE 常數,用於儲存和擷取 AdsLoader 時間。

範例如下:

app/src/main/java/com/example/project name/MyActivity.java


import static androidx.media3.common.C.CONTENT_TYPE_HLS;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionMediaSource;
import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionUriBuilder;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.util.EventLogger;
import androidx.media3.ui.PlayerView;
import androidx.multidex.MultiDex;

...

public class MyActivity extends Activity {

  private static final String KEY_ADS_LOADER_STATE = "ads_loader_state";
  private static final String SAMPLE_ASSET_KEY = "c-rArva4ShKVIAkNfy6HUQ";

  private PlayerView playerView;
  private ExoPlayer player;
  private ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader;
  private ImaServerSideAdInsertionMediaSource.AdsLoader.State adsLoaderState;

}

建立 adsLoader 執行個體

覆寫 onCreate 方法來尋找 PlayerView,並檢查已儲存的 AdsLoader.State,以便在啟動 adsLoader 物件時使用。

此外,如果應用程式的方法計數和 minSdkVersion 需要,請啟用 Multidex (如步驟 2 所述)。

範例如下:

app/src/main/java/com/example/project name/MyActivity.java

...

public class MyActivity extends Activity {

  private static final String KEY_ADS_LOADER_STATE = "ads_loader_state";
  private static final String SAMPLE_ASSET_KEY = "c-rArva4ShKVIAkNfy6HUQ";

  private PlayerView playerView;
  private ExoPlayer player;
  private ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader;
  private ImaServerSideAdInsertionMediaSource.AdsLoader.State adsLoaderState;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_my);
    MultiDex.install(this);

    playerView = findViewById(R.id.player_view);

    // Checks if there is a saved AdsLoader state to be used later when
    // initiating the AdsLoader.
    if (savedInstanceState != null) {
      Bundle adsLoaderStateBundle = savedInstanceState.getBundle(KEY_ADS_LOADER_STATE);
      if (adsLoaderStateBundle != null) {
        adsLoaderState =
            ImaServerSideAdInsertionMediaSource.AdsLoader.State.fromBundle(
                adsLoaderStateBundle);
      }
    }
  }

}

新增用於初始化播放器的方法

新增方法初始化播放器,然後執行下列操作:

  • 建立 AdsLoader 執行個體。
  • 建立 ExoPlayer
  • 使用直播的資產金鑰建立 MediaItem
  • MediaItem 設為玩家。

範例如下:

app/src/main/java/com/example/project name/MyActivity.java

public class MyActivity extends Activity {

  ...

  
  // Create a server side ad insertion (SSAI) AdsLoader.
  private ImaServerSideAdInsertionMediaSource.AdsLoader createAdsLoader() {
    ImaServerSideAdInsertionMediaSource.AdsLoader.Builder adsLoaderBuilder =
        new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(this, playerView);

    // Attempt to set the AdsLoader state if available from a previous session.
    if (adsLoaderState != null) {
      adsLoaderBuilder.setAdsLoaderState(adsLoaderState);
    }

    return adsLoaderBuilder.build();
  }

  private void initializePlayer() {
    adsLoader = createAdsLoader();

    // Set up the factory for media sources, passing the ads loader.
    DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(this);
    DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(dataSourceFactory);

    // MediaSource.Factory to create the ad sources for the current player.
    ImaServerSideAdInsertionMediaSource.Factory adsMediaSourceFactory =
        new ImaServerSideAdInsertionMediaSource.Factory(adsLoader, mediaSourceFactory);

    // 'mediaSourceFactory' is an ExoPlayer component for the DefaultMediaSourceFactory.
    // 'adsMediaSourceFactory' is an ExoPlayer component for a MediaSource factory for IMA server
    // side inserted ad streams.
    mediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory);

    // Create an ExoPlayer and set it as the player for content and ads.
    player = new ExoPlayer.Builder(this).setMediaSourceFactory(mediaSourceFactory).build();
    playerView.setPlayer(player);
    adsLoader.setPlayer(player);

    // Build an IMA SSAI media item to prepare the player with.
    Uri ssaiLiveUri =
        new ImaServerSideAdInsertionUriBuilder()
            .setAssetKey(SAMPLE_ASSET_KEY)
            .setFormat(CONTENT_TYPE_HLS) // Use CONTENT_TYPE_DASH for dash streams.
            .build();

    // Create the MediaItem to play, specifying the stream URI.
    MediaItem ssaiMediaItem = MediaItem.fromUri(ssaiLiveUri);

    // Prepare the content and ad to be played with the ExoPlayer.
    player.setMediaItem(ssaiMediaItem);
    player.prepare();

    // Set PlayWhenReady. If true, content and ads will autoplay.
    player.setPlayWhenReady(false);
  }
}

新增方法來釋放播放器

新增依照以下順序釋放播放器的方法:

  • 將玩家參照設為空值,並釋出玩家的資源。
  • 釋出 adsLoader 的狀態。

app/src/main/java/com/example/project name/MyActivity.java

public class MyActivity extends Activity {

  ...

  private void releasePlayer() {
    // Set the player references to null and release the player's resources.
    playerView.setPlayer(null);
    player.release();
    player = null;

    // Release the adsLoader state so that it can be initiated again.
    adsLoaderState = adsLoader.release();
  }

處理播放器事件

最後,為活動的生命週期事件建立回呼來處理串流 播放。

如要支援 Android SDK 24 以上版本:

如要支援 Android SDK 24 以下版本,請按照下列步驟操作: - onResume() - onPause()

onStart()onResume() 會對應至 playerView.onResume(),而 onStop()onPause() 則會對應至 playerView.onPause()

這個步驟也會用到 onSaveInstanceState() 事件,嘗試儲存 adsLoaderState

app/src/main/java/com/example/project name/MyActivity.java

public class MyActivity extends Activity {

  ...

  @Override
  public void onStart() {
    super.onStart();
    if (Util.SDK_INT > 23) {
      initializePlayer();
      if (playerView != null) {
        playerView.onResume();
      }
    }
  }

  @Override
  public void onResume() {
    super.onResume();
    if (Util.SDK_INT <= 23 || player == null) {
      initializePlayer();
      if (playerView != null) {
        playerView.onResume();
      }
    }
  }

  @Override
  public void onPause() {
    super.onPause();
    if (Util.SDK_INT <= 23) {
      if (playerView != null) {
        playerView.onPause();
      }
      releasePlayer();
    }
  }

  @Override
  public void onStop() {
    super.onStop();
    if (Util.SDK_INT > 23) {
      if (playerView != null) {
        playerView.onPause();
      }
      releasePlayer();
    }
  }

  @Override
  public void onSaveInstanceState(Bundle outState) {
    // Attempts to save the AdsLoader state to handle app backgrounding.
    if (adsLoaderState != null) {
      outState.putBundle(KEY_ADS_LOADER_STATE, adsLoaderState.toBundle());
    }
  }

  ...

}

VOD 串流設定 (選用)

如果您的應用程式需要播放含廣告的 VOD 內容,您必須執行下列操作:

  1. 為 VOD 測試串流新增 CMS IDVideo ID
  2. 使用 ImaServerSideAdInsertionUriBuilder() 建立 SSAI VOD URI。
  3. 使用這個新的 URI 做為播放器的媒體項目。

app/src/main/java/com/example/project name/MyActivity.java

public class MyActivity extends Activity {

  private static final String KEY_ADS_LOADER_STATE = "ads_loader_state";
  private static final String SAMPLE_ASSET_KEY = "c-rArva4ShKVIAkNfy6HUQ";
  private static final String SAMPLE_CMS_ID = "2548831";
  private static final String SAMPLE_VIDEO_ID = "tears-of-steel";

  ...

  private void initializePlayer() {

     ...

    Uri ssaiVodUri =
        new ImaServerSideAdInsertionUriBuilder()
            .setContentSourceId(SAMPLE_CMS_ID)
            .setVideoId(SAMPLE_VIDEO_ID)
            .setFormat(CONTENT_TYPE_HLS)
            .build();

    // Create the MediaItem to play, specifying the stream URI.
    MediaItem ssaiMediaItem = MediaItem.fromUri(ssaiVodUri);

    // Prepare the content and ad to be played with the ExoPlayer.
    player.setMediaItem(ssaiMediaItem);
    player.prepare();

    // Set PlayWhenReady. If true, content and ads will autoplay.
    player.setPlayWhenReady(false);
  }

大功告成!您現在透過 ExoPlayer 要求及播放媒體串流 IMA 擴充功能。如需完整程式碼,請參閱 GitHub 上的 Android DAI 範例