ExoPlayer IMA 扩展程序使用入门

ExoPlayer 是一款适用于 Android 的应用级媒体播放器。本指南介绍了如何使用封装了 IMA DAI SDK 的 ExoPlayer IMA 扩展程序来请求和播放包含广告和内容在内的媒体视频流。

以下是该扩展程序的一些优势:

  • 简化了将 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 模板。
  • 点击下一步
  • 配置项目页面中,为项目命名,然后选择 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.7.1'
    implementation 'androidx.media3:media3-exoplayer:1.7.1'
    implementation 'androidx.media3:media3-exoplayer-hls:1.7.1'
    implementation 'androidx.media3:media3-exoplayer-dash:1.7.1'

    // Adding the ExoPlayer IMA extension for ads will also include the IMA
    // SDK as a dependency.
    implementation 'androidx.media3:media3-exoplayer-ima:1.7.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>

添加 intent 声明

如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,当前版本和最新版本的 IMA DAI SDK 需要明确声明打开网页链接的 intent。将以下代码段添加到应用的清单文件中,以启用广告点击后到达的网页(用户点击了解详情按钮)。

  <?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 的界面

创建供 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 扩展程序的 import 语句。

将以下私有变量添加到 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;
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;

...

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

    // Initialize the IMA SDK as early as possible when the app starts. If your app already
    // overrides Application.onCreate(), call this method inside the onCreate() method.
    // https://developer.android.com/topic/performance/vitals/launch-time#app-creation
    ImaSdkFactory.getInstance().initialize(this, getImaSdkSettings());

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

  private ImaSdkSettings getImaSdkSettings() {
    if (imaSdkSettings == null) {
      imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
      // Set any IMA SDK settings here.
    }
    return imaSdkSettings;
  }

}

添加了用于初始化播放器的方法

添加一种用于初始化播放器的方法,并执行以下操作:

  • 创建一个 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
        .setImaSdkSettings(getImaSdkSettings())
        .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);
  }
}

添加释放播放器的方法

添加一种方法,以按以下顺序释放播放器:

  • 将播放器引用设置为 null 并释放播放器的资源。
  • 释放 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();
  }

处理播放器事件

最后,为 activity 的生命周期事件创建回调,以处理视频流播放。

如需支持 Android SDK 版本 24 及更高版本,请执行以下操作:

如需支持低于 24 的 Android SDK 版本,请执行以下操作:

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 示例