构建基本 Cast 接收器

googlecastnew500.png

在此 Codelab 中,您将学习如何构建支持 Cast 的接收器应用,使其可在 Google Cast 设备上播放内容。

什么是 Google Cast?

Google Cast 可让用户将移动设备上的内容投射到电视上。然后,用户可以将其移动设备或桌面设备上的 Chrome 浏览器用作遥控器,来控制电视上的媒体播放。

借助 Google Cast SDK,应用可以控制支持 Google Cast 的设备(例如:电视或音响系统)。Cast SDK 会根据 Google Cast 设计核对清单为您提供必需的界面组件。

Google Cast 设计核对清单用于在所有支持的平台上实现简单、可预测的 Cast 用户体验。请点击此处了解详情。

我们将要开发什么应用?

完成此 Codelab 后,您将拥有一个能在支持 Cast 的设备上显示视频内容的 HTML5 应用,作为您自己个人的自定义接收器

学习内容

  • 如何为接收器开发做好准备?
  • 有关基于 Cast 应用框架且支持 Cast 的接收器的基础知识。
  • 如何接收投射的视频?
  • 如何集成调试记录器?
  • 如何针对智能显示屏优化接收器?

所需条件

  • 最新版本的 Google Chrome 浏览器。
  • node.js、npm、http-serverngrok 模块。
  • 一台可连接到互联网的 Google Cast 设备,例如 ChromecastAndroid TV
  • 一台带 HDMI 输入端口的电视或显示器。

经验

  • 您需要具备 Web 开发经验。
  • 您还需要有观看电视的经验 :)

您打算如何使用本教程?

仅阅读教程内容 阅读并完成练习

您如何评价自己在构建 Web 应用方面的经验水平?

新手 中级 熟练

您如何评价自己在观看电视方面的经验水平?

新手 中级 熟练

您可以将所有示例代码下载到计算机上…

下载源代码

然后解压下载的 ZIP 文件。

为了能将接收器与 Cast 设备搭配使用,需要将接收器托管在 Cast 设备可以访问的某个位置。倘若您已有一台支持 https 的服务器可供使用,请跳过以下说明并记下其网址,因为下一部分需要用到该网址。

如果您没有可用的服务器,也无需担心。您可以安装 node.jshttp-serverngrok 节点模块。

npm install -g http-server
npm install -g ngrok

运行服务器

如果您使用的是 http-server,请转到控制台并运行以下命令:

cd app-start
http-server

然后,您应该会看到如下内容:

Starting up http-server, serving ./
Available on:
  http://127.0.0.1:8080
  http://172.19.17.192:8080
Hit CTRL-C to stop the server

请注意使用的本地端口,然后在新终端中运行以下命令,以使用 ngrok 通过 HTTPS 公开您的本地接收器:

ngrok http 8080

