在 Chromecast 上使用 IMA DAI SDK

播放使用 Google Cloud Video Stitcher API 註冊的直播

本指南將說明如何使用 IMA DAI SDK 為 CAF Web Receiver 要求並播放已透過 Google Cloud Video Stitcher API 註冊的活動直播,並在播放期間插入廣告插播。

本指南將擴充完整服務 DAI 的基本範例,新增對透過 Google Cloud Video Stitcher API 註冊的串流支援。

請先確認 CAF 網路接收器支援你的串流格式,再繼續操作。

如要瞭解如何整合其他平台,或使用 IMA 用戶端 SDK,請參閱「互動式媒體廣告 SDK」。

背景

使用本指南前,請先熟悉 Chromecast 應用程式架構的 Web Receiver 通訊協定。

本指南假設您對 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

  • 專案編號:使用 Video Stitcher API 的 Google Cloud 專案編號。

    PROJECT_NUMBER

  • OAuth 權杖:服務帳戶的短期 OAuth 權杖,具有 Video Stitcher 使用者角色。進一步瞭解如何為服務帳戶建立短期憑證

    OAUTH_TOKEN

  • Network Code:用於要求廣告的 Google Ad Manager 聯播網代碼。

    NETWORK_CODE

  • 自訂素材資源鍵:使用 Video Stitcher API 為直播活動建立設定時,Google Ad Manager 會產生自訂素材資源鍵。

    CUSTOM_ASSET_KEY

如要建立自訂投放接收端,您需要:

準備傳送端,將串流資料傳遞給接收端

首先,請設定傳送端應用程式,向網路接收端提出載入要求,並在平台的 MediaInformation 物件中包含下列欄位。

欄位 目錄
contentId 這個媒體項目的專屬 ID,如Cast 參考文件所定義。這個 ID 不應在同一媒體佇列中的多個項目中重複使用。

CONTENT_ID

contentUrl 選用備用串流網址,在動態廣告插播串流無法載入時播放。

BACKUP_STREAM_URL

contentType 在 DAI 串流無法載入時,可選的備用串流網址 Mimetype,用於播放備用串流。

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

以下提供一些程式碼範例,協助您快速上手:

網頁

如要在 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 指令與控制工具中設定這些值,請按一下「載入媒體」分頁,然後將自訂載入要求類型設為「載入」。然後將文字區域中的 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 網路接收器

CAF SDK 自訂網頁接收器指南所述,建立自訂網頁接收器。

接收器的程式碼應如下所示:

<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 後,新增指令碼標記,將 IMA DAI SDK for 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>

建立 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);