向自定义网络接收器添加核心功能

本页包含自定义 Web 接收器应用可用的功能的代码段和说明。

  1. 一个 cast-media-player 元素,表示随 Web 接收器提供的内置播放器界面。
  2. cast-media-player 元素提供类似 CSS 的自定义样式,以便为各种界面元素(例如 background-imagesplash-imagefont-family)设置样式。
  3. 用于加载 Web 接收器框架的脚本元素。
  4. 用于拦截消息和处理事件的 JavaScript 代码。
  5. 加入自动播放队列。
  6. 用于配置播放的选项。
  7. 用于设置 Web 接收器上下文的选项。
  8. 用于设置 Web 接收器应用支持的命令的选项。
  9. 用于启动 Web 接收器应用的 JavaScript 调用。

应用配置和选项

配置应用

CastReceiverContext 是向开发者公开的最外层类,它负责管理底层库的加载,并处理 Web 接收器 SDK 的初始化。该 SDK 提供了一些 API,可让应用开发者通过 CastReceiverOptions 配置 SDK。这些配置会在每次应用启动时评估一次,并在调用 start 时将可选参数传递给 SDK。

以下示例展示了如何替换用于检测发送方连接是否仍处于有效连接状态的默认行为。如果 Web 接收器在 maxInactivity 秒内无法与发送方通信,系统会分派 SENDER_DISCONNECTED 事件。以下配置会替换此超时设置。这在调试问题时非常有用,因为它可以防止在处于 IDLE 状态的连接发送器为零时,Web Receiver 应用关闭 Chrome 远程调试程序会话。

const context = cast.framework.CastReceiverContext.getInstance();
const options = new cast.framework.CastReceiverOptions();
options.maxInactivity = 3600; // Development only
context.start(options);

配置播放器

在加载内容时,Web Receiver SDK 提供了一种使用 cast.framework.PlaybackConfig 配置播放变量(例如 DRM 信息、重试配置和请求处理程序)的方法。此信息由 PlayerManager 处理,并在创建玩家时进行评估。每次将新加载内容传递给 Web 接收器 SDK 时,系统都会创建播放器。在创建播放器后对 PlaybackConfig 所做的修改将在下次加载内容时进行评估。该 SDK 提供了以下用于修改 PlaybackConfig 的方法。

以下示例展示了如何在初始化 CastReceiverContext 时设置 PlaybackConfig。该配置会替换用于获取清单的出站请求。该处理脚本指定应使用 Cookie 或授权标头等凭据发出 CORS 访问控制请求。

const playbackConfig = new cast.framework.PlaybackConfig();
playbackConfig.manifestRequestHandler = requestInfo => {
  requestInfo.withCredentials = true;
};
context.start({playbackConfig: playbackConfig});

以下示例展示了如何使用 PlayerManager 中提供的 getter 和 setter 替换 PlaybackConfig。此设置会将播放器配置为在加载 1 个片段后恢复内容播放。

const playerManager =
    cast.framework.CastReceiverContext.getInstance().getPlayerManager();
const playbackConfig = (Object.assign(
            new cast.framework.PlaybackConfig(), playerManager.getPlaybackConfig()));
playbackConfig.autoResumeNumberOfSegments = 1;
playerManager.setPlaybackConfig(playbackConfig);

以下示例展示了如何使用媒体播放信息处理程序替换特定加载请求的 PlaybackConfig。该处理程序会调用应用实现的方法 getLicenseUrlForMedia,以从当前项的 contentId 获取 licenseUrl

playerManager.setMediaPlaybackInfoHandler((loadRequestData, playbackConfig) => {
  const mediaInformation = loadRequestData.media;
  playbackConfig.licenseUrl = getLicenseUrlForMedia(mediaInformation.contentId);

  return playbackConfig;
});

事件监听器

借助 Web Receiver SDK,您的 Web Receiver 应用可以处理播放器事件。事件监听器采用 cast.framework.events.EventType 参数(或这些参数的数组),用于指定应触发监听器的事件。您可以在 cast.framework.events.category 中找到对调试有用的预配置 cast.framework.events.EventType 数组。事件参数可提供有关事件的其他信息。

