PulseAudio VS AudioFlinger(八)

我一直想尝试一遍,我们也听到了社区的许多要求。最近,我在Collabora有一些时间可以尝试一下-即让PulseAudio在Android设备上运行,并查看它与Android的AudioFlinger的比较。

竞争者

让我们首先介绍我们的竞争者。对于那些不知道的人,PulseAudio几乎是Linux音频堆栈的事实上的标准部分。它位于ALSA的顶部,后者提供了一种与音频硬件对话的统一方法,并提供了许多方便的功能,这些功能在台式机和嵌入式设备上非常有用。我不会重述所有这些内容,但是其中包括一个不错的模块化框架,一堆省电功能,灵活的路由等等。PulseAudio作为守护程序运行,客户端通常使用libpulse库与其进行通信。

在另一个角落,我们有Android的本地音频系统-AudioFlinger。AudioFlinger是从零开始为Android编写的。它提供了用于回放/记录的API以及用于实现策略的控制机制。它不依赖于ALSA,而是允许一种HAL,供供应商以其选择的任何方式实施。应用程序通常通过AudioFlinger之上的图层播放音频。即使您编写本机应用程序,它也将使用通过AudioFlinger进行的OpenSL ES实现。实际的服务作为mediaserver守护程序的线程运行,但这仅是实现细节。

注意:通常,我对AudioFlinger和Android的所有评论均基于Android 4.0(Ice Cream Sandwich)的文档和代码。

竞技场

我用于测试的测试平台是运行Android 4.0 的Galaxy Nexus,我们将其简称为ICS。我之所以选择ICS,是因为它是Google正在构建的当前平台,并希望它代表了AudioFlinger开发中的最新,最出色的产品。Galaxy Nexus运行的是德州仪器(TI)OMAP4处理器,这也非常方便,因为该芯片对运行现有的Linux有很好的支持(请继续阅读以了解其用途)。

准备工作

在Android上获取PulseAudio的第一步是决定是像常规应用程序一样使用Android NDK,还是集成到基本的Android系统中。我选择了后者-尽管起初要多做一些工作,但从长远来看,由于PulseAudio确实属于基本系统,因此更有意义。

下一个任务是将所需的依赖项移植到Android。幸运的是,Collabora的一些出色人员已经完成了许多基础工作。Derek Foreman的雄激素生成器工具非常方便,可以将基于自动工具的版本转换为对Android友好的makefile。以Reynaldo Verdejo和Alessandro Decina之前在Android GStreamer上的工作作为参考,事情变得更加容易。

最痛苦的一点是libltdl,我们使用它来动态加载模块。完成此操作后,其他依赖项将非常容易移植。另外,Android源代码已经提供了Speex的优化版本,我们将其用于重新采样,并且也很容易重用它。

如前所述,供应商可以选择如何实现其音频抽象层。在Galaxy Nexus上,它是建立在标准ALSA驱动程序之上的,而HAL通过极简的tinyalsa库与驱动程序进行通信。我的第一个希望是使用此功能,但是PulseAudio需要缺少很多功能。下一种方法是使用salsa-lib,它是为嵌入式设备编写的ALSA库的精简版本。这也缺少一些功能,但是这些功能较少且易于实现(现在位于上游)。

现在,只要生活如此简单。:)我让salsa-lib在Galaxy Nexus上运行PulseAudio ,甚至从HDMI端口发出声音。扬声器没有任何声音(它们由TI twl6040编解码器驱动)。只是为了验证,我决定移植完整的alsa-lib和alsa-utils软件包来调试正在发生的事情(这一次,我对androgenizer已经足够熟悉,这一切都变得轻而易举)。仍然没有运气。最后,借助TI同类人员的一些指导(感谢Liam!),我获得了OMAP4板卡的当前UCM配置文件,以及一些正在开发的补丁程序,它们为PulseAudio添加了UCM支持,并进行了一些小的修复, ham!我们有输出。:)

