背景
在网上找了很多资料。大多都不全。经过摸索,终于搞定,特记录分享下。
音量调节功能一般在音频解码后加音量处理。如果有重采样功能需要在之前处理。
音频解码-->音量调节-->重采样
正文
用到的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还是挺有意思的。