例如,如果您想知道何时广播 mediaStatus 更改,可以使用以下逻辑来处理事件:

const playerManager =
    cast.framework.CastReceiverContext.getInstance().getPlayerManager();
playerManager.addEventListener(
    cast.framework.events.EventType.MEDIA_STATUS, (event) => {
      // Write your own event handling code, for example
      // using the event.mediaStatus value
});

消息拦截

借助 Web Receiver SDK,您的 Web Receiver 应用可以拦截消息并对这些消息执行自定义代码。消息拦截器接受 cast.framework.messages.MessageType 参数,该参数用于指定应拦截哪类消息。

拦截器应返回经过修改的请求,或一个使用经过修改的请求值解析的 Promise。返回 null 将阻止调用默认消息处理程序。如需了解详情,请参阅加载媒体

例如,如果您想更改加载请求数据,可以使用以下逻辑来拦截和修改它:

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

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, loadRequestData => {
      const error = new cast.framework.messages.ErrorData(
                      cast.framework.messages.ErrorType.LOAD_FAILED);
      if (!loadRequestData.media) {
        error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
        return error;
      }

      if (!loadRequestData.media.entity) {
        return loadRequestData;
      }

      return thirdparty.fetchAssetAndAuth(loadRequestData.media.entity,
                                          loadRequestData.credentials)
        .then(asset => {
          if (!asset) {
            throw cast.framework.messages.ErrorReason.INVALID_REQUEST;
          }

          loadRequestData.media.contentUrl = asset.url;
          loadRequestData.media.metadata = asset.metadata;
          loadRequestData.media.tracks = asset.tracks;
          return loadRequestData;
        }).catch(reason => {
          error.reason = reason; // cast.framework.messages.ErrorReason
          return error;
        });
    });

context.start();

错误处理

当消息拦截器中发生错误时,您的 Web 接收器应用应返回适当的 cast.framework.messages.ErrorTypecast.framework.messages.ErrorReason

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, loadRequestData => {
      const error = new cast.framework.messages.ErrorData(
                      cast.framework.messages.ErrorType.LOAD_CANCELLED);
      if (!loadRequestData.media) {
        error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
        return error;
      }

      ...

      return fetchAssetAndAuth(loadRequestData.media.entity,
                               loadRequestData.credentials)
        .then(asset => {
          ...
          return loadRequestData;
        }).catch(reason => {
          error.reason = reason; // cast.framework.messages.ErrorReason
          return error;
        });
    });

消息拦截与事件监听器

消息拦截和事件监听器之间的一些主要区别如下:

  • 您无法使用事件监听器修改请求数据。
  • 事件监听器最适合用于触发 Google Analytics 或自定义函数。
playerManager.addEventListener(cast.framework.events.category.CORE,
    event => {
        console.log(event);
    });
  • 借助消息拦截功能,您可以监听消息、拦截消息以及修改请求数据本身。
  • 消息拦截最适合用于处理与请求数据相关的自定义逻辑。

加载媒体

MediaInformation 提供了许多属性,用于在 cast.framework.messages.MessageType.LOAD 消息中加载媒体,包括 entitycontentUrlcontentId

  • 建议您在发送方应用和接收方应用的实现中使用 entity 属性。此属性是一个深层链接网址,可以是播放列表或媒体内容。您的应用应解析此网址,并填充其他两个字段中的至少一个。
  • contentUrl 对应于播放器将用于加载内容的可播放网址。例如,此网址可以指向 DASH 清单。
  • contentId 可以是可播放的内容网址(类似于 contentUrl 属性的网址),也可以是正在加载的内容或播放列表的唯一标识符。如果将此属性用作标识符,您的应用应在 contentUrl 中填充可播放的网址。

建议使用 entity 存储真实 ID 或键参数,并使用 contentUrl 存储媒体的网址。以下代码段展示了此示例,其中 entity 存在于 LOAD 请求中,并且检索了可播放的 contentUrl

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

      if (!loadRequestData.media.entity) {
        // Copy the value from contentId for legacy reasons if needed
        loadRequestData.media.entity = loadRequestData.media.contentId;
      }

      return thirdparty.fetchAssetAndAuth(loadRequestData.media.entity,
                                          loadRequestData.credentials)
        .then(asset => {
          loadRequestData.media.contentUrl = asset.url;
          ...
          return loadRequestData;
        });
    });

