开发人员控制台

Fire TV上多媒体应用的要求


Fire TV上多媒体应用的要求

要使Fire TV应用与Fire TV上的其他应用和谐交互,所有应用都必须遵守相同的多媒体要求。这些多媒体要求包括如何正确处理活动生命周期和音频焦点事件、媒体会话、解码器实例、唤醒锁等。通过遵守此处概述的要求,当用户在Fire TV上的不同应用之间导航时,您的应用将为他们提供更好的体验。

Fire TV上应用的合作

在Fire TV上,应用的主要目的通常是播放多媒体。因此,Fire TV上各应用的合作是必不可少的。

在许多方面,面向电视设备的应用可能比一般的Android应用更复杂。“主页”按钮和语音交互随时可供用户使用,这可以立即中断应用的播放。与平板电脑和手机应用不同的是,在Fire TV上,不能使用状态栏和通知栏与应用交互。在电视设备上停止或终止出现异常的应用也是一项更复杂的任务。如果您明明已关闭播放音乐的应用,却在开始看电影时发现音乐还在继续播放,您就会知道多媒体交互发生冲突会带来多么灾难性的后果。

由于Fire TV上的应用需要和谐合作,因此所有应用都必须遵循相同的多媒体实践。一般来说,请遵循Android开发者页面上的推荐指南。但是,鉴于电视设备上各应用之间各种交互的复杂性,我们将Android开发者文档中列出的许多建议视为要求而不是建议。如果Fire TV的要求更严格,要求详细信息中会加以注明。

Android背景知识

本文中的概念假定您熟悉Android开发。为了更好地理解这些基本概念,请参阅Android文档中的以下主题:

请注意,这里提到的所有Android API并非都可以在Fire OS 5(最高支持API级别22)上使用。其中一些提到的API专用于Android API级别25 (Nougat),Fire OS 6支持该级别。如果要求所指的API级别并不受您的应用支持,请遵循您应用的API级别提供的实现指南。

按应用类型和活动/服务方法列出的要求

根据应用类型以及应用是将媒体作为活动还是服务进行播放,要求各不相同。多媒体应用可分为几种类型:

  • 仅播放音频的应用(例如,音乐应用)
  • 播放音频和视频的应用(例如,电视网络电台应用)
  • 仅播放视频的应用(例如,没有音频的游戏,允许用户用他们最喜欢的音乐应用在后台播放音乐)

这些不同类别的应用通常具有不同的要求。

此外,应用可以将媒体作为活动或服务播放。视频应用必须具有可用于渲染的窗口,因此它们通常使用活动方法。在后台播放(同时被其他活动覆盖)的纯音频应用必须使用前台服务以避免中断。在这两种情况下,必须在输出任何音频之前进行音频焦点管理。以下各部分分别指定了这些应用类型的要求。

基于活动的应用要求

当您的多媒体应用作为活动(而不是服务)播放时,您必须仔细处理活动生命周期事件。您必须了解Android的活动生命周期中描述的所有可能的过渡。处理这些过渡是您的Fire TV应用的要求。

要求1.1: 处理onPause()

当您的活动被其他活动部分覆盖(例如,在用户长按“主页”之后,Fire TV显示提醒通知)时调用onPause()。即使在调用onPause()后,您的活动仍可继续播放,但您仍然必须执行回调以改变音频焦点。(无论生命周期事件如何,都不允许您在失去音频焦点后播放音频。)

要求1.2: 处理onResume()

onResume()必须恢复原始播放状态(即恢复onPause()中所做的任何更改),而不调用onCreate()。这里的“播放状态”可能有各种含义,具体取决于您的应用。通常,您应使用原始音量继续自动播放(如果您也有音频焦点),并且还原后的UI显示最新进度条。如果您必须释放用于播放的任何资源,则可能需要重新初始化管道并找到播放中断之前的最后已知位置。

要求1.3: 在onStop()中释放资源

调用onStop()时,您必须释放用于播放的所有资源(AudioTrackMediaCodec等)。当用户按“主页”后返回到您的应用时,您的应用需要重新获取所有资源。

