ffmpeg编程-音量调节

背景

在网上找了很多资料。大多都不全。经过摸索,终于搞定,特记录分享下。

音量调节功能一般在音频解码后加音量处理。如果有重采样功能需要在之前处理。

音频解码-->音量调节-->重采样

正文

用到的ffmpeg相关结构。avfilter是很强大的工具。这里用到了volume filter用来处理音量大小。

AVFilterGraph
AVFilterContext

音量处理管理结构。如果需要调节音量的时候,重新创建个管理即可。

/**
 * 管理音量处理结构体
 **/
typedef struct {
    int                         channels;
    int                         sample_rate;
    int                         sample_fmt;

    float                       volume;
    
    AVFilterGraph              *graph;
    AVFilterGraph              *in;
    AVFilterGraph              *out;
} volume_mgr_t;

具体实现流程 

主要函数,主要用来初始化volume filter。


int init_volume_filter(AVFilterGraph **pGraph, 
                       AVFilterContext **src,     
                       AVFilterContext **out, 
                       float volume,
                       int channels,
                       int sample_rate,
                       enum AVSampleformat sample_fmt)
{
    AVFilterGraph               *filter_graph;
    AVFilterContext             *abuffer_ctx;
    const AVFilter              *abuffer;
    AVFilterContext             *volume_ctx;
    const AVFilter              *volume;
    AVFilterContext             *aformat_ctx;
    const AVFilter              *aformat;
    AVFilterContext             *abuffersink_ctx;
    const AVFilter              *abuffersink;

    AVDictionary                *options_dict = NULL;
    char                         options_str[1024] = {0};
    char                         ch_layout[64] = {0};
    char                         value_volume[32] = {0};

    int                          err = 0;

    //1.初始化AVFilterGraph
    filter_graph = avfilter_graph_alloc();
    if (!filter_graph) {
        return -1;
    }

    //2.获取abuffer用于接收输入端
    abuffer = avfilter_get_by_name("abuffer");
    if (!abuffer) {
        return -1;
    }

    abuffer_ctx = avfilter_graph_alloc_filter(filter_graph, abuffer, "src");
    if (!abuffer_ctx) {
        return -1;
    }

    //设置filter options
    av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, av_get_default_channel_layout(channels));
    av_opt_set(abuffer_ctx, "channel_layout", ch_layout, AV_OPT_SEARCH_CHILDREN);
    av_opt_set(abuffer_ctx, "sample_fmt", av_get_sample_fmt_name(sample_fmt), AV_OPT_SEARCH_CHILDREN);
    av_opt_set(abuffer_ctx, "time_base", (AVRational){1, sample_rate}, AV_OPT_SEARCH_CHILDREN);
    av_opt_set(abuffer_ctx, "sample_rate", sample_rate, AV_OPT_SEARCH_CHILDREN);
    
    err = avfilter_init_str(abuffer_ctx, NULL);
    if (err < 0) {
        return -1;
    }

    //3.初始化volume filter
    volume_ctx = avfilter_graph_alloc_filter(filter_graph, volume, "volume");
    if (!volume_ctx) {
        return -1;
    }

    snprintf(value_volume, 32, "%.2f", volume);
    av_dict_set(&options_dict, "volume", value_volume, 0);

    err = avfilter_init_dict(volume_ctx, &options_dict);
    av_dict_free(&options_dict);
    if (err < 0) {
        return -1;
    }

    //4.获取aformat
    aformat = avfilter_get_by_name("aformat");
    if (!aformat) {
        return -1;
    }

    aformat_ctx = avfilter_graph_alloc_filter(filter_graph, aformat, "aformat");
    if (!aformat_ctx) {
        return -1;
    }

    snprintf(options_str, sizeof(options_str),
            "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%"PRIx64,
            av_get_sample_fmt_name(sample_fmt), sample_rate,
            av_get_default_channel_layout(channels));
    err = avfilter_init_str(aformat_ctx, options_str);
    if (err < 0) {
        return -1;
    }

    //5.获取abuffer sink
    abuffersink = avfilter_get_by_name("abuffersink");
    if (!abuffersink) {
        return -1;
    }

    abuffersink_ctx = avfilter_graph_alloc_filter(filter_graph, abuffersink, "sink");
    if (!abuffersink_ctx) {
        return -1;
    }

    err = avfilter_init_str(abuffersink_ctx, NULL);
    if (err < 0) {
        return -1;
    }

    //6.把filter全部连接起来
    err = avfilter_link(abuffer_ctx, 0, volume_ctx, 0);
    if (err >= 0)
        err = avfilter_link(volume_ctx, 0, aformat_ctx, 0);
    if (err >= 0)
        err = avfilter_link(aformat_ctx, 0, abuffersink_ctx, 0);
    if (err < 0) {
        return -1;
    }

    //7.配置graph,然后就搞定了。流程有点长
    err = avfilter_graph_config(filter_graph, NULL);
    if (err < 0) {
        return -1;
    }

    //8.输出
    *pGraph = filter_graph;
    *src = abuffer_ctx;
    *out = abuffersink_ctx;

    return 0;
}

剩下的就比较简单了,只要初始化volume_mgr_t,直接使用就可以了。如果需要变音量,重新初始化一个。

解码出的frame调用处理音量逻辑

/**
 * @brief 处理音量,in/out AVframe
 * 
 */
int audio_volume_filter_handle(volume_mgr_t *volume_filter, AVFrame *frame)
{
    if (!volume_filter || !frame) {
        return -1;
    }

    int ret = av_buffersrc_add_frame(volume_filter->in, frame);
    if (ret < 0) {
        return -1;
    }

    ret = av_buffersink_get_frame(volume_filter->out, frame);
    if (ret < 0) {
        return -1;
    }

    return 0;
}

audio 音量调整filter还是挺有意思的。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前进的蜗牛啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值