设备功能

getDeviceCapabilities 方法可提供已连接的 Cast 设备以及连接到该设备的视频或音频设备的设备信息。getDeviceCapabilities 方法可提供 Google 助理、蓝牙以及已连接的显示屏和音频设备的支持信息。

此方法会返回一个对象,您可以通过传入指定枚举之一来查询该对象,以获取该枚举的设备功能。枚举在 cast.framework.system.DeviceCapabilities 中定义。

此示例用于检查 Web 接收器设备是否能够分别使用 IS_HDR_SUPPORTEDIS_DV_SUPPORTED 按键播放 HDR 和杜比视界 (DV) 内容。

const context = cast.framework.CastReceiverContext.getInstance();
context.addEventListener(cast.framework.system.EventType.READY, () => {
  const deviceCapabilities = context.getDeviceCapabilities();
  if (deviceCapabilities &&
      deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_HDR_SUPPORTED]) {
    // Write your own event handling code, for example
    // using the deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_HDR_SUPPORTED] value
  }
  if (deviceCapabilities &&
      deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_DV_SUPPORTED]) {
    // Write your own event handling code, for example
    // using the deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_DV_SUPPORTED] value
  }
});
context.start();

处理用户互动

用户可以通过发送器应用(Web、Android 和 iOS)、内置 Google 助理的设备上的语音指令、智能显示屏上的触控功能以及 Android TV 设备上的遥控器与您的 Web 接收器应用互动。Cast SDK 提供了各种 API,以便 Web Receiver 应用处理这些互动、通过用户操作状态更新应用界面,并可选择发送更改以更新任何后端服务。

支持的媒体命令

对于 iOS 和 Android 发送器展开式控制器、在触摸设备上运行的接收器和遥控器应用,以及 Android TV 设备上的接收器应用,界面控件状态由 MediaStatus.supportedMediaCommands 驱动。在该属性中启用特定的按位 Command 后,系统会启用与该操作相关的按钮。如果未设置该值,则按钮将处于停用状态。您可以在 Web 接收器上通过以下方式更改这些值:

  1. 使用 PlayerManager.setSupportedMediaCommands 设置特定的 Commands
  2. 使用 addSupportedMediaCommands 添加新命令
  3. 使用 removeSupportedMediaCommands 移除现有命令。
playerManager.setSupportedMediaCommands(cast.framework.messages.Command.SEEK |
  cast.framework.messages.Command.PAUSE);

接收器准备更新后的 MediaStatus 时,会在 supportedMediaCommands 属性中包含更改。广播状态后,关联的发送方应用会相应地更新其界面中的按钮。

如需详细了解支持的媒体指令和触摸设备,请参阅 Accessing UI controls 指南。

管理用户操作状态

当用户与界面互动或发送语音指令时,可以控制内容的播放以及与正在播放的内容相关的属性。控制播放的请求由 SDK 自动处理。用于修改当前正在播放内容的属性的请求(例如 LIKE 命令)需要接收器应用进行处理。SDK 提供了一系列 API 来处理此类请求。如需支持这些请求,必须执行以下操作:

  • 在加载媒体内容时,使用用户的偏好设置设置 MediaInformation userActionStates
  • 拦截 USER_ACTION 消息并确定请求的操作。
  • 更新 MediaInformation UserActionState 以更新界面。

以下代码段会拦截 LOAD 请求并填充 LoadRequestDataMediaInformation。在本例中,用户喜欢正在加载的内容。

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD, (loadRequestData) => {
      const userActionLike = new cast.framework.messages.UserActionState(
          cast.framework.messages.UserAction.LIKE);
      loadRequestData.media.userActionStates = [userActionLike];

      return loadRequestData;
    });