(对于那些不了解UCM的人-嵌入式芯片与台式机完全不同,并且通过ALSA混合器控件公开了很多功能。UCM致力于为应用程序和用户提供一种标准且有意义的方式来使用它们。)

在生产中,为salsa-lib编写轻量级UCM支持或将UCM配置转换为PulseAudio路径/配置文件配置(如果是自动工具,则可以得到奖励)可能会很方便。对于我们而言,仅使用alsa-lib就足够了。

为了使比较合理,我编写了一个简单的测试程序,该程序从文件中读取原始PCM S16LE数据,并通过AudioFlinger或PulseAudioAsynchronous API提供的AudioTrack接口播放它们。测试是在固定亮度,关闭wifi并连接到笔记本电脑的USB端口(用于adb外壳访问)的情况下进行的。

所有测试均以固定在350 MHz的CPU频率以及44.1和48 kHz样本进行。记录五个读数,最后取中值。

第一轮:CPU

首先,让我们看一下两者在CPU使用率方面的比较。下面的数字是CPU使用百分比,它是音频服务器进程的所有线程与客户端应用程序中使用top的音频线程的总和(这就是为什么将粒度限制为整数百分比的原因)。

44.1 kHz的 48 kHz的
自动对焦 功放 自动对焦 功放
1% 1% 2% 0%

两者在44.1 kHz时基本相同。两种情况都导致发生重采样(设备的本机采样率为48 kHz)。重采样是使用Speex库完成的,即使在350 MHz时,我们看到的CPU使用量也很小,因此很显然NEON的优化确实在这里取得了回报。

精明的读者会注意到,由于设备的原始采样率为48 kHz,因此48 kHz回放的CPU使用率应小于44.1 kHz。PulseAudio确实如此,但AudioFlinger却不正确!之所以这么奇怪,是因为AudioFlinger向HAL提供了44.1 kHz采样(这意味着流在此处进行了重新采样),然后HAL需要再次将其重新采样到48 kHz以使其达到设备的原始速率。据我所知,关于音频HAL对AudioFlinger的期望是一个惯例问题(如果我对基本原理有误,请纠正我)。

因此,第一回合稍微偏向于PulseAudio。

第二回合:记忆

比较服务器进程的内存消耗是有点意义的,因为AudioFlinger守护线程共享与其余的地址空间媒体服务器的过程。出于好奇,常驻集大小为:AudioFlinger — 6,796 KB,PulseAudio — 3,024 KB。同样,这并不意味着什么。

但是,我们可以比较客户端进程的内存消耗。这是以千字节为单位的RSS,使用top进行测量。

44.1 kHz的 48 kHz的
自动对焦 功放 自动对焦 功放
2600 KB 3020 kB 2604 KB 3020 kB

两者之间的内存消耗是可比的,但是倾向于AudioFlinger。

第三回合:力量

我无权使用电源监控器,因此我决定使用几个间接指标来比较电源利用率。第一个是PowerTOP,它实际上是一个Linux桌面工具,用于监视各种功率指标。幸运的是,有人已经将PowerTOP移植到Android。该工具除其他外,报告处理器整体的每秒从空闲状态唤醒的次数,并报告每个进程的情况。由于涉及多个线程,并且PowerTOP的每个进程的度量值加起来有些神秘,因此我使用了每秒从空闲状态唤醒的全局时间。“空闲”值用于计算什么都没有发生时的唤醒次数。实际值很可能很高,因为该设备以USB调试模式连接到我的笔记本电脑(从USB唤醒了很多设备,并阻止了设备进入完整睡眠状态)。

  44.1 kHz的 48 kHz的
自动对焦 功放 自动对焦 功放
79.6 107.8 87.3 108.5 85.7

第二个类似的数据点是vmstat报告的每秒中断数。这些证实了上面的数字:

  44.1 kHz的 48 kHz的