这会设置连接本地 HTTP 服务器的 ngrok 隧道,并为您分配一个能够全局使用的 HTTPS 安全端点,供您在下一步中使用 (https://116ec943.eu.ngrok.io):

ngrok by @inconshreveable                                                                                                                                                                                                                                     (Ctrl+C to quit)

Session Status         online
Version                2.2.4
Web Interface          http://127.0.0.1:8080
Forwarding             http://116ec943.eu.ngrok.io -> localhost:8080
Forwarding             https://116ec943.eu.ngrok.io -> localhost:8080

在完成此 Codelab 的过程中,您应让 ngrokhttp-server 保持运行状态。您在本地所做的任何更改都会立即生效。

您必须注册应用,才能在 Chromecast 设备上运行此 Codelab 中构建的这种自定义接收器。注册应用后,您会收到一个应用 ID,发送器应用必须使用此 ID 来执行 API 调用,以实现启动接收器应用等目的。

d8b39f5d33d33db4.png

点击“Add new application”

e8c19e57b85c7d.png

选择“Custom Receiver”,这是我们将构建的应用类型。

bf364a7d382e3c58.png

输入新接收器的详细信息,请务必使用您在上一部分最后

得出的网址。记下分配给新接收器的应用 ID

您还必须注册 Google Cast 设备,以便该设备可在您发布接收器应用前对其进行访问。接收器应用发布后,便可供所有 Google Cast 设备使用。在此 Codelab 中,建议使用未发布的接收器应用。

a446325da6ebd627.png

点击“Add new Device”

a21355793a3f4cd5.png

输入印在 Cast 设备背面的序列号,并为其指定一个描述性名称。在访问 Google Cast SDK Developer Console 时将屏幕投射到 Chrome 中,也可以找到此序列号

接收器和设备需要 5-15 分钟才能准备好进行测试。等待 5-15 分钟后,您必须重新启动 Cast 设备。

Google_Chrome_logo_icon.png

在等待新接收器应用做好测试准备期间,我们来看看完成后的示例接收器应用外观是怎样的。我们要构建的接收器能够使用自适应比特率流式传输来播放媒体内容(我们将使用针对基于 HTTP 的动态自适应流式传输 [DASH] 编码的示例内容)。

在浏览器中,打开命令与控制 (CaC) 工具

7dbd91a75140c46f.png

  1. 您应该会看到我们的 CaC 工具。
  2. 使用默认的“CC1AD845”示例接收器 ID,然后点击“Set App ID”按钮。
  3. 点击左上角的“Cast”按钮,然后选择您的 Google Cast 设备。

a02db8244a963fd6.png

  1. 转到顶部的“Load Media”标签页。

acd63df355a0493.png

  1. 点击“Load by Content”按钮播放示例视频。
  2. 该视频便会开始在您的 Google Cast 设备上播放,演示使用默认接收器时的基本接收器功能。

我们需要在您下载的起始应用中添加 Google Cast 支持。下面是一些我们会在此 Codelab 中使用的 Google Cast 术语:

  • 发送设备应用是指在移动设备或笔记本电脑上运行的应用;
  • 接收器应用是指在 Google Cast 设备上运行的应用。

现在,您可以使用自己喜爱的文本编辑器基于起始项目构建应用了:

  1. 从下载的示例代码中选择 android_studio_folder.pngapp-start 目录。
  2. 打开 js/receiver.jsindex.html

请注意,在执行此 Codelab 的过程中,http-server 应该会应用您所做的更改。如果您发现它并未这样做,请尝试终止并重启 http-server

应用设计

接收器应用初始化 Cast 会话并一直等待,直至收到某个发送器发出的 LOAD 请求(即,播放媒体内容的命令)。

该应用包含一个主视图(在 index.html 中定义)和一个名为 js/receiver.js 的 JavaScript 文件,该文件中包含接收器正常运行所需的全部逻辑。

index.html

此 HTML 文件将包含接收器应用的界面。目前,此文件是空的,我们将在整个 Codelab 过程中向其中添加内容。

receiver.js

此脚本将管理接收器应用的所有逻辑。此时它只是一个空文件,但在下一部分中,只需添加几行代码,我们便可将其变成功能完备的 Cast 接收器。

基本 Cast 接收器将在启动时初始化 Cast 会话。为了让连接的所有发送器应用得知接收器已成功启动,这一步必不可少。此外,新的 SDK 已经过预先配置,安装后即可直接用于处理自适应比特率流式传输媒体(使用 DASH、HLS 和 Smooth Streaming)和普通 MP4 文件。下面我们就来试一试。

初始化

将以下代码添加到 index.html 的头部:

<head>
  ...

  <script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
</head>

将以下代码添加到 index.html <body> 部分用于加载 receiver.js, 的 <footer> 之前,以便为接收器 SDK 提供空间来打开您刚添加的脚本所附带的默认接收器界面。

<cast-media-player></cast-media-player>

现在,我们需要初始化 js/receiver.js 中的 SDK,此过程包括以下几个步骤:

  • 获取对 CastReceiverContext(整个接收器 SDK 的主入口点)的引用
  • 存储对 PlayerManager 的引用,该对象负责处理播放并为您提供插入您自己的自定义逻辑所需的所有钩子
  • 通过对 CastReceiverContext 调用 start() 来初始化该 SDK

将以下内容添加到 js/receiver.js 中。

const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();

context.start();

在此 Codelab 中,请使用 CaC 工具来试用新接收器。

使用网络浏览器访问命令与控制 (CaC) 工具

72039b93a476e35e.png

请务必使用您之前注册的自己的应用 ID 代替相应字段的内容,然后点击“Set App ID”。这会指示该工具在启动 Cast 会话时使用您的接收器。

投射媒体内容

大体而言,如需在 Cast 设备上播放媒体内容,需要满足以下条件:

  1. 发送器从 Cast SDK 创建一个用于为媒体内容建模的 MediaInfo JSON 对象。
  2. 发送器连接到 Cast 设备以启动接收器应用。
  3. 接收器通过 LOAD 请求来加载 MediaInfo 对象以播放内容。
  4. 接收器监控并跟踪媒体状态。
  5. 发送器根据用户与发送器应用的互动情况向接收器发送播放命令,以控制播放。

这是第一次基本尝试,我们此时将在 MediaInfo 中填充一个可播放资源网址(存储于 MediaInfo.contentUrl 中)。

在实际情况中,发送器会使用 MediaInfo.contentId 中的应用专用媒体标识符。接收器使用 contentId 作为标识符来发出适当的后端 API 调用,以解析实际资源网址并将其设为 MediaInfo.contentUrl.此外,接收器还将处理一些其他任务,例如获取数字版权管理许可或注入有关广告插播时间点的信息。

我们将在下一部分中扩展您的接收器,使其执行此类操作。现在,请点击“Cast”图标,然后选择您的设备以打开您的接收器。

4954e949c3d3b232.png

转到“Load Media”标签页,然后点击“Load by Content”按钮。您的接收器应当开始播放示例内容。

ccd8cc258d0c1522.png

由此可见,接收器 SDK 在安装后即可直接用于处理以下任务:

  • 初始化 Cast 会话
  • 处理从发送器收到的包含可播放资源的 LOAD 请求
  • 提供可在大屏幕上显示的基本播放器界面

您尽可以在进入下一部分之前自由摸索和了解 CaC 工具及其代码,我们将在下一部分扩展接收器,使其与简单的示例 API 通信,以实现从发送器收到的 LOAD 请求。

我们将按照大多数开发者在实际应用中与 Cast 接收器交互的方式修改接收器,以便处理通过 API 密钥引用预期媒体内容而不是通过可播放资源网址发送的 LOAD 请求。

应用采用这种方式通常出于以下原因:

  • 发送器可能不知道内容网址。
  • Cast 应用旨在直接在接收器中处理身份验证、其他业务逻辑或 API 调用。

此功能主要在 PlayerManager setMessageInterceptor() 方法中实现。这样,您就能在收到的消息到达 SDK 内部消息处理程序之前,按类型拦截这些消息并对其进行修改。在这一部分中,我们将处理 LOAD 请求,需要执行以下操作:

  • 读取收到的 LOAD 请求及其自定义 contentId
  • 向我们的 API 发出 GET 调用,以便按 contentId 查找可流式传输的资源。
  • 使用媒体流的网址修改 LOAD 请求。
  • 修改 MediaInformation 对象以设置媒体流的类型参数。
  • 将请求传递到 SDK 以进行播放,或者在无法查找所请求的媒体时拒绝该命令。

提供的示例 API 展示了一些 SDK 钩子,用于自定义常见的接收器任务,同时仍主要依赖于“安装后即可直接使用”的卓越体验。

示例 API

使用浏览器访问 https://storage.googleapis.com/cpe-sample-media/content.json,并查看我们的示例视频目录。其中的内容包括 png 格式的海报图片的网址以及 DASH 和 HLS 媒体流的网址。DASH 和 HLS 媒体流指向存储在 fragmented mp4 容器中的多路分配视频和音频源。

{
  "bbb": {
    "author": "The Blender Project",
    "description": "Grumpy Bunny is grumpy",
    "poster": "https://[...]/[...]/BigBuckBunny/images/screenshot1.png",
    "stream": {
      "dash": "https://[...]/[...]/BigBuckBunny/BigBuckBunny_master.mpd",
      "hls": "https://[...]/[...]/BigBuckBunny/BigBuckBunny_master.m3u8",
    "title": "Big Buck Bunny"
  },
  "fbb_ad": {
    "author": "Google Inc.",
    "description": "Introducing Chromecast. The easiest way to enjoy [...]",
    "poster": "https://[...]/[...]/ForBiggerBlazes/images/screenshot8.png",
    "stream": {
      "dash": "https://[...]/[...]/ForBiggerBlazes/ForBiggerBlazes.mpd",
      "hls": "https://[...]/[...]/ForBiggerBlazes/ForBiggerBlazes.m3u8",
    "title": "For Bigger Blazes"
  },

  [...]

}

在下一步中,我们要在使用 LOAD 请求调用接收器之后,将每个条目的键(例如 bbb, fbb_ad)映射到媒体流的网址。

拦截 LOAD 请求

在此步骤中,我们将创建一个 load 请求拦截器,其中包含一个向托管的 JSON 文件发出 XHR 请求的函数。获取 JSON 文件后,我们将解析内容并设置元数据。在以下部分中,我们将自定义 MediaInformation 参数,以指定内容类型。

将以下代码添加到 js/receiver.js 文件中,紧邻在 context.start() 调用的前面。

function makeRequest (method, url) {
  return new Promise(function (resolve, reject) {
    let xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
    xhr.send();
  });
}

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
        // Fetch content repository by requested contentId
        makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json').then(function (data) {
          let item = data[request.media.contentId];
          if(!item) {
            // Content could not be found in repository
            reject();
          } else {
            // Add metadata
            let metadata = new
               cast.framework.messages.GenericMediaMetadata();
            metadata.title = item.title;
            metadata.subtitle = item.author;

            request.media.metadata = metadata;

            // Resolve request
            resolve(request);
          }
        });
      });
    });

