音频延迟时间:缓冲区大小html
在 Android 上打造出色的多媒体体验java
延迟时间是指信号在系统中传输所需的时间。下面是与音频应用相关的常见类型的延迟时间:android
往返延迟时间是指输入延迟时间、应用处理时间和输出延迟时间的总和。git
本页面将介绍如何在开发您的音频应用时保证低输入和输出延迟时间以及如何避免出现预热延迟时间。github
仅在使用 OpenSL ES™ API 的 Android 实现和 Android NDK 时,才支持低延迟时间音频。shell
很难单独测量音频输入和输出延迟时间,由于须要准确地了解第一个样本什么时候传送入音频路径(尽管可使用光检测电路和示波器完成)。 若是您了解往返音频延迟时间,则可使用通常经验法则:音频输入(和输出)延迟时间是通过无信号处理路径的往返音频延迟时间的一半。app
往返音频延迟时间根据设备型号和 Android 版本号的不一样而大不相同。 您能够经过阅读已发布的测量值大略了解 Nexus 设备的往返延迟时间。异步
您能够经过建立应用测量往返音频延迟时间,该应用可以生成音频信号、侦听此音频信号和测量发送与接收信号之间的时间。或者,您也能够安装此延迟时间测试应用。 此应用使用拉森测试执行往返延迟时间测试。 您也能够查看延迟时间测试应用的源代码。ide
因为最低延迟时间是在信号处理最少的音频路径上得到的,您可能还想使用回环音频适配器,它让测试可以经过耳麦链接器运行。oop
Android 兼容性定义文档 (CDD) 枚举了兼容 Android 设备的硬件和软件要求。请参阅 Android 兼容性了解与总体兼容性计划有关的详细信息,参阅 CDD 了解实际的 CDD 文档。
在 CDD 中,往返延迟时间被指定为 20 毫秒或更低(而乐师一般须要 10 毫秒)。 这是由于 20 毫秒能够实现一些重要的用例。
当前没有 API 能够在运行时肯定 Android 设备上经过任何路径的音频延迟时间。 不过,您可使用下列硬件功能标记了解设备是否能为延迟时间提供任何保证:
android.hardware.audio.low_latency
指示 45 毫秒或更短的持续输出延迟时间。android.hardware.audio.pro
指示 20 毫秒或更短的持续往返延迟时间。报告这些标记的标准在 CDD 的 5.6 音频延迟时间和 5.10 专业音频部分中定义。
如下是如何在 Java 中检查这些功能:
boolean hasLowLatencyFeature =
getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
boolean hasProFeature =
getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO);
对于各音频功能的关系,android.hardware.audio.low_latency
功能是 android.hardware.audio.pro
的先决条件。 设备能够实现 android.hardware.audio.low_latency
而不能实现 android.hardware.audio.pro
,但反之则否则。
请注意有助于避免延迟时间问题的下列假设:
CLOCK_MONOTONIC
稍微不一样的采样率计时。 这是由于音频和系统时钟由不一样的晶体制成。潜在独立的音频时钟的一个结果是须要异步采样率转换。 异步采样率转换的简单(尽管音频质量不理想)方法是根据须要在接近过零点的位置重复或减小样本。 也能够进行更复杂的转换。
此部分会提供建议,帮助您在使用内置麦克风或外部耳麦麦克风录音时减小音频输入延迟时间。
VOICE_RECOGNITION
预设值)。要得到最低延迟时间,您必须提供与设备的最佳采样率和缓冲区大小匹配的音频数据。 如需了解详细信息,请参阅面向更低的延迟时间设计。
以下列代码示例中所示,在 Java 中,您能够从 AudioManager 得到最佳采样率:
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int sampleRate = Integer.parseInt(sampleRateStr);
if (sampleRate == 0) sampleRate = 44100; // Use a default value if property not found
知道最佳采样率后,您能够在使用 OpenSL ES 建立您的播放器时提供具体数值:
// create buffer queue audio player
void Java_com_example_audio_generatetone_MainActivity_createBufferQueueAudioPlayer
(JNIEnv* env, jclass clazz, jint sampleRate, jint framesPerBuffer)
{
...
// specify the audio source format
SLDataFormat_PCM format_pcm;
format_pcm.numChannels = 2;
format_pcm.samplesPerSec = (SLuint32) sampleRate * 1000;
...
}
注:samplesPerSec
指的是每一个通道的采样率,单位为毫赫(1 Hz = 1000 mHz)。
您能够经过 AudioManager API 采用与得到最佳采样率类似的方式得到最佳缓冲区大小:
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int framesPerBufferInt = Integer.parseInt(framesPerBuffer);
if (framesPerBufferInt == 0) framesPerBufferInt = 256; // Use default
PROPERTY_OUTPUT_FRAMES_PER_BUFFER
属性表示 HAL(硬件抽象层)缓冲区能够容纳的音频帧数量。 您应构建音频缓冲区,使其能够容纳这个数量的确切倍数。 若是使用准确数量的音频帧,会按期出现回调,这将减小抖动。
使用 API 而不是硬编码值来肯定缓冲区大小相当重要,由于在不一样的设备和 Android 版本号中,HAL 缓冲区大小会有所不一样。
快速混合器仅支持下列这些接口:
不支持如下这些接口,由于它们涉及信号处理,且会致使快速音轨的请求被拒绝:
建立播放器时,请确保仅添加快速接口,如如下示例所示:
const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
完成下列这些步骤以验证您是否已成功得到低延迟时间音轨:
adb shell ps | grep your_app_name
adb shell dumpsys media.audio_flinger
第一次将音频数据加入队列时,设备音频电路须要较短、但仍十分重要的一段时间来预热。 要避免这种预热延迟时间,您能够将包含无声的音频数据缓冲区加入队列,如如下代码示例所示:
#define CHANNELS 1
static short* silenceBuffer;
int numSamples = frames * CHANNELS;
silenceBuffer = malloc(sizeof(*silenceBuffer) * numSamples);
for (i = 0; i < numSamples; i++) {
silenceBuffer[i] = 0;
}
须要生成音频时,您能够将包含真实音频数据的缓冲区加入队列。
注:持续输出音频将消耗大量的电量。请确保您在 onPause() 方法中中止输出。另外,请考虑在用户无活动的一段时间后暂停无声输出。