以下代码段会拦截 USER_ACTION 消息,并处理使用请求的更改调用后端。然后,它会进行调用以更新接收器上的 UserActionState

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.USER_ACTION,
  (userActionRequestData) => {
    // Obtain the media information of the current content to associate the action to.
    let mediaInfo = playerManager.getMediaInformation();

    // If there is no media info return an error and ignore the request.
    if (!mediaInfo) {
        console.error('Not playing media, user action is not supported');
        return new cast.framework.messages.ErrorData(messages.ErrorType.BAD_REQUEST);
    }

    // Reach out to backend services to store user action modifications. See sample below.
    return sendUserAction(userActionRequestData, mediaInfo)

    // Upon response from the backend, update the client's UserActionState.
    .then(backendResponse => updateUserActionStates(backendResponse))

    // If any errors occurred in the backend return them to the cast receiver.
    .catch((error) => {
      console.error(error);
      return error;
    });
});

以下代码段模拟了对后端服务的调用。该函数会检查 UserActionRequestData 以查看用户请求的更改类型,并且仅在后端支持相应操作时才会进行网络调用。

function sendUserAction(userActionRequestData, mediaInfo) {
  return new Promise((resolve, reject) => {
    switch (userActionRequestData.userAction) {
      // Handle user action changes supported by the backend.
      case cast.framework.messages.UserAction.LIKE:
      case cast.framework.messages.UserAction.DISLIKE:
      case cast.framework.messages.UserAction.FOLLOW:
      case cast.framework.messages.UserAction.UNFOLLOW:
      case cast.framework.messages.UserAction.FLAG:
      case cast.framework.messages.UserAction.SKIP_AD:
        let backendResponse = {userActionRequestData: userActionRequestData, mediaInfo: mediaInfo};
        setTimeout(() => {resolve(backendResponse)}, 1000);
        break;
      // Reject all other user action changes.
      default:
        reject(
          new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType.INVALID_REQUEST));
    }
  });
}

以下代码段会获取 UserActionRequestData,然后从 MediaInformation 中添加或移除 UserActionState。更新 MediaInformationUserActionState 会更改与请求的操作关联的按钮的状态。此更改反映在智能显示屏控件界面、遥控器应用和 Android TV 界面中。它还会通过传出 MediaStatus 消息广播,以更新 iOS 和 Android 发送器的展开式控制器的界面。

function updateUserActionStates(backendResponse) {
  // Unwrap the backend response.
  let mediaInfo = backendResponse.mediaInfo;
  let userActionRequestData = backendResponse.userActionRequestData;

  // If the current item playing has changed, don't update the UserActionState for the current item.
  if (playerManager.getMediaInformation().entity !== mediaInfo.entity) {
    return;
  }

  // Check for existing userActionStates in the MediaInformation.
  // If none, initialize a new array to populate states with.
  let userActionStates = mediaInfo.userActionStates || [];

  // Locate the index of the UserActionState that will be updated in the userActionStates array.
  let index = userActionStates.findIndex((currUserActionState) => {
    return currUserActionState.userAction == userActionRequestData.userAction;
  });

  if (userActionRequestData.clear) {
    // Remove the user action state from the array if cleared.
    if (index >= 0) {
      userActionStates.splice(index, 1);
    }
    else {
      console.warn("Could not find UserActionState to remove in MediaInformation");
    }
  } else {
    // Add the UserActionState to the array if enabled.
    userActionStates.push(
      new cast.framework.messages.UserActionState(userActionRequestData.userAction));
  }

  // Update the UserActionState array and set the new MediaInformation
  mediaInfo.userActionStates = userActionStates;
  playerManager.setMediaInformation(mediaInfo, true);
  return;
}

语音指令

适用于内置 Google 助理的设备的 Web Receiver SDK 目前支持以下媒体指令。这些命令的默认实现位于 cast.framework.PlayerManager 中。

