KunSpace
返回博客

ALSA 音频缓冲区:period_size / period_count 怎么算延迟与 CPU

2026年7月7日
2 次阅读

读完了?别空手走啊 😏

读都读完了,不点个赞说不过去吧

写评论

赞同、吐槽、提问都行,评论区开放

这篇写什么?

配置麦克风采集时,常会看到类似:

mic_cfg.period_size  = 960;
mic_cfg.period_count = 8;

很多人只记住「数字越大越稳」,却说不清:延迟多少、能扛多久卡顿、回调一秒几次、和 CPU 什么关系

本文用 16 kHz、4 通道、16bit 这组常见语音参数,把 ALSA(以及同类 PCM 驱动)里的 帧 → period → 环形缓冲 一次算清楚,并补上调参时要用到的专业概念。

一句话:

period_size = 一周期多少帧(决定延迟与回调频率)
period_count = 环形缓冲有几段(决定能扛多久处理抖动)
总缓冲时间 ≈ period_size × period_count / sample_rate

ALSA 环形缓冲与 period 示意


一、先统一几个「单位」

名词 含义
采样点(sample) 某一个声道上的一个数值(如 16bit 整数)
帧(frame) 同一时刻所有声道各取一个采样点。N 通道 → 1 帧里有 N 个采样点
period(周期) 驱动按「一块」通知上层的数据量,单位通常是 帧数
环形缓冲(ring buffer) 由多个 period 首尾相接组成;硬件写、软件读(或反过来),指针绕圈前进

4 通道、16bit(2 字节/采样)

1 帧 = 4 通道 × 2 字节 = 8 字节

这就是后面所有「字节数」换算的基础。

和「交错 / 非交错」的关系:交错(interleaved)时,一帧在内存里是 L R L R …ch0 ch1 ch2 ch3 紧挨着;算字节时仍是 帧数 × 通道 × 每采样字节


二、period_size:一周期多少帧

mic_cfg.period_size = 960;   // 单位:帧

含义:

  • 每个 period 包含 960 帧 音频
  • 不是「960 个采样点」——若误当成采样点,4 通道时会算错 4 倍

一周期有多少字节?

字节/周期 = period_size × channels × bytes_per_sample
         = 960 × 4 × 2
         = 7680 字节

单周期延迟(最常用公式)

单周期延迟 = period_size / sample_rate
           = 960 / 16000
           = 0.06 s = 60 ms

直观理解:硬件大约每攒满 60 ms 的数据,就「敲一下门」——触发一次读回调 / 一次 period 就绪。

影响 说明
period 越小 延迟越低,实时性更好,但回调更勤,CPU 与调度开销↑
period 越大 延迟更高,回调更稀,CPU 更省,但手感/交互变钝

语音唤醒、通话类:几十毫秒级 period 很常见;乐器监听、低延迟监控有时会压到 5~15 ms(对驱动与线程调度要求更高)。


三、period_count:环形缓冲有几段

mic_cfg.period_count = 8;

含义:整个环形缓冲区由 8 个 period 串起来。

总缓冲大小(字节)

总字节 = period_size × period_count × frame_size
       = 960 × 8 × 8
       = 61440 字节 ≈ 60 KB

其中 frame_size = channels × bytes_per_sample = 8

总缓冲时间(能扛多久抖动)

总缓冲时间 = (period_size × period_count) / sample_rate
           = (960 × 8) / 16000
           = 480 ms

直观理解:软件偶尔忙了几十~一两百毫秒,只要读指针还没被写指针追上,就 不容易 underrun/overrun;缓冲越大越「扛得住」,但:

  • 捕获(录音)路径上,过大缓冲会让「听到/算法看到」的数据更「晚」
  • 播放路径上,过大缓冲会增加可感延迟

period_count 常见取值 2~8(再大往往收益递减,还浪费内存、拉长最坏延迟)。


四、回调频率与 CPU

回调频率 ≈ sample_rate / period_size
         = 16000 / 960
         ≈ 16.67 Hz

即大约 每秒 16~17 次 进入 audioMicDevCallback(或等价读线程唤醒)。

频率 典型观感
很低(如 <10 Hz) CPU 省,但单次处理块大,抖动时更容易积压
适中(约 10~50 Hz) 语音采集常用区间
很高(如 >100~200 Hz) 低延迟场景常见,但线程唤醒、锁、拷贝成本明显上升

