开发人员控制台

电视直播集成


电视直播集成

Fire TV提供各种实时浏览、语音和搜索体验,以帮助客户发现和观看他们喜爱的内容。电视直播集成主要侧重于以转发内容的方式帮助现有客户体验直播电视(24小时不间断订阅源中安排的节目)。它基于以下Android文档: 开发电视输入服务

背景知识和术语

以下外部链接有助于您深入了解Fire TV电视直播。在进行集成之前,请查看此文档:

Fire TV上的电视直播功能

完成此集成后,客户安装和验证您的应用后即可在所有Fire TV设备中使用以下功能。(请注意,“直播”选项卡并不适用于所有市场)。

“主页”选项卡上的连续内容磁贴显示每个不间断订阅源的当前节目信息:

“主页”选项卡上的连续内容磁贴

“直播”选项卡上的连续内容磁贴显示当前节目信息:

推广机会

“直播”选项卡上的连续内容磁贴显示当前节目信息:

“直播”选项卡上的连续内容磁贴

显示每个频道14天节目信息的集成频道指南:

显示14天信息的集成频道指南

用户可以搜索未来14天的电台和节目信息:

搜索电台/节目信息

支持Alexa的“更换到<channel_name>”语音命令:

频道更换

用户可以在浏览体验中收藏和管理频道:

收藏和管理频道

上面列出的所有浏览、搜索和语音入口点都可以选择在本机电视直播应用中处理播放或通过深度链接启动播放。

集成要求

要集成连续的直播频道,您必须提供对客户的频道授权以及相关元数据的访问权限,以便在Fire TV上提供内容转发浏览和搜索体验。

直播频道授权

应对您的应用进行更新,以便将授权频道推送到设备上由电视输入框架(TIF)提供的本地频道数据库中。您可以随时添加、更新或删除频道,即使您的应用在后台运行也可如此。请为您的应用注册广播接收器INITIALIZE_PROGRAMS操作,以确保在最初安装您的应用时更新授权元数据。

要编辑频道,请参阅使用频道数据

频道若出现在此数据库中,意味着客户可以观看这些频道。对于在任何直播体验中客户能观看的所有频道,设备上的电视直播应用将使用此数据库进行授权。可以随时在此数据库中添加或删除频道,并且更改将立即反映在UI中。

应尽可能完整地填写TvContract.Channels中的频道列。如果您知道您的Gracenote频道ID或希望为播放提供深度链接,您可以在Channels#COLUMN_INTERNAL_PROVIDER_DATA中共享该信息,如下面的代码段所述。

获取节目元数据

如果您能够提供Gracenote频道ID(位于电视或全球视频数据(GVD)上),这些信息将由电视直播应用同步到云端,并用于在包含长达14天节目信息的Fire TV目录中查找相应的频道。如果我们的云中有该Gracenote ID,该频道的相关节目信息将显示在“主页”和“直播”选项卡以及频道指南中,无需任何进一步的集成,并会自动刷新。搜索和Alexa功能也可以开箱即用。

没有Gracenote ID?

如果您无法提供您频道的Gracenote频道ID,则需要插入所有频道元数据(包括标志),并尽可能定期将所有频道的新节目信息插入到TvContract.Programs中。Android示例电视输入应用提供了如何实现这一功能的示例。

频道深层链接

电视直播内容的播放通常由设备上的本机电视直播应用通过与TvinputService.Session进行交互来处理。如果您希望使用自己的应用进行全屏播放,则需要在插入到频道数据库的频道信息中提供深度链接意图。下面提供了一个代码示例以供参考。当在任何浏览或搜索体验中选择频道时,将会启动此意图。预期此意图会跳转到您的应用并启动所请求频道的全屏播放。

即使提供了深度链接意图,仍然需要实现TvInputService.Session。当调用onTune时(当客户关注浏览行中的一个磁贴时会调用),强烈建议将所请求频道的播放转到提供的安全Surface上,因为这是一个关键的参与驱动程序。onTune请求也可用于捕获指标,或作为开始加载直播内容以提高性能的提示。