命令 说明
Play 从暂停状态播放或继续播放。
暂停 暂停当前正在播放的内容。
上一步 跳转到媒体队列中的上一项媒体内容。
前进 跳转到媒体队列中的下一个媒体内容。
停止 停止当前正在播放的媒体。
不重复播放 在队列中的最后一项内容播放完毕后,停用队列中媒体项的重复播放。
重复单次 无限期重复当前正在播放的媒体。
全部重复 播放队列中的最后一项内容后,重复播放队列中的所有内容。
全部重复和随机播放 队列中的最后一项内容播放完毕后,随机播放队列中的所有内容。
随机 随机播放媒体队列中的媒体内容。
开启 / 关闭字幕 为媒体启用 / 停用字幕。您还可以按语言启用 / 停用。
跳转绝对时间 跳转到指定的绝对时间。
跳转到相对于当前时间的时间 相对于当前播放时间,向前或向后跳转指定的时间段。
重玩 重新开始播放当前正在播放的媒体,或者如果当前没有任何内容在播放,则播放上次播放的媒体内容。
设置播放速度 改变媒体播放速度。默认情况下,系统应该会处理此问题。您可以使用 SET_PLAYBACK_RATE 消息拦截器替换传入的费率请求。

支持的语音媒体指令

如需防止语音指令在内置 Google 助理的设备上触发媒体指令,您必须先设置您计划支持的受支持的媒体指令。然后,您必须启用 CastReceiverOptions.enforceSupportedCommands 属性,以强制执行这些命令。Cast SDK 发送器和支持触控的设备上的界面将发生变化,以反映这些配置。如果未启用该标志,系统会执行传入的语音指令。

例如,如果您允许发送方应用和支持触控的设备使用 PAUSE,则还必须配置接收器以反映这些设置。配置后,如果传入的语音指令不在支持的指令列表中,系统会将其舍弃。

在以下示例中,我们在启动 CastReceiverContext 时提供 CastReceiverOptions。我们添加了对 PAUSE 命令的支持,并强制要求玩家仅支持该命令。现在,如果语音指令请求执行其他操作(例如 SEEK),系统会拒绝该请求。系统会通知用户该命令尚不受支持。

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

context.start({
  enforceSupportedCommands: true,
  supportedCommands: cast.framework.messages.Command.PAUSE
});

您可以为要限制的每条命令应用单独的逻辑。移除 enforceSupportedCommands 标志,然后针对您要限制的每个命令,您都可以拦截传入的消息。在这里,我们会拦截 SDK 提供的请求,以便向内置 Google 助理的设备发出的 SEEK 命令不会在 Web Receiver 应用中触发跳转。

对于您的应用不支持的媒体指令,请返回适当的错误原因,例如 NOT_SUPPORTED

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.SEEK,
  seekData => {
    // Block seeking if the SEEK supported media command is disabled
    if (!(playerManager.getSupportedMediaCommands() & cast.framework.messages.Command.SEEK)) {
      let e = new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType
      .INVALID_REQUEST);
      e.reason = cast.framework.messages.ErrorReason.NOT_SUPPORTED;
      return e;
    }

    return seekData;
  });

从语音活动进入后台

如果 Cast 平台因 Google 助理活动(例如监听用户语音或回复用户)而将应用的声音置于后台,则会在 activity 启动时向 Web 接收器应用发送 NOT_IN_FOCUSFocusState 消息。当 activity 结束时,系统会再发送一条包含 IN_FOCUS 的消息。根据您的应用和正在播放的媒体,您可能需要在 FocusStateNOT_IN_FOCUS 时通过拦截消息类型 FOCUS_STATE 来暂停媒体。

例如,如果 Google 助理正在响应用户询问,则应暂停有声读物播放,以提供良好的用户体验。

playerManager.setMessageInterceptor(cast.framework.messages.MessageType.FOCUS_STATE,
  focusStateRequestData => {
    // Pause content when the app is out of focus. Resume when focus is restored.
    if (focusStateRequestData.state == cast.framework.messages.FocusState.NOT_IN_FOCUS) {
      playerManager.pause();
    } else {
      playerManager.play();
    }

    return focusStateRequestData;
  });

语音指定的字幕语言

如果用户未明确说明字幕的语言,则字幕使用的语言与用户下达命令时所用的语言相同。在这些情况下,传入消息的 isSuggestedLanguage 参数表示关联的语言是系统建议的还是用户明确请求的。

