AUDIO latency

音频延迟时间

视频

音频延迟时间:缓冲区大小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 设备上经过任何路径的音频延迟时间。 不过,您可使用下列硬件功能标记了解设备是否能为延迟时间提供任何保证:

报告这些标记的标准在 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,但反之则否则。

不做有关音频性能的假设

请注意有助于避免延迟时间问题的下列假设:

  • 不要假设移动设备中使用的扬声器和麦克风一般拥有良好的音效。 它们的体积较小,一般音效较差,因此增长信号处理来提升音质。 此信号处理会引发延迟。
  • 不要假设您的输入和输出回调是同步的。对于同步输入和输出,将为每一侧使用单独的缓冲区队列完成处理程序。 即便两侧均采用相同的采样率,也没法保证这些回调的相对顺序或音频时钟的同步。 您的应用应当在适当同步缓冲区的状况下缓冲数据。
  • 不要假设实际采样率与名义采样率彻底一致。例如,若是名义采样率是 48,000 Hz,则正常状况下,音频时钟会使用与操做系统 CLOCK_MONOTONIC 稍微不一样的采样率计时。 这是由于音频和系统时钟由不一样的晶体制成。
  • 不要假设实际回放样本率与实际捕获采样率彻底一致,特别是端点在单独的路径上时。 例如,若是您正以 48,000 Hz 的名义采样率从设备上的麦克风捕获数据,并以 48,000 Hz 的名义采样率在 USB 上播放音频,实际采样率极可能会彼此稍有不一样。

潜在独立的音频时钟的一个结果是须要异步采样率转换。 异步采样率转换的简单(尽管音频质量不理想)方法是根据须要在接近过零点的位置重复或减小样本。 也能够进行更复杂的转换。

最大程度减小输入延迟时间


此部分会提供建议,帮助您在使用内置麦克风或外部耳麦麦克风录音时减小音频输入延迟时间。

  • 若是您的应用要监控输入,建议您的用户使用耳麦(例如,经过在第一次运行时显示最好使用耳机屏幕)。 请注意,仅使用耳麦没法保证尽量最低的延迟时间。 您可能须要执行其余步骤从音频路径中移除任何不须要的信号处理(例如,在录音时经过使用 VOICE_RECOGNITION 预设值)。
  • 准备好处理由 PROPERTY_OUTPUT_SAMPLE_RATE 的 getProperty(String) 报告的名义采样率 44,100 和 48,000 Hz。 也有其余采样率,但不多见。
  • 准备好处理由 PROPERTY_OUTPUT_FRAMES_PER_BUFFER 的 getProperty(String) 报告的缓冲区大小。 典型的缓冲区大小包括 9六、12八、160、19二、240、256 或 512 帧,但也有其余值。

最大程度减小输出延迟时间


建立您的音频播放器时使用最佳采样率

要得到最低延迟时间,您必须提供与设备的最佳采样率和缓冲区大小匹配的音频数据。 如需了解详细信息,请参阅面向更低的延迟时间设计

以下列代码示例中所示,在 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 缓冲区大小会有所不一样。

避免添加涉及信号处理的输出接口

快速混合器仅支持下列这些接口:

  • SL_IID_ANDROIDSIMPLEBUFFERQUEUE
  • SL_IID_VOLUME
  • SL_IID_MUTESOLO

不支持如下这些接口,由于它们涉及信号处理,且会致使快速音轨的请求被拒绝:

  • SL_IID_BASSBOOST
  • SL_IID_EFFECTSEND
  • SL_IID_ENVIRONMENTALREVERB
  • SL_IID_EQUALIZER
  • SL_IID_PLAYBACKRATE
  • SL_IID_PRESETREVERB
  • SL_IID_VIRTUALIZER
  • SL_IID_ANDROIDEFFECT
  • SL_IID_ANDROIDEFFECTSEND

建立播放器时,请确保仅添加快速接口,如如下示例所示:

const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };

验证您正在使用低延迟时间音轨

完成下列这些步骤以验证您是否已成功得到低延迟时间音轨:

    1. 启动您的应用,而后运行下列命令:
adb shell ps | grep your_app_name
    1. 记下您应用的进程 ID。
    2. 如今,从您的应用播放一些音频。您大约有三秒钟的时间能够从终端运行下列命令:
adb shell dumpsys media.audio_flinger
  1. 扫描您的进程 ID。若是您在 Name 列看到 F,表示它在低延迟时间音轨上(F 表明快速音轨)。

最大程度减小预热延迟时间


第一次将音频数据加入队列时,设备音频电路须要较短、但仍十分重要的一段时间来预热。 要避免这种预热延迟时间,您能够将包含无声的音频数据缓冲区加入队列,如如下代码示例所示:

#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() 方法中中止输出。另外,请考虑在用户无活动的一段时间后暂停无声输出。

更多信息


  1. 适合应用开发者的音频延迟时间
  2. 影响音频延迟时间的因素
  3. 测量音频延迟时间
  4. 音频预热
  5. 延迟时间(音频)
  6. 往返延迟时间
相关文章
相关标签/搜索