构建自定义 Web 接收器

1. 概览

Google Cast 徽标

此 Codelab 会教您如何构建自定义 Web 接收器应用以在支持 Cast 的设备上播放内容。

什么是 Google Cast?

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

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

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

构建目标

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

学习内容

  • 如何设置接收者开发。
  • 基于 Cast 应用框架且支持 Cast 的接收器的基础知识。
  • 如何接收投射的视频。
  • 如何集成调试日志记录器。
  • 如何优化智能显示屏的接收器。

所需条件

体验

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

您打算如何使用本教程?

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

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

新手水平 中等水平 熟练水平

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

新手水平 中等水平 熟练水平

2. 获取示例代码

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

然后解压下载的 zip 文件。

3. 在本地部署接收器

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

如果您没有可供使用的服务器,则可以使用 Firebase Hostingngrok

运行服务器

设置完您所选的服务后,请转到 app-start 并启动您的服务器。

记下托管接收器的网址。您将在下一部分中使用它。

4. 在 Cast 开发者控制台中注册应用

您必须注册您的应用,才能在 Chromecast 设备上运行自定义接收器(如此 Codelab 中所构建)。注册您的应用后,您会收到一个应用 ID,发送者的应用必须使用该 ID 来执行 API 调用,例如启动接收器应用。

Google Cast SDK Console 的图片,其中突出显示了“Add New Application”(添加新应用)按钮

点击“添加新应用”

“New Receiver Application”(新建接收器应用)屏幕的图片,其中突出显示了“Custom Receiver”(自定义接收器)选项

选择“Custom Receiver”(自定义接收器),这就是我们构建的功能。

“新建自定义接收器”屏幕的图片,其中显示用户在“接收器应用网址”字段中输入的网址

输入新接收器的详细信息,请务必使用您最终选择的网址

(在上一部分中)。记下分配给全新接收器的应用 ID

此外,您必须注册 Google Cast 设备,才能让该设备访问您的接收器应用,然后再发布该应用。您发布接收器应用后,便可在所有 Google Cast 设备上使用。在此 Codelab 中,建议使用未发布的接收器应用。

Google Cast SDK Console 的图片,其中突出显示了“Add New Device”按钮

点击“添加新设备”

“Add Cast Receiver Device”对话框的图片

输入印在设备背面的序列号,并为其指定一个描述性名称。访问 Google Cast SDK 管理中心时,您也可在 Chrome 中投射屏幕,从而找到您的序列号

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

5. 运行示例应用

Google Chrome 徽标

在等待新接收器应用可供测试期间,让我们看看已完成的示例接收器应用是什么样的。我们要构建的接收器能够播放采用自适应比特率流式传输的媒体内容(我们将使用通过 HTTP 进行动态自适应流式传输 (DASH) 编码的示例内容)。

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

命令和控制 (CaC) 工具的“Cast Connect 和日志记录器控件”标签页的图片

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

命令和控制 (CaC) 工具的“Cast Connect 和 Logger 控件”标签页的图片,指示它已连接到接收器应用

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

命令和控制 (CaC) 工具的“加载媒体”标签页的图片

  1. 点击“按内容加载”按钮可播放示例视频。
  2. 视频将开始在 Google Cast 设备上播放,以显示使用默认接收器时的基本接收器功能。

6. 准备起始项目

我们需要在您下载的入门级应用中添加 Google Cast 支持。以下是我们将在此 Codelab 中使用的一些 Google Cast 术语:

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

现在,您可以使用自己喜爱的文本编辑器基于入门级项目进行构建了:

  1. 从下载的示例代码中选择 文件夹图标app-start 目录。
  2. 打开 js/receiver.jsindex.html

请注意,在学习此 Codelab 的过程中,http-server 应该会捕捉您的更改。如果发现未启动,请尝试终止并重启 http-server

应用设计

接收器应用会初始化投放会话,并等待接收方发送 LOAD 请求(即用于播放一部分媒体的命令)。

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

index.html

此 HTML 文件将包含接收方应用的界面。目前它是空的,我们将在 Codelab 中将其添加到其中。

接收器.js

此脚本将管理接收器应用的所有逻辑。目前,此文件只是一个空文件,但在下一节中,我们只需编写几行代码,就能将其转换为功能齐全的 Cast 接收器。

7. 基本的 Cast 接收器

基本的 Cast 接收器会在启动时初始化投放会话。只有这样,才能让所有连接发送者的应用启动接收器。此外,新的 SDK 也预先经过配置,可自动处理自适应码率流媒体(使用 DASH、HLS 和流畅的流式传输),以及普通 MP4 文件。我们来试试看。

初始化

将以下代码添加到标头中的 index.html

<head>
  ...

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

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

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

现在,我们需要在 js/receiver.js 中初始化 SDK,其中包含:

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

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

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

context.start();

8. 投放“基本”视频内容

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

将网络浏览器指向命令和控制 (CaC) 工具

命令和控制 (CaC) 工具的“Cast Connect 和日志记录器控件”标签页的图片

请务必将您之前的应用 ID 替换为此字段中之前注册的应用 ID,然后点击“设置应用 ID”。这会指示该工具在启动投放会话时使用接收器。

投射媒体

概括来讲,如需在投放设备上播放媒体内容,需要满足以下条件:

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

在第一个基本尝试中,我们将为 MediaInfo 填充一个可播放的素材资源网址(存储在 MediaInfo.contentUrl 中)。

真实的发送者在 MediaInfo.contentId 中使用应用特定的媒体标识符。接收器使用 contentId 作为标识符来进行适当的后端 API 调用以解析实际的素材资源网址并将其设置为 MediaInfo.contentUrl.。接收器还将处理 DRM 许可获取或广告插播时间点信息等任务。

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