例如,对于“Ok Google,开启字幕”命令,isSuggestedLanguage 会设为 true,因为系统会根据命令的口语语言推断出语言。如果明确请求语言(例如“Ok Google,开启英语字幕”),isSuggestedLanguage 会设为 false

元数据和语音投放

虽然默认情况下语音指令由 Web Receiver 处理,但您应确保内容的元数据完整且准确。这样可确保 Google 助理能够正确处理语音指令,并且元数据能够在新类型的界面(例如 Google Home 应用)和智能显示屏(例如 Google Home Hub)中正确显示。

流式传输

保留会话状态是流式传输的基础,用户可以使用语音指令、Google Home 应用或智能显示屏在设备之间移动现有音频和视频流。媒体在一部设备(来源)上停止播放,并在另一部设备(目标设备)上继续播放。任何搭载最新固件的 Cast 设备都可以作为流式传输的源或目标。

串流传输的事件流如下所示:

  1. 在来源设备上:
    1. 媒体停止播放。
    2. Web 接收器应用会收到用于保存当前媒体状态的命令。
    3. Web 接收器应用关闭。
  2. 在目标设备上:
    1. 加载 Web Receiver 应用。
    2. Web 接收器应用会收到用于恢复已保存媒体状态的命令。
    3. 媒体继续播放。

媒体状态的元素包括:

  • 歌曲、视频或媒体内容的特定位置或时间戳。
  • 在更大队列(例如歌单或音乐人电台)中的位置。
  • 经过身份验证的用户。
  • 播放状态(例如,正在播放或暂停)。

启用数据流传输

如需为 Web 接收器实现数据流传输,请执行以下操作:

  1. 使用 STREAM_TRANSFER 命令更新 supportedMediaCommands
    playerManager.addSupportedMediaCommands(
    cast.framework.messages.Command.STREAM_TRANSFER, true);
  2. (可选)替换 SESSION_STATERESUME_SESSION 消息拦截器,如保留会话状态中所述。仅当需要将自定义数据存储为会话快照的一部分时,才替换这些方法。否则,用于保留会话状态的默认实现将支持流传输。

保留会话状态

Web Receiver SDK 为 Web Receiver 应用提供了默认实现,该实现通过拍摄当前媒体状态的快照、将状态转换为加载请求,并使用加载请求恢复会话来保留会话状态。

如有必要,您可以在 SESSION_STATE 消息拦截器中替换 Web 接收器生成的加载请求。如果您想将自定义数据添加到加载请求中,建议将其放入 loadRequestData.customData 中。

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.SESSION_STATE,
    function (sessionState) {
        // Override sessionState.loadRequestData if needed.
        const newCredentials = updateCredentials_(sessionState.loadRequestData.credentials);
        sessionState.loadRequestData.credentials = newCredentials;

        // Add custom data if needed.
        sessionState.loadRequestData.customData = {
            'membership': 'PREMIUM'
        };

        return sessionState;
    });

您可以从 RESUME_SESSION 消息拦截器中的 loadRequestData.customData 检索自定义数据。

let cred_ = null;
let membership_ = null;

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.RESUME_SESSION,
    function (resumeSessionRequest) {
        let sessionState = resumeSessionRequest.sessionState;

        // Modify sessionState.loadRequestData if needed.
        cred_ = sessionState.loadRequestData.credentials;

        // Retrieve custom data.
        membership_ = sessionState.loadRequestData.customData.membership;

        return resumeSessionRequest;
    });

内容预加载

Web 接收器支持在队列中的当前播放内容后预加载媒体内容。

预加载操作会预先下载即将播放内容的多个片段。该规范基于 QueueItem 对象中的 preloadTime 值(如果未提供,则默认为 20 秒)进行指定。时间以秒为单位,相对于当前正在播放的项的结束时间。仅正值有效。例如,如果此值为 10 秒,则系统会在前一项播放完毕前 10 秒预加载此项。如果预加载时间大于 currentItem 剩余时间,则系统会尽快进行预加载。因此,如果在 queueItem 上指定了非常大的预加载值,则可以实现以下效果:每当我们播放当前项时,就已经预加载了下一项。不过,我们将此设置和选择权交给开发者,因为此值可能会影响当前播放内容的带宽和在线播放性能。