使用单独的活动进行播放时要小心谨慎。快速返回到您的应用可能意味着生命周期事件的顺序将是onStop()onRestart()onStart()onResume()。如果在onStop()中释放资源,则可以考虑通过调用finish()来关闭播放活动。或者,您可以沿这些生命周期事件重新获取所有播放资源(在onRestart()onStart()onResume()中)。

要求1.4: 避免使用onDestroy()

您不能依赖调用onDestroy()来释放资源。资源必须已在onStop()中释放。调用onDestroy()的时机(如果调用)由设备的操作系统决定。当快速切换到其他应用时,它很可能不会被调用。

要求1.5: 音频焦点与活动生命周期无关

您不能依赖在活动生命周期中特定状态下收到的特定音频焦点更改事件。生命周期和音频焦点更改事件通常一起收到,例如当您的活动因语音交互而中断时。在这种情况下,您将收到一个AUDIOFOCUS_LOSS和一个onPause()活动生命周期事件,但这些事件实际上是独立的,事件的顺序可随时更改。

要求1.6: 覆盖onKeyDown

如果您决定通过覆盖onKeyDown()来处理输入事件,则必须仔细设置返回值。您必须返回true以防止事件进一步传播。返回false意味着您尚未处理此事件,Android框架应继续予以传播。

基于服务的应用要求

如果您的应用播放不含视频的音频,建议您使用构建音频应用中所述的前台服务来实现播放。使用这种方法,当用户浏览其他活动时,允许在后台播放音频。

要求2.1: 启动服务

使用startForeground()方法将服务作为前台服务启动,以避免在内存不足的情况下被中断。

要求2.2: 停止服务

播放完毕后,使用stopForeground()删除服务的前台状态。此方法表示如果需要更多内存,您的服务可能会被终止。

媒体会话要求

媒体应用必须处理传入的媒体会话事件,无论事件发生在何处。事件可能源自以下各项:

  • Fire TV设备的遥控器
  • 带下一个/上一个按钮的蓝牙耳机
  • 带多媒体按钮的附加键盘,以及鼠标
  • 智能手表
  • 用户手机上运行的遥控应用
  • 响应语音交互的亚马逊服务(例如用户说“Alexa,暂停!”)等等。

要以通用方式处理所有这些事件,而不考虑活动生命周期状态、AudioFocus事件等,您的应用必须遵循使用媒体会话中的准则。具体而言,请注意以下事项:

“媒体会话与它管理的播放器一起存续。您应该在拥有媒体会话及其关联播放器的活动或服务的onCreate()方法中创建和初始化媒体会话。

处理媒体会话事件的要求如下:

要求3.1: 允许传输控制回调

初始化媒体会话时,设置标记以允许从传输控制器和媒体按钮回调。(注意:​ 从API级别26开始,所有会话都需要处理传输控制。)

要求3.2: 发布媒体会话

在开始播放之前,通过调用setActive来发布媒体会话。

要求3.3: 更新播放状态

保持PlaybackState为最新:

  • 维护有效控制器操作的列表。例如,“暂停”只有正在播放时才可能;此外,有些应用可能会在广告期间限制快进。

  • 更新播放器位置,以便正确更新任何进度条。注意:​ 为避免仅因更新播放位置而刷新状态,您可能需要使用带4个参数(包括updateTime)的PlaybackStateCompat.Builder.setState()

  • 在元数据发生变化时更新元数据信息,如艺术家姓名、曲目名称、曲目持续时间等。应更新所有UI显示(甚至是手机上的遥控应用)。

要求3.4: 释放媒体会话

播放完成后,使用MediaSession.release() API释放媒体会话。

要求3.5: 媒体会话的持续时间

您的媒体会话的有效期必须与您的应用被允许并愿意播放的时间段完全匹配。例如,如果您在后台播放音乐,则必须在后台持续处理媒体按钮。如果在暂时失去音频焦点时暂停播放,则还必须停用媒体会话以放弃对媒体按钮的处理。

音频焦点事件要求

通过音频焦点处理,应用以协调的方式播放音频。正如管理音频焦点中所解释的那样:

“为了避免每个音乐应用同时播放,Android引入了音频焦点的概念。一次只有一个应用可以持有音频焦点。”

