You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Linux嵌入式系统中用FFmpeg的AVFilter API调节音频音量

嵌入式Linux音频播放器添加音量调节方案(基于FFmpeg AVFilter)

核心方案概述

使用FFmpeg的volume滤镜实现音量调节,将其插入到解码后的音频帧与aformat重采样滤镜之间的滤镜链中,处理流程为:
解码帧 → volume滤镜(音量调节) → aformat滤镜(重采样/格式转换) → ALSA播放

具体修改步骤

1. 修改滤镜链配置

在初始化filter graph时,把volume滤镜加入到滤镜链路里。原来的链路是buffersrc → aformat → buffersink,现在调整为buffersrc → volume → aformat → buffersink。

固定音量配置示例

// 替换原滤镜链定义,加入volume滤镜,这里设置音量为原音量的0.5倍,范围0~2,1为原音量
char filter_desc[256];
snprintf(filter_desc, sizeof(filter_desc),
         "volume=volume=%.2f,aformat=sample_fmts=%s:sample_rates=%d:channel_layouts=stereo",
         0.5f,
         av_get_sample_fmt_name(AV_SAMPLE_FMT_S16),
         32000);

动态音量调节配置

如果需要播放过程中实时调整音量,可后续通过avfilter_graph_send_command修改参数:

// 示例:将音量调整为原音量的1.2倍
avfilter_graph_send_command(graph, "volume", "volume", "1.2", NULL, 0, 0);

2. 调整Filter Graph初始化逻辑

如果之前是手动创建滤镜实例,需要新增volume滤镜的初始化与链路连接步骤:

AVFilterContext *volume_ctx;
// 查找volume滤镜
const AVFilter *volume_filter = avfilter_get_by_name("volume");
// 创建volume滤镜实例,初始音量设为1.0(原音量)
if (avfilter_graph_create_filter(&volume_ctx, volume_filter, "volume", "volume=1.0", NULL, graph) < 0) {
    fprintf(stderr, "Failed to create volume filter\n");
    return -1;
}
// 连接buffersrc到volume滤镜
if (avfilter_link(src, 0, volume_ctx, 0) < 0) {
    fprintf(stderr, "Failed to link buffersrc to volume\n");
    return -1;
}
// 连接volume滤镜到aformat滤镜
if (avfilter_link(volume_ctx, 0, aformat_ctx, 0) < 0) {
    fprintf(stderr, "Failed to link volume to aformat\n");
    return -1;
}
// 保留原aformat到buffersink的连接逻辑
if (avfilter_link(aformat_ctx, 0, sink, 0) < 0) {
    fprintf(stderr, "Failed to link aformat to buffersink\n");
    return -1;
}

3. 滤镜顺序的合理性说明

  • 必须将volume放在aformat之前:解码后的原始音频是fltp浮点格式,浮点格式处理音量调节精度更高,不会出现整数格式的截断失真;如果先转成S16再调音量,容易产生量化误差,导致音质损失。
  • 正确顺序:先处理音量(浮点帧),再做重采样/格式转换(转成ALSA需要的S16格式)。

4. 播放逻辑适配

原播放代码无需大幅修改,滤镜处理后的帧仍通过av_buffersink_get_frame获取,直接传给ALSA播放即可。

完整Filter Graph初始化示例片段

int init_filter_graph(AVFilterGraph **graph, AVFilterContext **src, AVFilterContext **sink) {
    *graph = avfilter_graph_alloc();
    if (!*graph) return -1;

    // 初始化buffersrc,匹配解码后的原始格式:44100Hz、立体声、fltp
    const AVFilter *buffersrc_filter = avfilter_get_by_name("abuffer");
    char src_args[256];
    snprintf(src_args, sizeof(src_args),
             "sample_rate=%d:sample_fmt=%s:channel_layout=stereo",
             44100, av_get_sample_fmt_name(AV_SAMPLE_FMT_FLTP));
    if (avfilter_graph_create_filter(src, buffersrc_filter, "src", src_args, NULL, *graph) < 0) {
        fprintf(stderr, "Failed to create buffersrc\n");
        return -1;
    }

    // 初始化volume滤镜
    AVFilterContext *volume_ctx;
    const AVFilter *volume_filter = avfilter_get_by_name("volume");
    if (avfilter_graph_create_filter(&volume_ctx, volume_filter, "volume", "volume=1.0", NULL, *graph) < 0) {
        fprintf(stderr, "Failed to create volume filter\n");
        return -1;
    }

    // 初始化aformat滤镜
    AVFilterContext *aformat_ctx;
    const AVFilter *aformat_filter = avfilter_get_by_name("aformat");
    char aformat_args[256];
    snprintf(aformat_args, sizeof(aformat_args),
             "sample_fmts=%s:sample_rates=%d:channel_layouts=stereo",
             av_get_sample_fmt_name(AV_SAMPLE_FMT_S16), 32000);
    if (avfilter_graph_create_filter(&aformat_ctx, aformat_filter, "aformat", aformat_args, NULL, *graph) < 0) {
        fprintf(stderr, "Failed to create aformat filter\n");
        return -1;
    }

    // 初始化buffersink
    const AVFilter *buffersink_filter = avfilter_get_by_name("abuffersink");
    if (avfilter_graph_create_filter(sink, buffersink_filter, "sink", NULL, NULL, *graph) < 0) {
        fprintf(stderr, "Failed to create buffersink\n");
        return -1;
    }

    // 连接完整滤镜链:src → volume → aformat → sink
    if (avfilter_link(*src, 0, volume_ctx, 0) < 0 ||
        avfilter_link(volume_ctx, 0, aformat_ctx, 0) < 0 ||
        avfilter_link(aformat_ctx, 0, *sink, 0) < 0) {
        fprintf(stderr, "Failed to link filters\n");
        return -1;
    }

    // 配置并启用滤镜图
    if (avfilter_graph_config(*graph, NULL) < 0) {
        fprintf(stderr, "Failed to configure filter graph\n");
        return -1;
    }

    return 0;
}

动态音量调节函数实现

// 实时调整音量,volume参数范围建议0~2,超过2易出现削波失真
void adjust_volume(AVFilterGraph *graph, float volume) {
    char vol_str[32];
    snprintf(vol_str, sizeof(vol_str), "%.2f", volume);
    int ret = avfilter_graph_send_command(graph, "volume", "volume", vol_str, NULL, 0, 0);
    if (ret < 0) {
        fprintf(stderr, "Failed to adjust volume: %s\n", av_err2str(ret));
    }
}

内容的提问来源于stack exchange,提问作者wangt13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 09:48:10