如何编程实现FFmpeg每10ms回调捕获一次原始音频采样
FFmpeg+PulseAudio实现10ms粒度原始音频采样捕获方案
核心逻辑
不需要改动现有Opus编码、WebRTC传输逻辑,只需要调整PulseAudio采集层的块大小配置,让FFmpeg每次读取采集数据时仅返回10ms长度的原始PCM采样,再通过读包拦截逐块触发业务回调即可。
具体实现步骤
- 先计算10ms采样对应的固定块大小:你当前使用48kHz采样率,单通道10ms对应采样点数为
48000 * 0.01 = 480个样本。如果是s16le采样格式(PulseAudio默认采集格式),单通道10ms块大小为480 * 2 = 960字节,双声道为480 * 2 * 2 = 1920字节,这个值是配置采集粒度的核心依据。 - 打开PulseAudio采集设备时覆盖默认块配置:FFmpeg的PulseAudio采集模块默认会按后续编码器的帧长(你当前配置的120ms)设置采集块大小,所以默认读包只能拿到120ms长度的采样块。需要在调用
avformat_open_input时通过字典传入自定义采集参数,强制底层按10ms粒度返回数据:
AVDictionary *pulse_opts = NULL; // 按你的声道配置填对应值,示例为双声道s16le的10ms块大小 av_dict_set(&pulse_opts, "fragment_size", "1920", 0); // 设置总采集缓冲为100ms,避免低粒度下出现采样溢出丢包 av_dict_set(&pulse_opts, "pulse_buffer_size", "19200", 0); // 显式指定采集参数,禁止FFmpeg自动做重采样引入额外延迟 av_dict_set(&pulse_opts, "sample_rate", "48000", 0); av_dict_set(&pulse_opts, "channels", "2", 0); av_dict_set(&pulse_opts, "sample_fmt", "s16", 0); AVFormatContext *pulse_fmt_ctx = NULL; int ret = avformat_open_input(&pulse_fmt_ctx, "pulse:对应采集设备名", av_find_input_format("pulse"), &pulse_opts); av_dict_free(&pulse_opts);
- 循环读包拦截原始采样:在独立的采集线程中循环调用
av_read_frame读取采集包,每读到一个AVPacket就是对应10ms长度的原始PCM数据,在这里直接触发你的10ms业务回调,把裸采样数据抛给上层逻辑即可。如果偶尔读到的包大小和计算的10ms固定值有偏差,用一个简单的环形缓冲做攒齐处理,凑够固定长度再触发回调,避免PulseAudio时序抖动导致的采样块长度不稳定。 - 兼容现有Opus编码逻辑:拿到10ms粒度的采样后,如果还是要走你当前配置的120ms帧长Opus编码,只需要把连续12个10ms采样块拼接起来,就是刚好匹配编码器要求的输入帧长度,不需要修改任何现有编码器参数、WebRTC传输逻辑。
注意事项
- 不要把采集读包逻辑放在业务主线程,要单独开高优先级线程跑
av_read_frame循环,避免其他业务逻辑阻塞采集调用,导致PulseAudio缓冲积压,出现回调间隔不准的问题。 - 不需要修改系统级PulseAudio配置,所有采集粒度参数都可以通过FFmpeg的Pulse采集模块参数在代码内完成设置。
- 不需要调整现有WebRTC编码器工厂逻辑,10ms粒度的原始采样可以直接走你已经验证通的原始音视频传输通道发送。
内容的提问来源于stack exchange,提问作者Muhammad Zaid Ali
相关产品推荐
相关产品推荐

