在 Chromecast 上使用 IMA DAI SDK

播放通过 Google Cloud Video Stitcher API 注册的直播

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

本指南扩展了全服务 DAI 的基本示例,增加了对通过 Google Cloud Video Stitcher API 注册的视频流的支持。

继续操作之前,请确保 CAF 网络接收器支持您的流式传输格式。

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

背景

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

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

应用组件和架构

借助 IMA CAF DAI SDK,使用 Google Cloud Video Stitcher API 实现直播播放需要两个主要组件,如本指南中所示:

  • 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 令牌:具有 Video Stitcher 用户角色的服务帐号的短期有效 OAuth 令牌。详细了解如何为服务帐号创建短期有效凭据

    OAUTH_TOKEN

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

    NETWORK_CODE

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

    CUSTOM_ASSET_KEY

自定义 Cast 接收器需要以下内容:

准备发送器以将流数据传递给接收者

首先,配置发送器应用,使其向网络接收器发出加载请求,其中包含平台的 MediaInformation 对象中的以下字段。

字段 目录
contentId 此媒体项的唯一标识符,如 Cast 参考文档中所定义。此 ID 不应重复用于同一媒体队列中的多个项。

CONTENT_ID

contentUrl DAI 视频流加载失败时要播放的可选备用视频流网址。

BACKUP_STREAM_URL

contentType DAI 视频流加载失败时要播放的备用视频流网址的可选 MIME 类型。

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 网页发送器中配置这些值,请先创建包含所需数据的 MediaInfo 对象,然后向网络接收器发出加载请求

// 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 网页发送器中配置这些值,请先创建包含所需数据的 MediaInfo 对象,然后向网络接收器发出加载请求

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 网页发送器中配置这些值,请先创建包含所需数据的 GCKMediaInformation 对象,然后向网络接收器发出加载请求

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 网页发送器中配置这些值,请先创建包含所需数据的 GCKMediaInformation 对象,然后向网络接收器发出加载请求

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。然后将文本区域中的 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 接收器,如 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 并获取播放器管理器

添加脚本代码,将适用于 CAF 的 IMA DAI SDK 导入您的网络接收器(在脚本加载 CAF 之后立即进行)。然后,在随后的脚本代码中,将接收器上下文和播放器管理器存储为常量,然后再启动接收器。

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

创建数据流管理器加载拦截器

在媒体项传递给 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;
    };