命令和控制 (CaC) 工具的“Cast Connect 和 Logger 控件”标签页的图片,指示它已连接到接收器应用

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

命令和控制 (CaC) 工具的“加载媒体”标签页的图片

因此,接收器 SDK 可直接使用:

  • 初始化投放会话
  • 处理来自包含可播放资源的发送者的 LOAD 请求
  • 提供可以显示在大屏幕上的基本播放器界面。

在转到下一部分之前,您可以随意探索 CaC 工具及其代码。在下一部分,我们将扩展接收器以与简单的示例 API 通信,以满足来自发件人的 LOAD 请求。

9. 与外部 API 集成

为与大多数开发者在真实应用中与 Cast 接收器交互的方式保持一致,我们将修改接收器以处理 LOAD 请求(通过 API 密钥引用目标媒体内容),而不是通过可播放的素材资源网址发送请求。

应用这样做的原因如下:

  • 发件人可能不知道内容网址。
  • 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 流会指向存储在碎片化 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 请求

在此步骤中,我们将创建一个加载拦截器,其中包含一个函数,用于向托管的 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 内容配置加载请求的 media 属性。

使用示例 API DASH 内容

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

...
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 之外,加载请求还需要一些其他属性才能使用示例 API 的 HLS 网址。当接收器配置为播放 HLS 流时,默认的默认容器类型为传输流 (TS)。因此,如果只修改 contentUrl 属性,接收器将尝试以 TS 格式打开示例 MP4 流。在加载请求中,应使用其他属性修改 MediaInformation 对象,以便接收器知道内容的类型为 MP4 而非 TS。在加载拦截器中将以下代码添加到 js/receiver.js 文件以修改 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。使用“投放”按钮选择您的设备。

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

命令和控制 (CaC) 工具的“加载媒体”标签页的图片

假设一切都符合接收器的修改,拦截器应该负责将 MediaInfo 对象调整为 SDK 可以在屏幕上播放的内容。

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

10. 针对智能显示屏进行优化

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

本部分介绍如何在智能显示屏上启动接收设备应用,以及如何自定义播放器控件。

访问界面控件

您可以使用 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,智能显示屏的界面控件对象会如下例所示。

播放视频的界面控件叠加在顶部图片

  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,智能显示屏的界面控件对象将如下所示:

音乐界面,界面控件叠加在顶部

  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 时,默认控件布局将显示如下:

媒体播放器控件的图片:已启用进度条、“播放”按钮、“快进”和“快退”按钮已启用

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

媒体播放器控件的图片:进度条、“播放”按钮、“快进”和“向后跳转”按钮,以及“将上一队列加入队列”和“将下一队列添加到队列”按钮

当支持的 MediaCommands 的值等于 PAUSE | QUEUE_PREV | QUEUE_NEXT 时,默认控件布局将如下所示:

媒体播放器控件的图片:进度条、“播放”按钮以及“加入队列”和“加入队列”按钮

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

媒体播放器控件的图片:进度条、“播放”按钮、“快进”和“快退”按钮、“在上一队列中加入”和“接下来播放”按钮,以及已启用“字幕”按钮

如需在启动接收器上下文后动态更改 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();

11. 在智能显示屏上实现媒体浏览

媒体浏览是一项 CAF 接收器功能,可让用户在触摸设备上浏览更多内容。为了实现这一点,您将使用 PlayerDataBinder 来设置 BrowseContent 界面。然后,您可以根据想要显示的内容填充 BrowseItems

浏览内容

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

显示两张视频缩略图和三分之一的部分的浏览内容界面的图片

  1. BrowseContent.title
  2. BrowseContent.items

宽高比

您可以使用 targetAspectRatio property 为图片素材资源选择最佳宽高比。CAF 接收器 SDK 支持三种宽高比:SQUARE_1_TO_1PORTRAIT_2_TO_3LANDSCAPE_16_TO_9

浏览项目

使用 BrowseItem 显示每项内容的标题、副标题、持续时间和图片:

显示两张视频缩略图和三分之一的部分的浏览内容界面的图片

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

设置媒体浏览数据

您可以通过调用 setBrowseContent 提供要浏览的媒体内容列表。将以下代码添加到 playerDataBinder 文件中的 js/receiver.js 文件中以及 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,以移除媒体浏览界面。

12. 调试接收器应用

Cast Receiver SDK 提供了另一个选项,供开发者使用 CastDebugLogger API 和配套的 Command and Control (CaC) Tool 捕获日志,轻松调试接收器应用。

初始化

如需添加 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.
context.addEventListener(cast.framework.system.EventType.READY, () => {
  if (!castDebugLogger.debugOverlayElement_) {
      castDebugLogger.setEnabled(true);
  }
});

启用调试日志记录器后,接收器上会显示叠加的 DEBUG MODE

播放视频时显示的画面,在框架左上方以红色背景显示了“DEBUG MODE”

记录播放器事件

您可以使用 CastDebugLogger 轻松记录由 CAF Receiver 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.
context.addEventListener(cast.framework.system.EventType.READY, () => {
  if (!castDebugLogger.debugOverlayElement_) {
      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 可清除叠加层上的日志消息。

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

context.addEventListener(cast.framework.system.EventType.READY, () => {
  if (!castDebugLogger.debugOverlayElement_) {
      // 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();
  }
});

此图片显示了调试叠加层,即调试日志消息列表(在视频帧顶部的半透明背景中显示)

13. 恭喜

现在,您已了解如何使用 Cast Web Receiver SDK 创建自定义 Web 接收器应用。

如需了解详情,请参阅网络接收器开发者指南。