在 Chromecast 上使用 IMA DAI SDK

播放使用 Google Cloud Video Stitcher API 注册的直播

本指南演示了如何使用适用于 CAF Web 接收器的 IMA DAI SDK 请求并播放已通过 Google Cloud Video Stitcher API 注册的活动的直播,以及在播放期间插入广告插播时间点。

本指南对完整服务 DAI 的基本示例进行了扩展,添加了对使用 Google Cloud Video Stitcher API 注册的串流的支持。

请先确保 CAF Web 接收器支持您的在线播放格式,然后再继续。

如需了解如何与其他平台集成或如何使用 IMA 客户端 SDK,请参阅互动式媒体广告 SDK

背景

在使用本指南之前,请先熟悉 Chromecast 应用框架的 Web 接收器协议。

本指南假定您基本熟悉 CAF 接收器概念,例如消息拦截器MediaInformation 对象,以及使用 Cast 命令和控制工具来模拟 CAF 发送器。

应用组件和架构

如本指南所示,使用 Google Cloud Video Stitcher API 和 IMA CAF DAI SDK 实现直播播放涉及两个主要组成部分:

  • VideoStitcherLiveStreamRequest:用于定义对 Google 服务器的流式传输请求的对象。该请求指定 Cloud Video Stitcher API 的实例、直播配置 ID 和其他可选参数。
  • StreamManager:用于处理视频流与 IMA DAI SDK 之间的通信的对象,例如触发跟踪 ping 和将流事件转发给发布商。

前提条件

您需要为 IMA SDK 提供以下变量:

  • 实时配置 ID:这是您在创建 Video Stitcher API 实时配置时指定的实时配置 ID。

    LIVE_CONFIG_ID

  • 位置:创建实时配置的 Google Cloud 区域。

    LOCATION

  • Project Number:使用 Video Stitcher API 的 Google Cloud 项目编号。

    PROJECT_NUMBER

  • OAuth 令牌:服务账号的短期有效 OAuth 令牌,具有视频剪辑器用户角色。详细了解如何为服务账号创建短期凭据

    OAUTH_TOKEN

  • 广告资源网代码:用于请求广告的 Google Ad Manager 广告资源网代码。

    NETWORK_CODE

  • 自定义素材资源键:使用 Video Stitcher API 为直播活动创建配置过程中生成的 Google Ad Manager 自定义素材资源键。

    CUSTOM_ASSET_KEY

如需使用自定义 Cast 接收器,您需要满足以下要求:

准备好发送端以将流数据传递给接收端

首先,将发送方应用配置为向 Web 接收器发出加载请求,并在平台的 MediaInformation 对象中包含以下字段。

字段 目录
contentId 此媒体内容的唯一标识符,如投放参考文档中所定义。请勿将此 ID 重复用于同一媒体队列中的多个项。

CONTENT_ID

contentUrl 备用直播网址(可选),用于在 DAI 直播无法加载时播放。

BACKUP_STREAM_URL

contentType 备用视频流网址的 MIME 类型(可选),用于在 DAI 视频流无法加载时播放。

BACKUP_STREAM_MIMETYPE

streamType 用于此值的字符串字面量或常量因发件人平台而异。

LIVE

customData

customData 字段包含其他必需字段的键值对存储区。在本例中,customData 包含您收集的 DAI 数据流。

字段 目录
liveConfigID LIVE_CONFIG_ID
region LOCATION
projectNumber PROJECT_NUMBER
oAuthToken OAUTH_TOKEN
networkCode NETWORK_CODE
customAssetKey CUSTOM_ASSET_KEY

以下是一些代码示例,可帮助您开始使用:

Web

如需在 Cast Web 发送器中配置这些值,请先创建包含所需数据的 MediaInfo 对象,然后向 Web 接收器发出加载请求

// Create mediaInfo object
const mediaInfo = new chrome.cast.media.MediaInfo("CONTENT_ID");
mediaInfo.contentUrl = "BACKUP_STREAM_URL";
mediaInfo.contentType = "BACKUP_STREAM_MIMETYPE";
mediaInfo.streamType = chrome.cast.media.StreamType.LIVE;
mediaInfo.customData = {
liveConfigID: "LIVE_CONFIG_ID",
region: "LOCATION",
projectNumber: "PROJECT_NUMBER",
oAuthToken: "OAUTH_TOKEN",
networkCode: "NETWORK_CODE",
customAssetKey: "CUSTOM_ASSET_KEY"
};

