什么是 Android 音频延迟 10毫秒问题?
对超过4,238种不同Android手机型号/版本进行了音频延迟测试,数据表明Android在音频延迟问题上得到了很大改进,但随着当前媒体技术的发展,Android的这些优化还远远不够。迄今为止,Android N在音频延迟方面有任何改进,音频的延迟问题仍然制约着Android音频应用的发展。
低延迟音频环路延迟的定义
音频输入(时间)+ 音频处理(时间)+ 音频输出(时间)= 10 毫秒或更短。
这是能够让用户感觉到自然、舒适所必需的最高限制延迟。即使延迟增加几毫秒,用户也会感到不舒服,并且通常用户也不知道为什么。自 2007 年 iPhone 推出以来,iOS 的延迟一直很低,所以IOS音频应用的用户的体验要比Android好得多。
截至2016年6月,最佳官方Android音频延迟为:
手机型号 | Android版本 | Buffer size | 环路延迟 |
Nexus 9 | 6.0.0 | 128 | 15 |
Nexus 6P | 6.0.0 | 192 | 18 |
常见误区
这是硬件问题
这是Linux音频问题
这是ALSA驱动问题
虽然每个地方都存在小问题,但上述问题都不是不可克服的或致命的,甚至是完全可以接受的。
问题的关键在于,AudioTrack存在架构上的问题,这个问题从Android一开始就已经存在,早在Google收购Android之前。这个问题虽然无法避免,但是我们完全可以通过一些手段尽可能正确的处理音频,保证音频延迟。
延迟产生的过程
引用自Superpowered
模拟音频输入 延迟:0 | 可能有几个不同的模拟信号组件,例如内置麦克风的前置放大器。在这种情况下,这些模拟信号组件可以被视为“零延迟”,因为它们的真实延迟通常低于 1 毫秒。 |
模数转换器(ADC) 延迟:1 毫秒 | 音频芯片以既定的时间间隔测量传入的音频流,并将每个测量值转换为一个数字。这个既定的时间间隔称为采样率,以 Hz 为单位。48000 Hz 是 Android 和 iOS 设备上大多数音频芯片的原生采样率,这意味着音频流每秒被采样 48000 次。 由于 ADC 实现通常在内部包含一个过采样滤波器,因此这个过程将产生接近1毫秒的延迟。 经过ADC的处理模拟音频变成数字信号。数字音频无法在系统中逐个传输,而是以块的形式,称为“缓冲区”或“周期”。 |
内存总线 延迟:1-6 毫秒 | 音频芯片有几个任务。它处理 ADC 和 DAC,在多个输入和输出之间切换或混合、调整音量等。它还将离散数字音频样本“分组”到缓冲区中,并负责处理这些缓冲区数据传输到操作系统。 音频芯片通过 USB、PCI、Firewire 等总线连接到 CPU。每条总线都有自己的传输延迟,具体取决于其内部缓冲区大小和缓冲区数量。此处的延迟范围通常从 1 毫秒(设备内部的音频芯片)到 6 毫秒(性能一般的USB总线)。 |
音频驱动程序 延迟:一个或多个周期 | 音频驱动程序把音频芯片产生的音频传输到缓冲区当中,缓冲区起到了平滑数据传输速率、避免抖动的作用,因此自然会增加一些延迟。 Android底层基于Linux实现,因此大多数 Android 设备使用了Linux 音频驱动系统ALSA(高级 Linux 声音架构)。ALSA会有序的处理缓冲区数据:
例如:
常见的周期数是 2,但有些系统可能会更高。 |
音频硬件抽象层(HAL) 延迟:0(最佳情况) | HAL是Android 媒体服务器和 Linux 音频驱动程序之间的中间件。HAL由OEM厂商负责实现,这些厂商可以自由实现HAL逻辑。媒体服务器加载 HAL时会要求使用可选的首选参数(例如采样率、缓冲区大小或音频效果)创建输入或输出流。 注意:HAL是否根据参数执行我们无法知道,所以媒体服务器必须“适应”HAL。 典型的 HAL 实现是 tinyALSA,它用于与 ALSA 音频驱动程序通信。一些厂商会在这里实现一些重要的功能,并且是闭源的。 糟糕的HAL会给音频链路增加不必要的延迟和 CPU 负载。一个好的HAL 实现不应增加任何延迟。 |
Audio Flinger 延迟:1 段时间(最佳情况) | Android 媒体服务器由两个服务组成:
Audio Flinger 创建一个 RecordThread,它充当应用程序和音频驱动程序之间的中间人。基本工作流程如下:
Audio Flinger 有一个“快速混音器”的实现(如果厂商有实现的话)。如果应用程序使用硬件原生支持的采样率的缓冲区大小,则系统将会跳过重采样和无必要的混合处理。 RecordThread 使用“push”的工作方式,与音频驱动程序没有任何严格同步,因为它需要预测合适应该运行,这会额外增加延迟。低延迟系统应该使用“pull”方法,音频驱动程序驱动整个音频链路的运行,而不是预测什么时候应该从缓冲区读取数据。很显然,Android系统早期设计并没有考虑到对低延迟的支持。 |
Binder 延迟:0 | Android进程间通讯的重要组件,用于在 Audio Flinger 和应用程序之间传输音频数据。 |
AudioRecord 延迟:0+ samples | 这里处于应用程序的进程中。AudioRecord 实现了音频输入客户端。 AudioRecord线程定期从 Audio Flinger 获取新缓冲区,使用 Audio Flinger 中描述的“推送”原理。如果开发人员将其设置为仅使用一个缓冲区,则不会增加音频路径的延迟。 |
User Application 延迟:超过 1 个周期,通常接近 2 个(最佳情况) | 最后,音频输入的目的地,即用户应用程序。 由于输入和输出线程不同,用户应用程序必须在线程之间实现环形缓冲区。它的大小最小为 2 个周期(1 个用于音频输入,1 个用于音频输出),但编写得不好的应用程序通常使用更多的缓冲区以解决CPU瓶颈。 |
AudioTrack 延迟:0+ samples | AudioTrack用于应用程序的音频输出。它运行一个线程定期将下一个音频缓冲区发送到 Audio Flinger。在 Android 4.4.4 之后,AudioTrack不会增加额外延迟,因为它可以设置为只使用一个缓冲区。 |
Binder 延迟:0 | 与音频输入相同。 |
Audio Flinger 延迟:1 段时间(最佳情况) | 创建一个 PlaybackThread,它与音频输入中描述的 RecordThread 相反。 |
音频硬件抽象层(HAL) 延迟:0(最佳情况) | 与音频输入相同。 |
音频驱动程序 延迟:一个或多个周期 | 音频驱动程序中的音频输出与音频输入的工作方式相同,并且也使用环形缓冲区。 |
内存总线 延迟:1-6 毫秒 | 与音频输入总线传输类似,此处的延迟范围通常为 1 ms 到 6 ms。 |
模数转换器(ADC) 延迟:1 毫秒 | 与 ADC 相反,这里会将数字信号转为模拟信号。根据经验假设 DAC 的延迟为 1 ms。 |
模拟音频输出 延迟:0 | DAC 的输出信号是模拟音频,但它需要额外的组件来驱动连接的设备,例如耳机。类似于模拟音频输入,模拟组件可以被认为是“零延迟”。 |
音频延迟和Buffer Size的关系
引用自Google Developers
了解音频延迟产生的原因后,我们后续将有针对性的给出优化方案。
技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。
私信领取相关资料
推荐阅读:
觉得不错,点个在看呗~