下一部分将概述如何针对 DASH 内容配置 load 请求的 media 属性。

使用示例 API DASH 内容

现在,我们已准备好 load 拦截器,接下来要为接收器指定内容类型。此信息将为接收器提供主播放列表网址和媒体流 MIME 类型。将以下代码添加到 js/receiver.js 文件中 LOAD 拦截器的 Promise() 中:

...
playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
          ...
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.dash;
            request.media.contentType = 'application/dash+xml';
            ...
          }
        });
      });
    });

完成此步骤后,您可以继续“进行测试”部分,使用 DASH 内容尝试进行加载。如果您想使用 HLS 内容测试加载,请参考下一步。

使用示例 API HLS 内容

示例 API 包括 HLS 内容以及 DASH。除了像上一步中那样设置 contentType 之外,load 请求还需要有一些其他属性才能使用示例 API 的 HLS 网址。当接收器被配置为播放 HLS 媒体流时,默认的容器类型应为传输流 (TS)。因此,如果仅修改 contentUrl 属性,接收器将尝试以 TS 格式打开示例 MP4 媒体流。在 load 请求中,应当使用其他属性修改 MediaInformation 对象,使接收器知道内容的类型为 MP4 而非 TS。将以下代码添加到 js/RECEIVEr.js 文件中的 load 拦截器中,以修改 contentUrlcontentType 属性。此外,还要添加 HlsSegmentFormatHlsVideoSegmentFormat 属性。