// Make load request to cast web receiver
const castSession = cast.framework.CastContext.getInstance().getCurrentSession();
const request = new chrome.cast.media.LoadRequest(mediaInfo);
castSession.loadMedia(request).then(
  () => { console.log('Load succeed'); },
  (errorCode) => { console.log('Error code: ' + errorCode); });

Android

如需在 Cast Web 发送器中配置这些值,请先使用所需数据创建 MediaInfo 对象,然后向 Web 接收器发出加载请求

JSONObject customData = new JSONObject()
  .put("liveConfigID", "LIVE_CONFIG_ID")
  .put("region", "LOCATION")
  .put("projectNumber", "PROJECT_NUMBER")
  .put("oAuthToken", "OAUTH_TOKEN")
  .put("networkCode", "NETWORK_CODE")
  .put("customAssetKey", "CUSTOM_ASSET_KEY");

MediaInfo mediaInfo = MediaInfo.Builder("CONTENT_ID")
  .setContentUrl("BACKUP_STREAM_URL")
  .setContentType("BACKUP_STREAM_MIMETYPE")
  .setStreamType(MediaInfo.STREAM_TYPE_LIVE)
  .setCustomData(customData)
  .build();

RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());

iOS (Obj-C)

如需在 Cast Web 发送器中配置这些值,请先创建包含所需数据的 GCKMediaInformation 对象,然后向 Web 接收器发出加载请求

NSURL url = [NSURL URLWithString:@"BACKUP_STREAM_URL"];
NSDictionary *customData = @{
  @"liveConfigID": @"LIVE_CONFIG_ID",
  @"region": @"LOCATION",
  @"projectNumber": @"PROJECT_NUMBER",
  @"oAuthToken": @"OAUTH_TOKEN",
  @"networkCode": @"NETWORK_CODE",
  @"customAssetKey": @"CUSTOM_ASSET_KEY"
};

GCKMediaInformationBuilder *mediaInfoBuilder =
  [[GCKMediaInformationBuilder alloc] initWithContentID: @"CONTENT_ID"];
mediaInfoBuilder.contentURL = url;
mediaInfoBuilder.contentType = @"BACKUP_STREAM_MIMETYPE";
mediaInfoBuilder.streamType = GCKMediaStreamTypeLive;
mediaInfoBuilder.customData = customData;
self.mediaInformation = [mediaInfoBuilder build];

GCKRequest *request = [self.sessionManager.currentSession.remoteMediaClient loadMedia:self.mediaInformation];
if (request != nil) {
  request.delegate = self;
}

iOS (Swift)

如需在 Cast Web 发送器中配置这些值,请先创建包含所需数据的 GCKMediaInformation 对象,然后向 Web 接收器发出加载请求

let url = URL.init(string: "BACKUP_STREAM_URL")
guard let mediaURL = url else {
  print("invalid mediaURL")
  return
}

let customData = [
  "liveConfigID": "LIVE_CONFIG_ID",
  "region": "LOCATION",
  "projectNumber": "PROJECT_NUMBER",
  "oAuthToken": "OAUTH_TOKEN",
  "networkCode": "NETWORK_CODE",
  "customAssetKey": "CUSTOM_ASSET_KEY"
]

let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentId: "CONTENT_ID")
mediaInfoBuilder.contentURL = mediaUrl
mediaInfoBuilder.contentType = "BACKUP_STREAM_MIMETYPE"
mediaInfoBuilder.streamType = GCKMediaStreamType.Live
mediaInfoBuilder.customData = customData
mediaInformation = mediaInfoBuilder.build()

guard let mediaInfo = mediaInformation else {
  print("invalid mediaInformation")
  return
}

if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInfo) {
  request.delegate = self
}

CAC 工具

如需在 Cast 命令和控制工具中配置这些值,请点击“Load Media”(加载媒体)标签页,然后将自定义加载请求类型设置为“LOAD”。然后,将文本区域中的 JSON 数据替换为以下 JSON:

{
  "media": {
    "contentId": "CONTENT_ID",
    "contentUrl": "BACKUP_STREAM_URL",
    "contentType": "BACKUP_STREAM_MIMETYPE",
    "streamType": "LIVE",
    "customData": {
      "liveConfigID": "LIVE_CONFIG_ID",
      "region": "LOCATION",
      "projectNumber": "PROJECT_NUMBER",
      "oAuthToken": "OAUTH_TOKEN",
      "networkCode": "NETWORK_CODE",
      "customAssetKey": "CUSTOM_ASSET_KEY"
    }
  }
}

此自定义加载请求可发送给接收器,以测试其余步骤。

创建自定义 CAF Web 接收器

创建自定义 Web 接收器,如 CAF SDK 自定义 Web 接收器指南中所述。

接收器的代码应如下所示:

