在 Chromecast 上使用 IMA DAI SDK

播放通过 Google Cloud Video Stitcher API 注册的 VOD 视频流

本指南将演示如何使用适用于 CAF 网络接收器的 IMA DAI SDK 执行以下操作: 请求并播放 Google Cloud VOD 视频流

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

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

有关与其他平台集成或使用 IMA 客户端 SDK,请参阅互动式媒体广告 SDK

背景

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

本指南假定您对 CAF 接收器概念有基本的了解, 如 message 拦截器MediaInformation 对象,并使用 Cast 命令和控件 工具来模拟 CAF 发送器。

应用组件和架构

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

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

设置 Google Cloud 项目

输入以下变量,以便在 IMA SDK 中使用:

  • 位置 - 您创建了 VOD 配置的 Google Cloud 区域。

    LOCATION

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

    PROJECT_NUMBER

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

    OAUTH_TOKEN

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

    NETWORK_CODE

  • VOD 配置 ID - VOD 视频流的 VOD 配置 ID。

    VOD_CONFIG_ID

    如需详细了解如何创建 VOD 配置 ID,请参阅 Cloud 拼接创建 VOD 配置指南

    VOD_URI

设置自定义投屏接收器

如需开发自定义 Cast 接收器,您需要下列项:

  • 拥有满足以下条件的 Cast Developer Console 账号: 许可名单中的测试设备。

  • 托管网络接收器 应用 已在 Cast Developer Console 中注册,并且可进行修改, 托管本指南提供的代码。

  • 配置为使用您的网站接收器应用的发送应用。为了 本指南使用 Cast 命令和控件 工具作为发件人。

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

首先,配置发送器应用,向网络接收器发出加载请求, 包含您平台的 MediaInformation 对象。

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

CONTENT_ID

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

BACKUP_STREAM_URL

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

BACKUP_STREAM_MIMETYPE

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

VOD

customData

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

字段 目录
region LOCATION
projectNumber PROJECT_NUMBER
oAuthToken OAUTH_TOKEN
networkCode NETWORK_CODE
vodConfigId VOD_CONFIG_ID

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

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.VOD;
mediaInfo.customData = {
  region: "LOCATION",
  projectNumber: "PROJECT_NUMBER",
  oAuthToken: "OAUTH_TOKEN",
  networkCode: "NETWORK_CODE",
  vodConfigId: "VOD_CONFIG_ID"
};

// 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("region", "LOCATION")
  .put("projectNumber", "PROJECT_NUMBER")
  .put("oAuthToken", "OAUTH_TOKEN")
  .put("networkCode", "NETWORK_CODE")
  .put("vodConfigId", "VOD_CONFIG_ID");

MediaInfo mediaInfo = MediaInfo.Builder("CONTENT_ID")
  .setContentUrl("BACKUP_STREAM_URL")
  .setContentType("BACKUP_STREAM_MIMETYPE")
  .setStreamType(MediaInfo.STREAM_TYPE_VOD)
  .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 = @{
  @"region": @"LOCATION",
  @"projectNumber": @"PROJECT_NUMBER",
  @"oAuthToken": @"OAUTH_TOKEN",
  @"networkCode": @"NETWORK_CODE",
  @"vodConfigId": @"VOD_CONFIG_ID"
};

GCKMediaInformationBuilder *mediaInfoBuilder =
  [[GCKMediaInformationBuilder alloc] initWithContentID: @"CONTENT_ID"];
mediaInfoBuilder.contentURL = url;
mediaInfoBuilder.contentType = @"BACKUP_STREAM_MIMETYPE";
mediaInfoBuilder.streamType = GCKMediaStreamTypeNone;
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 = [
  "region": "LOCATION",
  "projectNumber": "PROJECT_NUMBER",
  "oAuthToken": "OAUTH_TOKEN",
  "networkCode": "NETWORK_CODE",
  "vodConfigId": "VOD_CONFIG_ID"
]

let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentId: "CONTENT_ID")
mediaInfoBuilder.contentURL = mediaUrl
mediaInfoBuilder.contentType = "BACKUP_STREAM_MIMETYPE"
mediaInfoBuilder.streamType = GCKMediaStreamType.none
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": "VOD",
    "customData": {
      "region": "LOCATION",
      "projectNumber": "PROJECT_NUMBER",
      "oAuthToken": "OAUTH_TOKEN",
      "networkCode": "NETWORK_CODE",
      "vodConfigId": "VOD_CONFIG_ID"
    }
  }
}

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

创建自定义 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 并获取播放器管理器

添加脚本代码,将适用于 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 VOD stream 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 函数以创建 Video Stitcher API VOD 流式传输请求。

    /**
     * Creates a VOD stream 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.VideoStitcherVodStreamRequest();
      const customData = castRequest.media.customData;

      streamRequest.region = customData.region;
      streamRequest.projectNumber = customData.projectNumber;
      streamRequest.oAuthToken = customData.oAuthToken;
      streamRequest.networkCode = customData.networkCode;
      streamRequest.vodConfigId = customData.vodConfigId;
      streamRequest.videoStitcherSessionOptions = {};

      return streamRequest;
    };

(可选)添加直播会话选项

使用 VideoStitcherVodStreamRequest.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);