在Fire TV上,播放任何类型音频的应用都必须具有音频焦点,然后才能开始播放。围绕请求音频焦点、处理音频焦点更改和放弃音频焦点有各种要求。

要求4.1: 在播放的前一刻请求音频焦点

应用必须在播放音频前即刻调用AudioManager.requestAudioFocus()。(许多开发者忽略了要求中的即刻部分。) 您的requestAudioFocus()abandonAudioFocus()音频焦点调用之间的时间段必须与您处理焦点更改回调的时间完全匹配。

例如,通常错误实现的场景是:

  1. 用户在列出应用内各种可用内容的屏幕上单击“播放”。
  2. 应用请求音频焦点以检查是否允许播放。
  3. 应用开始下载内容,显示“正在缓冲”屏幕几秒钟。
  4. 应用切换到活动,由实际播放处理音频焦点变化。

在实际播放开始前的瞬态“正在缓冲”屏幕(步骤3)期间,您的应用也可能会失去音频焦点。如果您在步骤2中请求了音频焦点,则必须从该时刻开始连续处理AudioFocus事件。当用户到达步骤4(由播放活动处理AudioFocus事件)时,您的应用可能已经失去了播放权限(例如,如果用户在步骤1中单击“播放”后快速启动了语音交互)。

要求4.2: 验证是否已授予焦点

应用必须验证requestAudioFocus()调用是否返回AUDIOFOCUS_REQUEST_GRANTED。返回的任何其他值意味着不允许您的应用开始播放。注意:​ API级别26引入了AUDIOFOCUS_REQUEST_DELAYED,但目前不会在Fire TV设备(API级别25)上返回此值。

要求4.3: 放弃音频焦点

每个播放会话都必须在完成播放后立即放弃音频焦点。如果您的应用有多个播放会话(例如,您开始在新活动上播放下一集节目),则不需要第二次请求音频焦点。如果您第二次请求音频焦点,您的第一个会话将收到一个带有参数AUDIOFOCUS_LOSS的回调;作为响应,您的第一个会话应该释放它的资源(那些未与第二个会话共享的资源)并放弃音频焦点。因此,您的应用将始终只出现在AudioFocus堆栈中一次(您可以在命令行中运行adb shell dumpsys audio来对此进行手动确认)。

音频焦点更改要求

如果您的应用请求音频焦点,它还必须处理实现AudioManager.OnAudioFocusChangeListener的焦点更改,并且必须正确处理所有回调参数。回调参数是以下项之一:

  • AUDIOFOCUS_LOSS
  • AUDIOFOCUS_LOSS_TRANSIENT
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
  • AUDIOFOCUS_GAIN

要求4.4: 处理AUDIOFOCUS_LOSS

当您的应用收到AUDIOFOCUS_LOSS事件时,它必须完全停止音频播放,因为系统已授予另一个应用完全播放权限。您不应该期望音频焦点会重新回到您的应用(在此之后,您将永远不会得到AUDIOFOCUS_GAIN)。您应该关闭您的应用,释放资源(AudioTrackMediaCodec等等),并通过调用AudioManager.abandonAudioFocus()来通知系统您播放完毕。

要求4.5: 处理AUDIOFOCUS_LOSS_TRANSIENT

当您的应用收到AUDIOFOCUS_LOSS_TRANSIENT事件时,它必须“暂停”音频播放(但这里没有对视频的硬性要求)。当您的应用因短暂通知、系统声音或其他临时状态而中断时,通常会发生这种情况,并且音频焦点将很快回到您的应用。

暂停音频意味着在此时间点之后您无法输出任何音频。您的应用必须处于静音状态。您可以暂停整个播放,也可以将音频音量降低到0并继续播放视频。播放按需内容的视频应用应选择完全暂停视频和音频播放。您可以保留用于播放的资源。如果完全暂停将导致用户体验较差(例如,直播视频馈送缓冲较长时间从而中断),您可以在音频静音时继续播放视频。

要求4.6: 处理AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK

当您的应用收到AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK时,它必须降低音量(放弃)。预期的音量水平可能因各种用例而异,但不得干扰与音频输出平行播放的任何其他声音。(例如,您可以将临时音量级别设置为原始音量的30-40%左右。)