...
playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      return new Promise((resolve, reject) => {
          ...
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.hls;
            request.media.contentType = 'application/x-mpegurl';
            request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
            request.media.hlsVideoSegmentFormat = cast.framework.messages.HlsVideoSegmentFormat.FMP4;
            ...
          }
        });
      });
    });

进行测试

再次打开命令与控制 (CaC) 工具,然后将您的应用 ID 设置为接收器的应用 ID。使用“Cast”按钮选择您的设备。

转到“Load Media”标签页。这次删除“Load by Content”按钮旁的“Content URL”字段中的文字,这会强制应用发送仅包含对媒体的 contentId 引用的 LOAD 请求。

3e830710c562189f.png

假设对接收器所做的修改一切正常,拦截器应当会负责将 MediaInfo 对象调整为 SDK 可在屏幕上播放的内容。

点击“Load by Content”按钮,看看媒体内容能否正常播放。您可以随意将“Content ID”更改为 content.json 文件中的其他 ID。

智能显示屏是具有触摸功能的设备,让接收器应用得以支持触摸式控件。

此部分将介绍如何在接收器应用在智能显示屏上启动时对其进行优化,以及如何自定义播放器控件。

访问界面控件

您可以使用 cast.framework.ui.Controls.GetInstance() 访问智能显示屏的界面控件对象。将以下代码添加到 js/receiver.js 文件中的 context.start() 上方:

...

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();

context.start();

如果您使用的不是 <cast-media-player> 元素,需要在 CastReceiverOptions 中设置 touchScreenOptimizedApp。在此 Codelab 中,我们使用的是 <cast-media-player> 元素。

context.start({ touchScreenOptimizedApp: true });

根据 MetadataTypeMediaStatus.supportedMediaCommands 将默认控件按钮分配到每个插槽。

视频控件

对于 MetadataType.MOVIEMetadataType.TV_SHOWMetadataType.GENERIC,智能显示屏的界面控件对象将如下例中的截图所示。

5f5a3d567813cf10.png

  1. --playback-logo-image
  2. MediaMetadata.subtitle
  3. MediaMetadata.title
  4. MediaStatus.currentTime
  5. MediaInformation.duration
  6. ControlsSlot.SLOT_SECONDARY_1ControlsButton.QUEUE_PREV
  7. ControlsSlot.SLOT_PRIMARY_1ControlsButton.SEEK_BACKWARD_30
  8. PLAY/PAUSE
  9. ControlsSlot.SLOT_PRIMARY_2ControlsButton.SEEK_FORWARD_30
  10. ControlsSlot.SLOT_SECONDARY_2ControlsButton.QUEUE_NEXT

音频控件

对于 MetadataType.MUSIC_TRACK,智能显示屏的界面控件对象将如下所示:

e93d4557f324e8c0.png

  1. --playback-logo-image
  2. MusicTrackMediaMetadata.albumName
  3. MusicTrackMediaMetadata.title
  4. MusicTrackMediaMetadata.albumArtist
  5. MusicTrackMediaMetadata.images[0]
  6. MediaStatus.currentTime
  7. MediaInformation.duration
  8. ControlsSlot.SLOT_SECONDARY_1ControlsButton.NO_BUTTON
  9. ControlsSlot.SLOT_PRIMARY_1ControlsButton.QUEUE_PREV
  10. PLAY/PAUSE
  11. ControlsSlot.SLOT_PRIMARY_2ControlsButton.QUEUE_NEXT
  12. ControlsSlot.SLOT_SECONDARY_2ControlsButton.NO_BUTTON

更新支持的媒体命令

界面控件对象还会根据 MediaStatus.supportedMediaCommands 决定是否显示 ControlsButton

supportedMediaCommands 的值等于 ALL_BASIC_MEDIA 时,默认控件布局将如下显示:

5b97ada8d239239c.png

supportedMediaCommands 的值等于 ALL_BASIC_MEDIA | QUEUE_PREV | QUEUE_NEXT 时,默认控件布局将如下显示:

2d8267d4d8b68e87.png

当 supportedMediaCommands 的值等于 PAUSE | QUEUE_PREV | QUEUE_NEXT 时,默认控件布局如下显示:

39c9afe44c4fa8dc.png

在有文字轨道时,字幕按钮将始终在 SLOT_1 显示。

771952ab29e2eb21.png

如需在启动接收器上下文后动态更改 supportedMediaCommands 的值,可以调用 PlayerManager.setSupportedMediaCommands 来替换该值。此外,您还可以使用 addSupportedMediaCommands 添加一条新命令或使用 removeSupportedMediaCommands 移除一条现有命令。

自定义控件按钮

您可以使用 PlayerDataBinder 自定义控件。将以下代码添加到 js/receiver.js 文件中的 touchControls 下方,以设置控件的第一个插槽:

...

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);

