设置 IMA SDK for DAI

借助 IMA SDK,您可以轻松地将多媒体广告集成到网站和应用中。IMA SDK 可以从任何 符合 VAST 标准的广告服务器请求广告,并在您的应用中管理广告播放。借助 IMA DAI SDK,应用可以针对广告和内容视频(VOD 或直播内容)发出视频流请求。然后,SDK 会返回一个组合视频流,这样您就不必在应用内管理广告和内容视频之间的切换。

选择您感兴趣的 DAI 解决方案

Pod Serving DAI

本指南演示了如何使用 IMA DAI SDK for HTML5 和依赖于 hls.js 进行播放的视频播放器,播放直播或 VOD 内容的 DAI Pod Serving 流。如需查看或遵循已完成的示例集成(同时支持 HLS.js 和 Safari 播放),请参阅 HLS Pod 服务示例。如需了解 DASH.js 支持,请参阅 DASH Pod 服务示例。 您可以从 HTML5 DAI GitHub 发布页面下载这些示例应用。

DAI Pod Serving 概览

使用 IMA DAI SDK 实现广告连播涉及两个主要组件,本指南将对此进行演示:

  • PodStreamRequest / PodVodStreamRequest:用于定义向 Google 广告服务器发出的信息流请求的对象。请求指定影音平台代码PodStreamRequest 还要求提供自定义资源密钥和可选的 API 密钥。两者都包含其他可选参数。

  • StreamManager:用于处理视频流与 IMA DAI SDK 之间通信的对象,例如触发跟踪 ping 和将流事件转发给发布商。

前提条件

在开始之前,您需要做好以下准备:

  • 三个空文件:

    • dai.html
    • dai.css
    • dai.js
  • 在用于测试的计算机上安装 Python,或使用网络服务器或其他托管式开发环境

配置开发环境

由于 SDK 加载依赖项时会使用协议,此协议与加载该 SDK 的网页所用的协议相同,因此您需要使用网络服务器来测试应用。要启动本地开发服务器,最简单的方法是使用 Python 的内置服务器。

  1. 在您的 index.html 文件所在的目录下使用命令行运行以下命令:

    python -m http.server 8000
  2. 在网络浏览器中,前往 http://localhost:8000/

    您也可以使用任何其他托管的开发环境或网络服务器,例如 Apache HTTP Server

创建视频播放器

首先,修改 dai.html,以创建一个 HTML5 视频元素和一个用于广告界面元素的 div。此外,还要添加必要的标记来加载 dai.cssdai.js 文件,以及导入 hls.js 视频播放器。

然后,修改 dai.css 以指定网页元素的大小和位置。 最后,在 dai.js 中,定义用于保存视频流请求信息的变量,以及在页面加载时运行的 initPlayer() 函数。

流式请求常量如下:

  • BACKUP_STREAM:备用视频流的网址,用于在广告进程遇到严重错误时播放。

  • STREAM_URL仅用于直播视频。由清单操纵器或使用广告连播投放的第三方合作伙伴提供的视频流网址。它应该要求您在发出请求之前插入 IMA DAI SDK 提供的视频流 ID。在这种情况下,直播网址包含一个占位符 [[STREAMID]],该占位符会在发出请求之前替换为直播 ID。

  • NETWORK_CODE:您的 Ad Manager 360 账号的广告资源网代码。

  • CUSTOM_ASSET_KEY仅用于直播视频。用于在 Ad Manager 360 中标识您的 Pod Serving 事件的自定义素材资源键。此文件可由您的清单操纵器或第三方 Pod Serving 合作伙伴创建。

  • API_KEY仅用于直播视频。一个可选的 API 密钥,可能需要该密钥才能从 IMA DAI SDK 检索视频流 ID。

dai.html

<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
  <script src="dai.js"></script>
  <link rel="stylesheet" href="dai.css" type="text/css">
</head>
<body onLoad="initPlayer()">
  <h2>IMA DAI SDK Demo (HLS.JS)</h2>
    <video id="video"></video>
    <div id="adUi"></div>
</body>
</html>

dai.css

#video,
#adUi {
  width: 640px;
  height: 360px;
  position: absolute;
  top: 35px;
  left: 0;
}

#adUi {
  cursor: pointer;
}

dai.js

