ExoPlayer IMA 扩展程序使用入门

ExoPlayer 是一款适用于 Android 的应用级媒体播放器。本指南介绍了如何使用 ExoPlayer IMA 扩展程序(封装了 IMA Android 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 作为语言。

  • 点击 Finish

将 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 19
      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 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 SDK -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    ...

</manifest>

添加 intent 声明

如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,则当前版本和最新版本的 IMA 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;

...

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

添加释放播放器的方法

按以下序列添加方法以释放播放器:

  • 将播放器引用设置为 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 版本,请执行以下命令: - 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 = "2528370";
  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 示例,获取完整代码。