playerDataBinder.addEventListener(
  cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
  (e) => {
    if (!e.value) return;

    // Clear default buttons and re-assign
    touchControls.clearDefaultSlotAssignments();
    touchControls.assignButton(
      cast.framework.ui.ControlsSlot.SLOT_PRIMARY_1,
      cast.framework.ui.ControlsButton.SEEK_BACKWARD_30
    );
  });

context.start();

媒体浏览是一项 CAF 接收器功能,用户可借助该功能在触摸设备上浏览更多内容。为实现此功能,您需要使用 PlayerDataBinder 来设置 BrowseContent 界面。然后,您可以根据要显示的内容,以 BrowseItems 对其进行填充。

BrowseContent

以下是 BrowseContent 界面及其属性的一个示例:

e40623e1907d983a.png

  1. BrowseContent.title
  2. BrowseContent.items

宽高比

targetAspectRatio property 用于为图片资源选择最佳宽高比。CAF 接收器 SDK 支持三种宽高比:SQUARE_1_TO_1PORTRAIT_2_TO_3LANDSCAPE_16_TO_9

BrowseItem

BrowseItem 用于显示每项媒体内容的标题、字幕、时长和图片:

838a0db6c9224710.png

  1. BrowseItem.image
  2. BrowseItem.duration
  3. BrowseItem.title
  4. BrowseItem.subtitle

设置媒体浏览数据

您可以通过调用 setBrowseContent 提供可供浏览的媒体内容列表。将以下代码添加到 js/receiver.js 文件中的 playerDataBinder 下方和 MEDIA_CHANGED 事件监听器中,以设置标题为“Up Next”的浏览项。

// Optimizing for smart displays
const touchControls = cast.framework.ui.Controls.getInstance();
const playerData = new cast.framework.ui.PlayerData();
const playerDataBinder = new cast.framework.ui.PlayerDataBinder(playerData);

...

let browseItems = getBrowseItems();

function getBrowseItems() {
  let browseItems = [];
  makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json')
  .then(function (data) {
    for (let key in data) {
      let item = new cast.framework.ui.BrowseItem();
      item.entity = key;
      item.title = data[key].title;
      item.subtitle = data[key].description;
      item.image = new cast.framework.messages.Image(data[key].poster);
      item.imageType = cast.framework.ui.BrowseImageType.MOVIE;
      browseItems.push(item);
    }
  });
  return browseItems;
}

let browseContent = new cast.framework.ui.BrowseContent();
browseContent.title = 'Up Next';
browseContent.items = browseItems;
browseContent.targetAspectRatio = cast.framework.ui.BrowseImageAspectRatio.LANDSCAPE_16_TO_9;

playerDataBinder.addEventListener(
  cast.framework.ui.PlayerDataEventType.MEDIA_CHANGED,
  (e) => {
    if (!e.value) return;

    ....

    // Media browse
    touchControls.setBrowseContent(browseContent);
  });

点击媒体浏览项会触发 LOAD 拦截器。将以下代码添加到 LOAD 拦截器中,以将 request.media.contentId 映射到媒体浏览项中的 request.media.entity

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      ...

      // Map contentId to entity
      if (request.media && request.media.entity) {
        request.media.contentId = request.media.entity;
      }

      return new Promise((resolve, reject) => {
            ...
        });
    });

您还可以将 BrowseContent 对象设置为 null,以移除媒体浏览界面。

Cast 接收器 SDK 为开发者提供了另一种选择:他们可以使用 CastDebugLogger API 和配套的命令与控制 (CaC) 工具获取日志,从而轻松调试接收器应用。

初始化

如需添加 API,请在 index.html 文件中添加 CastDebugLogger 源脚本。应在 Cast 接收器 SDK 声明后的 <head> 标记中声明源。

<head>
  ...
  <script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
  <!-- Cast Debug Logger -->
  <script src="//www.gstatic.com/cast/sdk/libs/devtools/debug_layer/caf_receiver_logger.js"></script>
</head>

在文件顶部的 js/receiver.js 中和 playerManager 下方,添加以下代码来检索 CastDebugLogger 实例并启用日志记录器:

const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();

// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
const LOG_TAG = 'MyAPP.LOG';

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);

启用调试记录器后,接收器上会显示一个叠加式界面,其上显示 DEBUG MODE 字样。