var BACKUP_STREAM =
    'https://storage.googleapis.com/interactive-media-ads/media/bbb.m3u8'

// Stream Config.
const STREAM_URL = "";
const NETWORK_CODE = "";
const CUSTOM_ASSET_KEY = "";
const API_KEY = "";

var hls = new Hls(); // hls.js video player
var videoElement;
var adUiElement;

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
}

加载 IMA DAI SDK

接下来,在 dai.html 中插入一个脚本标记,将其放在 dai.js 对应的标记之前,以便添加 DAI 框架。

dai.html

<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
  <script type="text/javascript" src="//imasdk.googleapis.com/js/sdkloader/ima3_dai.js"></script>
  <script src="dai.js"></script>
  <link rel="stylesheet" href="dai.css" type="text/css">
</head>
...

初始化 StreamManager 并发出直播或 VOD 流请求

直播 Pod 服务

为了请求一组广告,请创建一个 ima.dai.api.StreamManager,该类负责请求和管理 DAI 流。构造函数会接收一个视频元素,而生成的实例会接收一个广告界面元素来处理广告互动。

然后,定义一个函数来请求 Pod Serving 直播。此函数首先创建一个 PodStreamRequest,使用第 2 步中提供的 streamRequest 参数对其进行配置,然后使用该请求对象调用 streamManager.requestStream()

dai.js

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
  streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement)

  requestLivePodStream(NETWORK_CODE, CUSTOM_ASSET_KEY, API_KEY);
}

function requestLivePodStream(networkCode, customAssetKey, apiKey) {
  // clear HLS.js instance, if in use
  if (hls) {
    hls.destroy();
  }

  // Generate a Pod Serving live Stream Request
  const streamRequest = new google.ima.dai.api.PodStreamRequest();
  streamRequest.networkCode = networkCode;
  streamRequest.customAssetKey = customAssetKey;
  streamRequest.apiKey = apiKey;
  streamRequest.format = 'hls';
  streamManager.requestStream(streamRequest);
}

视频点播广告连播投放

为了请求一组广告,请创建一个 ima.dai.api.StreamManager,该类负责请求和管理 DAI 流。构造函数会接收一个视频元素,而生成的实例会接收一个广告界面元素来处理广告互动。

然后,定义一个函数来请求 Pod Serving VOD 视频流。此函数首先创建一个 PodVodStreamRequest,使用第 2 步中提供的 streamRequest 参数对其进行配置,然后使用该请求对象调用 streamManager.requestStream()

dai.js

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
  streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement)

  requestVodPodStream(NETWORK_CODE);
}

function requestVodPodStream(networkCode) {
  // clear HLS.js instance, if in use
  if (hls) {
    hls.destroy();
  }

  // Generate a Pod Serving VOD Stream Request
  const streamRequest = new google.ima.dai.api.PodVodStreamRequest();
  streamRequest.networkCode = networkCode;
  streamRequest.format = 'hls';
  streamManager.requestStream(streamRequest);
}

处理流事件

直播 Pod 服务

接下来,为主要视频事件实现事件监听器。此示例通过调用 onStreamEvent() 函数来处理 STREAM_INITIALIZEDERRORAD_BREAK_STARTEDAD_BREAK_ENDED 事件。此函数可处理视频流加载和错误,以及在播放广告时停用播放器控件(这是 SDK 所要求的)。当流加载完毕后,视频播放器会使用 loadStream() 函数加载并播放所提供的网址。

dai.js

var isAdBreak;

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
  streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement);
  
  streamManager.addEventListener(
    [google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED,
    google.ima.dai.api.StreamEvent.Type.ERROR,
    google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED,
    google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED],
    onStreamEvent,
    false);
...
function onStreamEvent(e) {
  switch (e.type) {
    case google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED:
      console.log('Stream initialized');
      loadStream(e.getStreamData().streamId);
      break;
    case google.ima.dai.api.StreamEvent.Type.ERROR:
      console.log('Error loading stream, playing backup stream.' + e);
      loadStream('');
      break;
    case google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED:
      console.log('Ad Break Started');
      isAdBreak = true;
      videoElement.controls = false;
      adUiElement.style.display = 'block';
      break;
    case google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED:
      console.log('Ad Break Ended');
      isAdBreak = false;
      videoElement.controls = true;
      adUiElement.style.display = 'none';
      break;
    default:
      break;
  }
}