默认情况下,预加载适用于 HLS、DASH 和流畅串流内容。

系统不会预加载常规 MP4 视频和音频文件(例如 MP3),因为 Cast 设备仅支持一个媒体元素,并且在现有内容项仍在播放时无法用于预加载。

自定义消息

消息交换是 Web 接收器应用的主要互动方式。

发件人使用发件人所运行平台(Android、iOS、网站)的适用发件人 API 向 Web 接收器发出消息。传递给事件监听器的事件对象(即消息的表现形式)包含一个数据元素 (event.data),其中的数据会采用特定事件类型的属性。

Web Receiver 应用可以选择监听指定命名空间中的消息。这样一来,Web Receiver 应用就被视为支持该命名空间协议。然后,希望在该命名空间中进行通信的任何已连接发件人都可以使用适当的协议。

所有命名空间都由字符串定义,并且必须以“urn:x-cast:”开头,后跟任何字符串。例如“urn:x-cast:com.example.cast.mynamespace”。

以下是 Web 接收器用于监听来自关联发件人的自定义消息的代码段:

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

const CUSTOM_CHANNEL = 'urn:x-cast:com.example.cast.mynamespace';
context.addCustomMessageListener(CUSTOM_CHANNEL, function(customEvent) {
  // handle customEvent.
});

context.start();

同样,Web Receiver 应用可以向已连接的发件人发送消息,让发件人了解 Web Receiver 的状态。Web 接收器应用可以在 CastReceiverContext 上使用 sendCustomMessage(namespace, senderId, message) 发送消息。Web 接收器可以向单个发件人发送消息,以响应收到的消息或因应用状态发生变化而发送消息。除了点对点消息传递(限制为 64kb)之外,Web 接收器还可以向所有已连接的发件人广播消息。

投放到音频设备

如需了解对仅音频播放的支持,请参阅 Google Cast 音频设备指南

Android TV

本部分介绍了 Google Web Receiver 如何使用您的输入内容进行播放,以及 Android TV 兼容性。

将应用与遥控器集成

在 Android TV 设备上运行的 Google Web Receiver 会将来自设备控制输入(例如手持遥控器)的输入转换为为 urn:x-cast:com.google.cast.media 命名空间定义的媒体播放消息,如媒体播放消息中所述。您的应用必须支持这些消息才能控制应用媒体播放,以便允许通过 Android TV 的控制输入进行基本播放控制。

Android TV 兼容性指南

以下是一些建议和常见陷阱,请务必避免这些陷阱,以确保您的应用与 Android TV 兼容:

  • 请注意,用户代理字符串同时包含“Android”和“CrKey”;由于检测到“Android”标签,某些网站可能会重定向到仅限移动设备的网站。不要假设用户代理字符串中的“Android”始终表示移动用户。
  • Android 的媒体堆栈可能会使用透明 GZIP 来提取数据。确保您的媒体数据可以响应 Accept-Encoding: gzip
  • Android TV HTML5 媒体事件的触发时间可能与 Chromecast 不同,这可能会揭示 Chromecast 上隐藏的问题。
  • 更新媒体时,请使用 <audio>/<video> 元素(例如 timeupdatepausewaiting)触发的媒体相关事件。避免使用与网络相关的事件,例如 progresssuspendstalled,因为这些事件通常与平台相关。如需详细了解如何在接收器中处理媒体事件,请参阅媒体事件
  • 配置接收器网站的 HTTPS 证书时,请务必添加中间 CA 证书。请访问 Qualsys SSL 测试页面进行验证:如果您网站的可信认证路径包含标记为“extra download”(额外下载)的 CA 证书,则该证书可能无法在基于 Android 的平台上加载。
  • 虽然 Chromecast 会在 720p 图形平面上显示接收器页面,但包括 Android TV 在内的其他 Cast 平台可能会以高达 1080p 的画质显示该页面。确保接收器页面能够在不同分辨率下正常缩放。