d9cb99e7742fc240.png

记录播放器事件

使用 CastDebugLogger,您可以轻松记录由 CAF 接收器 SDK 触发的播放器事件,并使用不同的日志记录器级别来记录事件数据。loggerLevelByEvents 配置使用 cast.framework.events.EventTypecast.framework.events.category 来指定要记录的事件。

castDebugLogger 声明下添加以下代码,以便在触发了播放器 CORE 事件时或广播 mediaStatus 更改时进行记录:

// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);

// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
  'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
  'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
}

日志消息和自定义标记

借助 CastDebugLogger API,您可以创建不同颜色的日志消息,并将其显示于接收器的叠加式调试界面中。可以使用的日志记录方法如下(按优先级从高到低的顺序列出):

  • castDebugLogger.error(custom_tag, message);
  • castDebugLogger.warn(custom_tag, message);
  • castDebugLogger.info(custom_tag, message);
  • castDebugLogger.debug(custom_tag, message);

对于每种日志记录方法,第一个参数都是一个自定义标记。此标记可以是您认为有意义的任何具有标识作用的字符串。CastDebugLogger 使用标记来过滤日志。标记的用法将在后面进行更详细的介绍。第二个参数是日志消息

如需显示日志的实际运用情况,请将日志添加到 LOAD 拦截器中。

playerManager.setMessageInterceptor(
  cast.framework.messages.MessageType.LOAD,
  request => {
    castDebugLogger.info(LOG_TAG, 'Intercepting LOAD request');

    // Map contentId to entity
    if (request.media && request.media.entity) {
      request.media.contentId = request.media.entity;
    }

    return new Promise((resolve, reject) => {
      // Fetch content repository by requested contentId
      makeRequest('GET', 'https://storage.googleapis.com/cpe-sample-media/content.json')
        .then(function (data) {
          let item = data[request.media.contentId];
          if(!item) {
            // Content could not be found in repository
            castDebugLogger.error(LOG_TAG, 'Content not found');
            reject();
          } else {
            // Adjusting request to make requested content playable
            request.media.contentUrl = item.stream.dash;
            request.media.contentType = 'application/dash+xml';
            castDebugLogger.warn(LOG_TAG, 'Playable URL:', request.media.contentUrl);

            // Add metadata
            let metadata = new cast.framework.messages.MovieMediaMetadata();
            metadata.metadataType = cast.framework.messages.MetadataType.MOVIE;
            metadata.title = item.title;
            metadata.subtitle = item.author;

            request.media.metadata = metadata;

            // Resolve request
            resolve(request);
          }
      });
    });
  });

通过在 loggerLevelByTags 中为每个自定义标记设置日志级别,您可以控制叠加式调试界面上显示的消息。例如,启用日志级别为 cast.framework.LoggerLevel.DEBUG 的自定义标记将显示通过错误、警告、信息和调试日志消息添加的所有消息。启用级别为 WARNING 的自定义标记则仅显示错误和警告日志消息。

loggerLevelByTags 配置是可选的。如果没有为自定义标记配置日志记录器级别,那么所有日志消息都将显示于叠加式调试界面上。

CORE 事件记录器下添加以下代码:

// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
  'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
  'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
}

// Set verbosity level for custom tags.
castDebugLogger.loggerLevelByTags = {
    [LOG_TAG]: cast.framework.LoggerLevel.DEBUG,
};

叠加式调试界面

Cast 调试记录器在接收器上提供叠加式调试界面,用于在投射设备上显示您的自定义日志消息。使用 showDebugLogs 可切换叠加式调试界面,使用 clearDebugLogs 可清除叠加式界面上的日志消息。

添加以下代码,以预览接收器上的叠加式调试界面。

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);

// Show debug overlay
castDebugLogger.showDebugLogs(true);

// Clear log messages on debug overlay
// castDebugLogger.clearDebugLogs();

356c6ac94318539c.png

现在,您已经知道如何使用最新的 Cast 接收器 SDK 创建自定义接收器应用。如需查看更多示例应用,可以在 GitHub 上找到,其网址如下:github.com/googlecast