注意:CPU 开销不只看频率,还看 每次回调里做了什么(拷贝、重采样、AEC、FFT)。频率低但每次做重算法,同样会打满核。


五、当前设置(960 / 8 @ 16 kHz)速查表

指标 数值 评价(语音场景)
单周期延迟 60 ms 对唤醒/ASR 通常可接受
总缓冲 480 ms 能应对处理波动与短暂调度延迟
回调频率 ≈16.7 Hz 不会过高,CPU 友好
一周期数据 7680 B 便于按块做算法
总缓冲 ≈60 KB 内存占用很小

一句话:延迟适中、缓冲够用、回调不疯——适合多数 16 kHz 多麦采集,而不是极限低延迟监听。


六、再补几条专业概念(调参时很有用)

1)延迟其实有好几层

不要只看 period_size / fs

层级 常见内容
硬件 / DMA 驱动内部 FIFO、DMA period
ALSA 应用缓冲 period_size × period_count
框架 AudioFlinger / TinyALSA / 自研 ring
算法 AEC 参考对齐、FFT 窗、VAD 平滑

端到端延迟 ≈ 以上之和。只改 period_size 却感觉「差不多」,往往是别层更大。

2)xrun:overrun 与 underrun

方向 名称 含义
录音(capture) overrun 写太快 / 读太慢,旧数据被盖掉 → 丢样、咔哒、算法断续
播放(playback) underrun 读太快 / 写太慢,缓冲空了 → 爆音、静音空洞

增大 period_count(总缓冲)通常能降低 xrun 概率;减小 period_size 会提高实时性,但给软件的「反应时间」更短,xrun 风险上升。

3)为什么 period 常选「整毫秒 / 对齐」的数?

例如 16 kHz 下:

period_size 时间
160 10 ms
320 20 ms
480 30 ms
960 60 ms

很多算法按 10/20 ms 一帧设计;960 正好是 3×3206×160,方便按块切分,也方便和 48 kHz 侧的 2880 帧(60 ms)做时间对齐。

4)和「块大小(blocksize)」的关系

在部分 SDK / 引擎里叫 framesPerBufferblockSize,本质同类:

时间块 = blockSize / sample_rate

ALSA 再多一层:period_count 决定 环形里有多少个这样的块

5)调参经验口诀

先定采样率与通道(业务决定)
→ 再定可接受延迟(period_size)
→ 再在实测 xrun / CPU 之间调 period_count
→ 用长时间拷机验证,而不是只听几秒

经验区间(语音 16 kHz):

  • 延迟敏感:period ≈ 10~20 ms,count 4~8
  • 稳妥唤醒/降噪:period ≈ 20~60 ms,count 4~8(本文 60 ms × 8 属于偏稳妥)
  • 若经常 overrun:先查回调是否在做重活 / 是否被同核抢占,再考虑加大 count 或略加大 period

6)和 Android Audio HAL 的对应直觉

上层常看到的 bufferSizeInFramesperiod_size(tinyalsa)等,最终仍落到「多少帧一块、几块围成环」。分析卡顿时:既要看 单块延迟,也要看 环有多深,还要结合 atrace 看线程 CPU——回调线程是否经常跑满、是否被别的线程卡住。


七、快速自算模板

把你的三个数填进去即可:

fs      = 16000          // 采样率
ch      = 4
bps     = 2              // 16bit
period  = 960            // 帧
count   = 8

frame_bytes   = ch * bps
period_bytes  = period * frame_bytes
buffer_bytes  = period * count * frame_bytes
latency_ms    = 1000.0 * period / fs
buffer_ms     = 1000.0 * period * count / fs
callback_hz   = fs / (double)period

换 48 kHz、2 通道、32bit 时,同一套公式照抄,只改三个输入。


八、小结

参数 主要决定
period_size 单次通知的数据量 → 延迟 + 回调频率
period_count 环有多深 → 抗抖动能力 + 最坏缓冲延迟
二者乘积 / fs 总可缓冲时间

当前 960 / 8 @ 16 kHz、4ch 16bit:约 60 ms 一拍、480 ms 总缓冲、16.7 Hz 回调——对语音采集是一套平衡点,而不是极限低延迟配置。

调参时记住:小 period 换实时,大 count 换稳定;出问题先分清是 延迟太大 还是 xrun / CPU 忙,再用公式定量,而不是只改一个「感觉」。


相关阅读

评论

还没有评论,来做第一个吧

赞同、吐槽、提问都行,评论区开放