当系统想要播放短通知时,通常会收到此事件,在此期间,音频应用可以继续以较低音量播放音乐。注意:​ 这意味着您还可以将此临时音量级别设置为0或完全暂停播放,从而实际上像处理AUDIOFOCUS_LOSS_TRANSIENT一样处理此事件。

要求4.7: 处理AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK

输出编码音频内容(例如杜比直通)的应用应在收到AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK时完全暂停播放。编码的内容在解码之前没有音量的概念。对于直通情况,只在接收器(AVR)上进行解码,因此在Fire TV上设置流媒体音量不会影响实际输出。

亚马逊的音频增强型平台或许能够对内容进行解码,将音量更新到较低级别,对内容进行重新编码,然后将其发送到HDMI输出,但不保证此功能。(您可以联系亚马逊了解具体信息和可用性。)

要求4.8: 处理AUDIOFOCUS_GAIN

当您的应用收到AUDIOFOCUS_GAIN时,它可以按照应用最初预期的那样继续播放。此事件将在短暂时间段(您在此期间暂停、静音或以降低音量播放)结束后发送。如果您之前调低了音量,则必须恢复原始音量级别

当收到此AUDIOFOCUS_GAIN事件时,播放实时流的音频应用和视频应用通常会自动继续播放。此外,还建议播放按需视频内容的应用继续播放,或者选择保持暂停状态,向用户显示“播放”按钮以便在按下该按钮后继续播放。

要求4.9: 放弃音频焦点

完成音频播放后,如果您的应用曾被授予音频焦点,则还必须使用与之前请求的音频焦点相同的参数调用AudioManager.AbandonAudioFocus()。您必须释放用于播放的资源。(您可以再次请求音频焦点以进行下一次播放)。

安全解码器要求

要求5.1: 安全解码器的限制

应用在任何给定时间只能使用一个安全的解码器管道。大多数平台只支持一个安全解码器(MediaCodec)实例,因此对多个安全内容进行并行解码的任何尝试都将失败。

要求5.2: 释放资源

当调用播放活动的onStop()函数时,必须立即释放安全解码器。安全解码器用于视频播放,因此需要解码器的应用将始终具有一个活动和用于呈现视频的表面。一旦此活动停止,应立即释放资源,以便其他应用获取安全管道的单个实例。

唤醒锁要求

一般来说,您可以预期所有Fire TV设备都由墙上电源供电(而不是电池供电),因此您可以跳过应用的功耗和性能管理。例如,您不需要保持CPU处于打开状态、调暗屏幕、管理高效的网络使用或WIFI锁。但是,您需要遵循保持设备处于唤醒状态开发者页面中的说明来管理唤醒锁。

要求6.1: 保持设备处于唤醒状态

在预期播放时,您必须保持设备处于唤醒状态。这通常意味着您的应用设置活动标记FLAG_KEEP_SCREEN_ON或通过获取FULL_WAKE_LOCK来实现动态唤醒锁管理。

注意:​ 使用FLAG_KEEP_SCREEN_ON时,您必须在播放暂停时取消设置该标记。

要求6.2: 释放唤醒锁

如果播放因任何原因未处于有效状态,则必须释放唤醒锁。(如果影片结束、显示缓冲屏幕或失去音频焦点,则播放可能不会处于有效状态。) 您的应用预计会让设备屏保启动(超时后),然后让设备进入睡眠状态。

输出状态更改要求

当输出状态发生变化时,应用必须遵守以下要求。

要求7.1: HDMI断开连接

当您收到HDMI断开事件时,必须暂停媒体播放。大多数应用应该不会在没有屏幕的情况下继续播放(不必要的4K视频播放的数据使用量可能非常大)。即使纯音频应用也应在HDMI接收器断开连接时暂停播放。

注意:​ 当显示屏的输入从Fire TV更改为其他事物时,也会发送HDMI事件。当HDMI线缆重新连接到显示屏时,大多数应用都应该保持暂停状态。

要求7.2: 从蓝牙接收器切换到HDMI输出