<html>
<head>
  <script
      src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js">
  </script>
</head>
<body>
  <cast-media-player></cast-media-player>
  <script>
    const castContext = cast.framework.CastReceiverContext.getInstance()
    castContext.start();
  </script>
</body>
</html>

导入 IMA DAI SDK 并获取 Player Manager

在脚本加载 CAF 后,添加一个脚本标记,以将适用于 CAF 的 IMA DAI SDK 导入到您的 Web 接收器。然后,在接下来的脚本标记中,将接收器上下文和播放器管理器存储为常量,然后再启动接收器。

<html>
<head>
  <script
      src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
  <script src="//imasdk.googleapis.com/js/sdkloader/cast_dai.js"></script>
</head>
<body>
  <cast-media-player></cast-media-player>
  <script>
    const castContext = cast.framework.CastReceiverContext.getInstance();
    const playerManager = castContext.getPlayerManager();

    castContext.start();
  </script>
</body>
</html>

初始化 IMA 串流管理器

初始化 IMA 串流管理器。

<html>
<head>
  <script type="text/javascript"
      src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
  <script src="//imasdk.googleapis.com/js/sdkloader/cast_dai.js"></script>
</head>
<body>
  <cast-media-player></cast-media-player>
  <script>
    const castContext = cast.framework.CastReceiverContext.getInstance();
    const playerManager = castContext.getPlayerManager();
    const streamManager = new google.ima.cast.dai.api.StreamManager();

    castContext.start();
  </script>
</body>
</html>

创建 Stream Manager 加载拦截器

在媒体内容传递给 CAF 之前,请在 LOAD 消息拦截器中创建流式传输请求。

    const castContext = cast.framework.CastReceiverContext.getInstance();
    const playerManager = castContext.getPlayerManager();
    const streamManager = new google.ima.cast.dai.api.StreamManager();

    /**
     * Creates a livestream request object for the Video Stitcher API.
     * @param {!LoadRequestData} castRequest The request object from the cast sender
     * @return {StreamRequest} an IMA stream request
     */
    const createStreamRequest = (castRequest) => { /* ... */};

    /**
     * Initates a DAI stream request for the final stream manifest.
     * @param {!LoadRequestData} castRequest The request object from the cast sender
     * @return {Promise<LoadRequestData>} a promise that resolves to an updated castRequest, containing the DAI stream manifest
     */
    const createDAICastRequest = (castRequest) => {
        return streamManager.requestStream(castRequest, createStreamRequest(castRequest))
          .then((castRequestWithStreamData) => {
            console.log('Successfully made DAI stream request.');
            return castRequestWithStreamData;
          })
          .catch((error) => {
            console.log('Failed to make DAI stream request.');
            // CAF will automatically fallback to the content URL
            // that it can read from the castRequest object.
            return castRequest;
          });
    };

    playerManager.setMessageInterceptor(
        cast.framework.messages.MessageType.LOAD, createDAICastRequest);

    castContext.start();

创建数据流请求

完成 createStreamRequest 函数,以便根据 CAF 加载请求创建 Video Stitcher API 直播请求。

    /**
     * Creates a livestream request object for the Video Stitcher API.
     * @param {!LoadRequestData} castRequest The request object from the cast sender
     * @return {StreamRequest} an IMA stream request
     */
    const createStreamRequest = (castRequest) => {
      const streamRequest = new google.ima.cast.dai.api.VideoStitcherLiveStreamRequest();
      const customData = castRequest.media.customData;

      streamRequest.liveStreamEventId = customData.liveConfigID;
      streamRequest.region = customData.region;
      streamRequest.projectNumber = customData.projectNumber;
      streamRequest.oAuthToken = customData.oAuthToken;
      streamRequest.networkCode = customData.networkCode;
      streamRequest.customAssetKey = customData.customAssetKey;

      return streamRequest;
    };

(可选)添加在线播放会话选项

使用 VideoStitcherLiveStreamRequest.videoStitcherSessionOptions 添加会话选项以替换默认的 Cloud Video Stitcher API 配置,从而自定义您的串流请求。 如果您提供的选项无法识别,Cloud Video Stitcher API 将返回 HTTP 400 错误。如需帮助,请参阅问题排查指南

例如,您可以使用以下代码段替换清单选项,该代码段会请求两个串流清单,其中呈现方式的排序方式为从最低比特率到最高比特率。

...

// The following session options are examples. Use session options
// that are compatible with your video stream.
streamRequest.videoStitcherSessionOptions = {
  "manifestOptions": {
    "bitrateOrder": "ascending"
  }
};

streamManager.requestStream(streamRequest);