自动对焦 功放 自动对焦 功放
190 266 215 284 207

在此比较中,PulseAudio的省电功能得到了明显强调。AudioFlinger引起的每秒唤醒次数大约是PulseAudio的三倍。实际上,在较旧的硬件上,经过优化的驱动程序比Galaxy Nexus的情况要糟得多(我很感谢在Nexus S或其他任何具有ALSA支持的设备上运行类似测试来证实这一点的报告)。

对于不熟悉PulseAudio的那些人,我们设法节省这些费用的原因是我们基于计时器的计划模式。在这种模式下,我们尽可能地填充硬件缓冲区并进入睡眠状态(如果可能的话,在处于该状态时禁用ALSA中断)。我们仅在缓冲区接近空时唤醒,然后再次填充。可以在Lennart的旧博客文章中找到更多详细信息。

第四轮:延迟

我只有Galaxy Nexus可以实际试用,但我可以肯定,我不是唯一看到Android延迟问题的人。例如,在Galaxy Nexus上,我可以获得的最佳延迟似乎是176毫秒。对于某些类型的应用程序,尤其是根据用户输入生成音调的应用程序,这是相当高的。

使用PulseAudio,我们可以根据客户的请求动态调整缓冲,我可以将总缓冲降低到大约20毫秒(太短了,而且我们开始出现辍学)。这里可能还有改进的余地,这是我的待办事项清单,但是即使开箱即用,我们也做得很好。

第五回合:特色

有了一些困难的数字,我想谈一谈PulseAudio带来的其他好处。除了回放/记录API外,AudioFlinger还提供用于执行各种策略位(例如音量)和设置“活动”设备等的机制。PulseAudio公开了类似的功能,其中一些作为客户端API的一部分,其余部分则通过向模块公开的核心API公开。

从SoC供应商的角度来看,通常需要在同一芯片上同时支持Android和标准Linux。在这种情况下,能够只专注于高质量的ALSA驱动程序,并且知道这将确保这两个系统上的质量都是绝对的优势。

当前的Android系统将电源管理留给了音频HAL。这意味着每个供应商都需要自己实施。让PulseAudio根据请求的延迟和策略管理硬件,可以为我们提供单点控制,从而大大简化了电源管理任务并避免了代码重复。

PulseAudio提供了许多功能,这些功能在使用Android的各种情况下很有用。例如,我们支持通过网络透明地传输音频流,这可能是一种方便的方法,可以完全透明且开箱即用地支持在电视上播放手机中的音频。我们还支持压缩格式(AC3,DTS等),您可以通过电视上正在进行的Android工作充分利用压缩格式。

编辑:正如有人在LWN上指出的那样,我错过了一件事-AudioFlinger具有我们在PulseAudio中还没有的效果API。我绝对希望将来将其添加到PulseAudio中。

D!D!D!

差不多就可以得出这两个音频守护程序的比较。由于Android端代码的文档不足,我欢迎熟悉AudioFlinger的代码和历史的读者发表评论。

我正在将我不得不编写的所有补丁推送到各个上游项目。其中许多仅仅是与Android构建系统集成的构建系统补丁,我希望对这些开放。有关构建此代码的说明,可在PulseAudio Android Wiki页面上找到。

对于将来的工作,在PulseAudio之上编写一个包装程序以显示AudioFlinger音频和策略API会很有趣-这基本上可以让我们运行PulseAudio作为AudioFlinger的替代品。此外,使用Android特定的基础架构(例如Binder(用于IPC)和ashmem(用于将音频块作为共享内存段进行传输)可以带来潜在的性能优势,我们使用标准Linux SHM机制在台式机上支持这些功能。在Android上不可用)。

如果您是对此工作感兴趣的OEM,可以与我们联系-有关详细信息,请访问Collabora网站。

我希望这对在那里的某些人有用!

你可能感兴趣的:(PulseAudio进阶)