開始使用 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 指南為基礎,說明如何建立完整應用程式並整合擴充功能。如需完整範例應用程式的範例,請參閱 GitHub 上的 ExoPlayerExample

必要條件

建立新的 Android Studio 專案

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

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

  • 按一下「完成」

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

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

設定應用程式並啟用 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 需要明確宣告意圖,才能開啟網頁連結。將以下程式碼片段新增至應用程式的資訊清單檔案,即可啟用廣告點閱 (使用者點選「Learn more」按鈕)。

  <?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 示範串流頁面。如要瞭解如何設定自己的串流,請參閱 DAI 的 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 範例