家长监护

如果您的应用将内容呈现到用于TvInputService的Surface上,则正确实施家长监护至关重要。这样可以确保在启用家长监护时,必须先输入PIN,然后才会为最终用户显示内容。您的TvInputService有责任在请求应该被阻止的内容时通知前台应用。请参阅代码示例部分了解建议的流程。

最佳实践

以下是一套产品和实施指南,可为您的客户提供最佳的Fire TV电视直播体验:

  • 提供无冲突注册,鼓励在适用情况下试用。例如减少应用上的注册表或使用电话号码进行注册。
  • 支持浏览时进行直播频道预览以提升体验
  • 对您频道列表中的每个TvContract.Channels.Logo使用透明的单色标志。
  • 优化深度链路流,以在2.5秒内开始全屏播放
  • 使用Gracenote频道ID(如适用)简化集成
  • 注重元数据加载性能,而不是提供完整节目单
  • 使用JobSchedulerWorkManager定期检查并确保授权始终准确。即使您的应用未在前台运行,这也可以确保您在浏览和搜索体验中的频道与实际的授权频道始终同步。
  • 在授权频道列表稍有变化时,最佳办法是更新您的授权频道列表,而不是删除和重新添加所有频道。
  • COLUMN_DISPLAY_NAME列提供可显示的频道名称,因为这在UI中可用作回退。Fire TV最多可显示16个字符。

程序包白名单

程序包白名单可用于确定哪些应用能够在Fire TV浏览和搜索体验中显示其频道。您需要与亚马逊开发者共享设备序列号(DSN)列表,以确保您能够在上线之前在设备上集成和验证上述功能。

提供方属性

默认情况下,您的应用的标签将用作包含您的频道的浏览体验中的标题。如果您想用其他名称但由于某种原因无法更改标签,请与亚马逊开发者合作,以已确保此信息被覆盖。您还需要提供一个单色标志,以覆盖在您的程序艺术设计之上。标志应为34像素高,水平方向上的宽度可为合理的任意值。

没有Gracenote ID?

当您填充TvContract.Program中的COLUMN_POSTER_ART_URI时,您需要将单色标志刻录到图像中。单色标志必须放置在右上角坐标的(-56, 44)位置。

认证核对清单

亚马逊将使用以下核对清单来证明您的应用已进行电视直播集成;您的应用的实现会直接影响下面的预期行为要点。请注意,任何有关认证的例外情况将根据具体情况处理:

授权状态更改

  • 客户执行以下操作之一后,客户授权频道将填充在Fire TV UI中(例如,频道指南中的“可观看”行): (1)打开并登录应用,或(2)在应用的设置页面上访问“设置–>电视直播->直播电视源–> <应用名称>”。
  • 如果客户的会员资格过期(即无访问权),这些频道会继续显示在Fire TV UI上,直到客户进入频道、查看付费墙以及退出或拒绝该选项。
  • 当应用被卸载或这些频道变为未授权频道时,应从Fire TV UI中删除这些频道。

浏览和播放体验

  • 当转到某个频道时,将提供丰富的元数据体验。在指南中,频道将包括频道标志、频道号(可选)和未来14天的节目安排信息。节目元数据包括节目名称、播放时间、剧集名称、剧集描述(时长)、季节和剧集信息、隐藏式字幕、评级节目图像(16:9)和背景图像(16:9)。此元数据信息应来自Gracenote匹配或作为电视合同的一部分推送。
  • 当客户转到“可观看”行中的某个频道时,客户将能够看到替换背景图像的直播频道预览。
  • 客户应该了解谁在提供此授权频道。这包括添加提供方属性作为背景图像的一部分(单色,34像素高度)或作为直播频道预览播放之前的启动画面。
  • 选择频道后,客户将进入全屏播放。后退按钮将最终返回到Fire TV UI。
  • 如果启用了PCON,则在播放前会显示PIN提示。如果启用了PCON,则必须禁用直播频道预览。
  • 如果要访问Alexa,实时播放的音频必须静音;视频可继续。
  • (可选)可将某个频道归类,因此该频道将显示在Fire TV UI上的其他入口点中。