function loadStream(streamID) {
  var url;
  if(streamID) {
    url = STREAM_URL.replace('[[STREAMID]]', streamID);
  } else {
    console.log('Stream Initialization Failed');
    url = BACKUP_STREAM;
  }
  console.log('Loading:' + url);
  hls.loadSource(url);
  hls.attachMedia(videoElement);
}

视频点播广告连播投放

接下来,为主要视频事件实现事件监听器。此示例通过调用 onStreamEvent() 函数来处理 STREAM_INITIALIZEDLOADEDERRORAD_BREAK_STARTEDAD_BREAK_ENDED 事件。此函数可处理视频流加载和错误,还可在播放广告时停用播放器控件(这是 SDK 的要求)。

此外,VOD Pod Serving 流需要调用 StreamManager.loadStreamMetadata() 来响应 STREAM_INITIALIZED 事件。您还需要向视频技术合作伙伴 (VTP) 请求直播网址。loadStreamMetadata() 调用成功后,系统会触发 LOADED 事件,您应在该事件中调用 loadStream() 函数并传入您的直播网址,以加载和播放直播。

var isAdBreak;

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
  streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement);
  
  streamManager.addEventListener(
    [google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED,
    google.ima.dai.api.StreamEvent.Type.ERROR,
    google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED,
    google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED],
    onStreamEvent,
    false);
...
function onStreamEvent(e) {
  switch (e.type) {
    case google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED:
      const streamId = e.getStreamData().streamId;
      // 'vtpInterface' is a place holder for your own video technology
      //  partner (VTP) API calls.
      vtpInterface.requestStreamURL({
        'streamId': streamId,
      })
      .then( (vtpStreamUrl) => {
        streamUrl = vtpStreamUrl;
        streamManager.loadStreamMetadata();
      }, (error) => {
        // Handle the error.
      });
      break;
    case google.ima.dai.api.StreamEvent.Type.LOADED:
      loadStream(streamUrl);
      break;
    case google.ima.dai.api.StreamEvent.Type.ERROR:
      console.log('Error loading stream, playing backup stream.' + e);
      loadStream();
      break;
    case google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED:
      console.log('Ad Break Started');
      isAdBreak = true;
      videoElement.controls = false;
      adUiElement.style.display = 'block';
      break;
    case google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED:
      console.log('Ad Break Ended');
      isAdBreak = false;
      videoElement.controls = true;
      adUiElement.style.display = 'none';
      break;
    default:
      break;
  }
}

function loadStream(url) {
  if(url) {
    console.log('Loading:' + url);
    hls.loadSource(url);
  } else {
    console.log('Stream Initialization Failed');
    hls.loadSource(BACKUP_STREAM);
  }
  hls.attachMedia(videoElement);
}

处理数据流元数据

在此步骤中,您将实现元数据的事件监听器,以便在发生广告事件时通知 SDK。监听插播广告元数据事件的方式可能会因以下因素而异:视频流格式(HLS 或 DASH)、视频流类型(直播或 VOD 视频流)、播放器类型以及所用 DAI 后端类型。如需了解详情,请参阅我们的定时元数据指南。

HLS 视频流格式(直播和 VOD 视频流,HLS.js 播放器)

如果您使用的是 HLS.js 播放器,请监听 HLS.js FRAG_PARSING_METADATA 事件以获取 ID3 元数据,并通过 StreamManager.processMetadata() 将其传递给 SDK。

为了在所有内容加载完毕并准备就绪后自动播放视频,请监听 HLS.js MANIFEST_PARSED 事件以触发播放。

function loadStream(streamID) {
  hls.loadSource(url);
  hls.attachMedia(videoElement);
  
  // Timed metadata is passed HLS stream events to the streamManager.
  hls.on(Hls.Events.FRAG_PARSING_METADATA, parseID3Events);
  hls.on(Hls.Events.MANIFEST_PARSED, startPlayback);
}

function parseID3Events(event, data) {
  if (streamManager && data) {
    // For each ID3 tag in the metadata, pass in the type - ID3, the
    // tag data (a byte array), and the presentation timestamp (PTS).
    data.samples.forEach((sample) => {
      streamManager.processMetadata('ID3', sample.data, sample.pts);
    });
  }
}