耳机通常以较高音量使用,而电视扬声器和音响系统的音量通常低得多。当用户断开所用蓝牙耳机的连接时,音频流将自动重新路由到扬声器。用户通常期望音频和视频应用暂停,以避免音量级别的突然变化。为了支持这种情况,您的应用应该处理意图ACTION_AUDIO_BECOMING_NOISY

要求7.3: 切换到蓝牙输出

直接输出编码音频流的应用(例如,杜比直通)必须侦听音频设备的更改。编码的音频流可以发送到HDMI输出(电视机和AV接收器支持大多数杜比标准),而无需在Fire TV上进行解码。但是,当打开蓝牙耳机时,音频流会自动重新路由到耳机,并且蓝牙标准不允许将编码的音频流定向到这些设备。因此,应用必须为设备功能的变化做好准备,并采取适当的行动。关于如何识别这些音频设备的变化以及如何对变化作出反应,有多个选项。

要接收通知,应用可以注册AudioDeviceCallbacks(在API级别23中引入)或注册意图AudioManager.ACTION_HDMI_AUDIO_PLUG(后者速度较慢并占用较多资源,因此建议使用AudioDeviceCallback)。

为了应对更改,除其他选项外,应用还可以切换到PCM输入流或关闭直通(通过使用MediaCodec,编码的输入流将被解码为PCM);蓝牙耳机将能够正确播放内容。

要求7.4: 在不同HDMI输出之间切换

对于依赖PCM输出以外的输出(如杜比或DTS等编码)的应用,必须在HDMI断开连接和重新连接事件之间检查接收器的功能是否发生了变化。如果功能已更改,它们必须重新创建音频对象。

用户可以从支持各种音频编码的显示屏拔下HDMI线缆,并将其插回支持不同编码的显示屏。功能的变化可能需要您的应用调整其音频输出。

其他要求

要求8.1: 不修改全局设备状态

切勿使用影响设备全局状态的API,例如,AudioManger.setStreamVolume()。尝试更新音量的应用应使用其AudioTrack或MediaPlayer实例的setVolume() API,而不是旨在影响设备所有应用的AudioManager API。

要求8.2: 以最新API级别为目标

Android框架的事件处理和行为可能取决于您的应用所针对的API级别。一般而言,始终以应用运行的最高API级别为目标。目前,如果您希望您的应用在最新Fire TV设备上正常运行,则必须以API级别28 (Android Pie)为目标。

要求8.3: 避免占用过多CPU

如果设备达到极高的温度,它将开始限制CPU性能来降温。如果温度还在升高,作为最后的手段,Fire TV设备将重新启动,以避免高温造成硬件损坏。

连续多媒体播放将使所有系统资源处于恒定的压力下。我们彻底测试所有常见的多媒体播放用例,包括音频和视频的所有加密和编码组合。通过最佳应用代码,确定所有设备功能都可实现且安全。

您有责任使您的应用代码足够高效,以避免设备过热。高CPU负载的短突发是可以接受的,但连续的高CPU使用率会导致设备温度升高。使用最苛刻的用例(最高分辨率、帧速率、加密播放、所有叠加层打开)超时测试您的应用。

例如,如果您的应用播放电影,请用最长的电影测试。应用不能让设备达到令CPU受限的温度。请考虑使用更高效的编码(例如,用HEVC代替AVC),用硬件解决方案代替软件实现,以减少整个多媒体管道的压力。

要求8.4: 测试复杂场景

考虑先前描述的要求的复杂方案。如果您正确处理AudioFocus事件和Android生命周期事件,维持媒体会话,遵循HDMI连接和断开连接状态等,您的应用应能够处理任意复杂的用例,如下所示:

  1. 在应用中开始播放。
  2. 启动语音搜索。
  3. 从显示屏上拔下HDMI线缆。
  4. 按遥控器上的“后退”以从语音搜索返回到您的应用。
  5. 打开之前配对的蓝牙耳机。
  6. 将HDMI线缆重新插入显示屏。

此时,大多数应用应等待处于暂停状态的用户交互。所有用户界面显示都应反映暂停状态。例如,遥控器应用应禁用“暂停”按钮。应将音频路由到蓝牙耳机,同时避免杜比直通输出。