开发、发行准备和部署

  • 亚马逊必须能够将特定账户列入白名单,以便在投入生产之前检查和认证集成体验。
  • 只有授权频道才会在双方同意启动的市场中添加到Fire TV UI上。新市场中的后续发布将有新的认证审查,并需要列入白名单。
  • 亚马逊必须知晓在启动后添加和删除的任何频道。双方必须就可添加频道的数量上限达成一致。
  • (强烈推荐)应用的下载页面和发布说明应提及电视直播集成,以便客户了解Fire TV上新功能的可用性。

代码示例

以下代码示例演示如何将Gracenote ID和深度链接添加到电视数据库中。

/**
 *用于存储外部ID类型的变量,用于匹配的服务元数据。有效类型为
 *下面定义为带有前缀“EXTERNAL_ID_TYPE_”的常量
 *空或无效数据将导致服务失败
 *元数据匹配
 */
private final static String EXTERNAL_ID_TYPE = "externalIdType";

/**
 *用于存储外部ID的值的变量,用于匹配的服务元数据。
 *空或无效数据将导致元数据的服务匹配失败
 */
private final static String EXTERNAL_ID_VALUE = "externalIdValue";

/**
 *用于在外部播放器中播放的深层链接的Uri。
 * 空或无效数据将导致默认与Gordon播放器集成
 */
private final static String PLAYBACK_DEEP_LINK_URI = "playbackDeepLinkUri";

// 已知外部ID类型常量列表的合约
private final static String EXTERNAL_ID_TYPE_GRACENOTE_ONTV = "gracenote_ontv"; // gracenote ontv id

// 播放深层链接uri的合约
//使用Intent.URI_INTENT_SCHEME从意图创建uri并转换回原始意图
Intent playbackDeepLinkIntent; // 由您的应用创建
String playbackDeepLinkUri = playbackDeepLinkIntent.toUri(Intent.URI_INTENT_SCHEME);

//构建BLOB
ContentValues values = new ContentValues();  // 存储所有频道数据
ContentResolver resolver = context.getContentResolver();
values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "#实际显示名称#");
values.put(TvContract.Channels.COLUMN_INPUT_ID, "#实际输入id#");
try {
    String jsonString = new JSONObject()
                  .put(EXTERNAL_ID_TYPE, "#实际Id类型#")
                  .put(EXTERNAL_ID_VALUE, "#实际Id值#")
                  .put(PLAYBACK_DEEP_LINK_URI, playbackDeepLinkUri).toString();

    values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, jsonString.getBytes());
} catch (JSONException e) {
    Log.e(TAG, "向blob添加数据时出错" + e);
}

Uri uri = resolver.insert(TvContract.Channels.CONTENT_URI, values);

执行家长监护

以下代码示例演示了如何侦听家长监护,以实时预览或进行本机全屏播放。

private TvContentRating mBlockedRating = null;

    @Override
    public boolean onTune(final Uri channelUri) {
        ...
        if (mTvInputManager.isParentalControlsEnabled()) {
            // 确保播放在Surface上无法听到或看见
            mBlockedRating = <content_rating>;
            // 1.在全屏播放时为用户触发PIN提示
            // 2.确保节目画面不会跳转到Surface
            // 浏览“可观看”行时。
            notifyContentBlocked(mBlockedRating);
        } else {
            // 播放应开始
            notifyContentAllowed();
        }
        ...
    }

    @Override
    public void onUnblockContent(final TvContentRating unblockedRating) {
        // 用户成功输入PIN以解禁
        // 所选评级对应的内容
        if (unblockedRating.unblockContent(mBlockedRating)) {
            // 播放应开始
            notifyContentAllowed();
        }
    }