骁龙及高通品牌产品均属于高通技术公司和/或其子公司产品。
如果您构建了富媒体应用程序,那么您可能已经使用了安卓应用程序接口的媒体播放器类别进行视频播放。与诸多开发人员一样,您可能已经发现,该应用程序接口缺乏通过超文本传输协议提供的动态流支持、平滑流、和持久性缓存,从而促使您开始寻找替代方案。在认识到该类缺点后,谷歌公司在安卓16级应用程序接口中添加了名为ExoPlayer的自定义媒体播放器。
ExoPlayer目前已成为最受安卓开发人员欢迎的媒体播放器之一。早在2016年,就已经有超过10,000个应用程序使用该媒体播放器;而在如今,使用该媒体播放器的应用程序包括了像网飞和YouTube这样家喻户晓的名字。目前正在将ExoPlayer从安卓应用程序接口范围转移到JetPack Media3模块中 – 一种富媒体库的集合,可提供丰富的音频和视觉体验。
尽管ExoPlayer具有诸多优势,但媒体播放器在音频卸载(即对来自于应用处理器(AP)的下游音频进行解码,例如:数字信号处理器)方面占据了有利地位。虽然在媒体播放器中默认启用,但ExoPlayer中的音频卸载支持在v2.12版本中是作为一项实验功能引入(参见 DefaultRenderersFactory.setEnableAudioOffload 和 ExoPlayer.experimentalSetOffloadSchedulingEnabled)。此外,由于应用程序接口的限制,ExoPlayer中的音频卸载仅适用于运行安卓10+版本的设备,而媒体播放器则可以在早期版本中支持卸载。
在几种常见情况下,选择ExoPlayer而不是媒体播放器的开发人员以提高音频功耗(尽管增加的音频功耗很小)为代价,增加了自身功能:
-
仅限音频
-
在开发人员意图在关闭屏幕时仍然确保音频与非音频轨道保持同步的情况下,关闭屏幕的音乐和播客播放
-
高延迟(几秒钟或更长时间)
-
长播放时间至少为一分钟
高通公司认识到这些用例的重要性,从而开始着手提高ExoPlayer的功率效率。
通过跨公司、多团队的工作方式达到目标
考虑到ExoPlayer受欢迎的程度,在将音频卸载到高通Hexagon数字信号处理器中的骁龙移动平台音频数字信号处理器(ADSP)时,我们希望使其功率效率达到接近媒体播放器的水平。
一年多来,我们的音频团队与谷歌、ExoPlayer团队和YouTube合作,将ExoPlayer的音频功耗降低到音频播放器的5%以内。这项跨公司、多团队的工作旨在通过骁龙应用程序上的ExoPlayer改善YouTube的音频功耗,并最终通过以下目标改进围绕ExoPlayer构建的任何应用程序:
-
启用芯片级的数字信号处理器
-
提高终端用户的电池使用寿命,特别是在启用卸载的情况下,在屏幕关闭音乐播放过程中显著提高电池使用寿命
-
降低上述场景中的功耗
-
启用非中央处理器式音频播放管理(例如,在本博客后面所描述的播放速度控制)。之前并没有针对音频样本的控制,而各个应用程序均必须依赖音频框架应用程序接口进行流控制
-
支持在卸载情况下进行音频解码时所使用的Opus编解码器。对于比特率较低的高品质音频,Opus已成为高级音频编码和MP3的首选编解码器。在安卓R11程序中增加了Opus支持功能(30级应用程序接口)
-
支持利用Opus进行无间隙播放时的卸载,以及采用卸载模式播放时的所有其他支持格式
提高效率
为了实现这些目标,我们探索了两种主要方法:
1. 在利用较少应用处理器时间/周期的情况下,提高硬件音频解码效率。
2. 在缓冲区中排列更多的音频,以减少应用处理器唤醒的数量。
以前,通过MediaCodecAudioRenderer播放的编码音频必须经过媒体编解码器,而解码后的音频则必须经过音频轨道,如下图1所示:
Explayer默认渲染器 | Explayer卸载渲染器 | |
媒体 编解码器 | 音频轨道 脉冲编码调制 | 软件开发工具包 音频轨道 卸载 |
脉冲编码调制 -0.04秒期间的环形缓冲区 | -60秒期间的 压缩环形缓冲区 | |
效果 | 解码器 | |
音频数字 音响处理器 高级数字信号处理 |
附图1 – 比较通过媒体编解码器的现有ExoPlayer音频渲染与音频卸载
在直通情况下,这种媒体编解码器的往复循环并不必要,并且会显著增加延迟和缓冲,通常还会增加功率使用。如要通过快速、少发的应用处理器唤醒来达到我们的功率效率目标,则必须避免这种情况。
如要通过旁路绕过媒体编解码器,渲染器需要跳过媒体编解码器的输入和输出循环。目前,可以将经过压缩的音频直接写入到音频轨道中,如附图2所示:
Exoplayer 音频与视频 “正常路径” | Exoplayer 音频与视频 直通 | ||
压缩 | 压缩 | ||
媒体编解码器 | 媒体编解码器 | 原媒体编解码器 | |
脉冲编码调制 | 压缩 | ||
表面 | 音频轨道 脉冲编码调制 | 表面 | 音频轨道 脉冲编码调制 |
Exoplayer 音频与视频 “正常路径” | Exoplayer 音频与视频 直通 | Exoplayer 仅限音频 卸载 | ||
压缩 | ||||
媒体编解码器 | 媒体 压缩 编解码器 (DTS,AC3…) | 压缩 (MP3,高级音频编码…) | ||
脉冲编码调制 | ||||
表面 | 音频轨道 脉冲编码调制 | 表面 | 音频轨道 直通 | 音频轨道 卸载 |
无变化 | 音频并未使用媒体编解码器 | 支持卸载 |
附图2 – 上图:普通模式和直通模式下现有ExoPlayer音频渲染
下图:针对直通模式以及新卸载模式的更新ExoPlayer路径
由于目前在直通和卸载情况下的音频路径均相同,因此应当通过此项修改改进直通延迟和解码抖动。
具体实施内容
ExoPlayer非常频繁地运行其主循环doSomeWork()。为了支持卸载模式,ExoPlayer必须尽可能少地运行,并且只在必要时运行,以减少应用处理器的使用。ExoPlayer唯一的重要工作是填充音频轨道的缓冲区,以避免欠载运行。将所有其他工作(例如:时间轴更新,错误处理等)延迟到doSomeWork()的下一次调用时,即为了填充音频轨道缓冲区而触发时(大约每分钟一次)。
为了通过音频轨道回调或适当时间的休眠进行doSomeWork()的调度,我们选择使用StreamEventCallback.onDataRequest。如系统已经消耗了某些音频,并且应用程序需要重新填充缓冲区,则会调用此项回调。该方法的优点在于通过音频服务器的消耗活动而触发。因此,应该在消耗了某些数据之后调度ExoPlayer,以便尽早重新填充缓冲区。
另一个优点在于,当ExoPlayer被音频服务器发送的消息唤醒时,应用处理器只需要唤醒一次,以进行消耗并重新填充音频轨道卸载缓冲区。
AudioTrack.setOffloadDelayPadding()可以在任何时间调用,并会影响当前播放的轨道,直到AudioTrack.setOffloadEndOfStream()被调用。设置流的卸载端可以暂时将音频轨道置于停止状态,直至将用户空间缓冲区输出到数字信号处理器。对AudioTrack.setOffloadDelayPadding()的所有后续调用均应归于下一个音轨。如能随时调用AudioTrack.setOffloadDelayPadding(),则有助于通过Opus进行播放以及类似情况;在该类情况下,除非提取并写入了最后一个样本,否则无法获知填充情况。
因此,doSomeWork()在卸载模式下的运行频率要低得多,并且像PlaybackInfo.currentPosition这样的字段通常每分钟更新一次,而不是每10毫秒更新一次。虽然对于所要延迟的大多数状态更新而言,可以接受这种更新频率,但播放器的当前位置会被客户端广泛使用(特别是用户界面)。目前,应用程序通过类似updateCurrentPosition()这样的新应用程序接口来请求当前位置,从而触发doSomeWork()更新位置,并调用一种新方法(例如:onPositionUpdate())。
我们还为卸载模式添加了播放速度控制。当offloadVariableRatesSupported为“真”时,ExoPlayer会读取音频管理器的参数,以确定某一设备是否支持卸载音频的可变播放速率。我们使用这一参数是因为没有能够提供该信息的音频管理器应用程序接口。
结果
到目前为止,该项设计的结果令人印象深刻。当YouTube音乐应用程序包文件中的音频流被卸载到高级信号处理器时,我们发现与非卸载模式相比,所消耗的电流毫安(mA)数减少了大约13%,如附表1所示:
附表1 – 将有关YouTube音频流的音频非卸载至卸载模式与ExoPlayer进行比较时所节约的大约能源数量(以毫安为单位)。
Audio Streaming 音频流 | Measured 测量值 | Adjusted 调整值 |
Non Offload 非卸载 | 59.79 | 53.39 |
Audio Offload w/ D4 disabled D4禁用情况下的音频卸载 | 52.63 | 46.69 |
Delta (mA) 增量(毫安) | -7.2 | -6.7 |
Delta (%) 增量(%) | -12% | -13% |
下文附表2显示了使用压缩卸载进行机载MP3文件播放的电池功率测量结果以及测试设备上采用深度缓冲模式的测量结果:
Use Case – MP3 playback 用例 – MP3播放 | Device 1 设备1 | Device 2 设备2 | Device 3 设备3 |
Compressed offload (DSP decoder) 压缩卸载(数字信号处理解码器) | 25.32 mA 25.32毫安 | 24.60 mA 24.60毫安 | 27.80 mA 27.80毫安 |
Deep buffer path (ARM decode) 深度缓冲路径(异步响应模式解码器) | 35.27 mA 35.27毫安 | 38.71 mA 38.71毫安 | 42.57 A 42.57安 |
结论
我们与谷歌、YouTube、以及ExoPlayer等团队进行合作,以努力确保音频卸载成为ExoPlayer的正式功能。通过此项工作,可以使音频播放的功率效率达到接近媒体播放器的水平,因此开发人员可以更加自信地选择ExoPlayer的附加功能。
在Google Play提供的YouTube音乐安卓应用程序包中,可以针对高级音频编码格式启用音频卸载。目前,YouTube(音乐)高级账户所有人可以使用该项功能,而在团队进行了额外的确认和验证工作后,会在更广泛的范围内推出该项功能。
您可以在ExoPlayer2的独立GitHub(dev-v2分支)和ExoPlayer2的AndroidX GitHub中获得音频卸载支持。
如要获得更多信息,请查看ExoPlayer文档以及本公司的骁龙开发人员门户上的安卓系统。