function startPlayback() {
  console.log('Video Play');
  videoElement.play();
}

DASH.js(DASH 视频流格式,直播和 VOD 视频流类型)

如果您使用的是 DASH.js 播放器,则必须使用不同的字符串来监听直播或 VOD 流的 ID3 元数据:

  • 直播:'https://developer.apple.com/streaming/emsg-id3'
  • 点播视频流:'urn:google:dai:2018'

使用 StreamManager.processMetadata() 将 ID3 元数据传递给 SDK。

如需在所有内容加载完毕并准备就绪后自动显示视频控件,请监听 DASH.js MANIFEST_LOADED 事件。

const googleLiveSchema = 'https://developer.apple.com/streaming/emsg-id3';
const googleVodSchema = 'urn:google:dai:2018';
dashPlayer.on(googleLiveSchema, processMetadata);
dashPlayer.on(googleVodSchema, processMetadata);
dashPlayer.on(dashjs.MediaPlayer.events.MANIFEST_LOADED, loadlistener);

function processMetadata(metadataEvent) {
  const messageData = metadataEvent.event.messageData;
  const timestamp = metadataEvent.event.calculatedPresentationTime;

  // Use StreamManager.processMetadata() if your video player provides raw
  // ID3 tags, as with dash.js.
  streamManager.processMetadata('ID3', messageData, timestamp);
}

function loadlistener() {
  showControls();

  // This listener must be removed, otherwise it triggers as addional
  // manifests are loaded. The manifest is loaded once for the content,
  // but additional manifests are loaded for upcoming ad breaks.
  dashPlayer.off(dashjs.MediaPlayer.events.MANIFEST_LOADED, loadlistener);
}

使用直播的 Shaka Player(DASH 流格式)

如果您使用 Shaka 播放器播放直播,请使用字符串 'emsg' 监听元数据事件。然后,在对 StreamManager.onTimedMetadata() 的调用中使用事件消息数据。

shakaPlayer.addEventListener('emsg', (event) => onEmsgEvent(event));

function onEmsgEvent(metadataEvent) {
  // Use StreamManager.onTimedMetadata() if your video player provides
  // processed metadata, as with Shaka player livestreams.
  streamManager.onTimedMetadata({'TXXX': metadataEvent.detail.messageData});
}

使用 VOD 视频流(DASH 视频流格式)的 Shaka Player

如果您使用 Shaka 播放器播放 VOD 流,请使用字符串 'timelineregionenter' 来监听元数据事件。然后,在调用 StreamManager.processMetadata() 时使用事件消息数据,并使用字符串 'urn:google:dai:2018'

shakaPlayer.addEventListener('timelineregionenter', (event) => onTimelineEvent(event));

function onTimelineEvent(metadataEvent) {
  const detail = metadataEvent.detail;
  if ( detail.eventElement.attributes &&
       detail.eventElement.attributes['messageData'] &&
       detail.eventElement.attributes['messageData'].value ) {
        const mediaId = detail.eventElement.attributes['messageData'].value;
        const pts = detail.startTime;
        // Use StreamManager.processMetadata() if your video player provides raw
        // ID3 tags, as with Shaka player VOD streams.
        streamManager.processMetadata('urn:google:dai:2018', mediaId, pts);
       }
}

处理播放器事件

向视频元素的 pausestart 事件添加事件监听器,以便用户在 SDK 因广告插播而暂停时恢复播放。

function loadStream(streamUrl) {
  ...
  
  videoElement.addEventListener('pause', onStreamPause);
  videoElement.addEventListener('play', onStreamPlay);
}

function onStreamPause() {
  console.log('paused');
  if (isAdBreak) {
    videoElement.controls = true;
    adUiElement.style.display = 'none';
  }
}

function onStreamPlay() {
  console.log('played');
  if (isAdBreak) {
    videoElement.controls = false;
    adUiElement.style.display = 'block';
  }
}

清理 IMA DAI 资产

当您使用 IMA DAI SDK 成功完成在 Pod Serving 视频流中请求和展示广告后,建议您在 Pod Serving 会话结束后清理所有资源。调用 StreamManager.destroy() 可停止视频流播放、停止所有广告跟踪并释放所有已加载的视频流素材资源。

如需了解更高级的 SDK 功能,请参阅其他指南或 GitHub 上的示例