This article will use libavfilter API to convert original audio format. A program input original audio file: pcm format, sample rate 48000,2 channel, AV_SAMPLE_FMT_FLT format. Output: PCM format, the sampling rate 44100,1 channel, AV_SAMPLE_FMT_S16P format.
Program flow is as follows:
Above all FFmpeg built-in filter, set the parameters, then calls can be. All filter sequentially connected to form a filter chain.
abuffer filter: filter the beginning end of the chain, is provided for the input format of audio data, including channels, sampling rate, and other storage format.
volume filter: used to set the volume.
aformat filter: used to set the output format of the audio data, including channels, sampling rate, and other storage format.
abuffersink filter: filter chain terminating end for end of the entire filter chain.
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "libavutil/channel_layout.h"
#include "libavutil/md5.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#define INPUT_SAMPLERATE 48000
#define INPUT_FORMAT AV_SAMPLE_FMT_FLT
#define INPUT_CHANNEL_LAYOUT AV_CH_LAYOUT_STEREO
#define OUTPUT_SAMPLERATE 44100
#define OUTPUT_FORMAT AV_SAMPLE_FMT_S16P
#define OUTPUT_CHANNEL_LAYOUT AV_CH_LAYOUT_MONO
#define VOLUME_VAL 0.90
#define FRAME_SIZE 1024
static int init_filter_graph(AVFilterGraph **graph, AVFilterContext **src,
AVFilterContext **sink)
{
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];
char ch_layout[64];
int err;
/* 创建filtergraph,包含所有的filter */
filter_graph = avfilter_graph_alloc();
if (!filter_graph) {
fprintf(stderr, "Unable to create filter graph.\n");
return AVERROR(ENOMEM);
}
/* 创建abuffer filter,用于输入数据(链的起始点) */
abuffer = avfilter_get_by_name("abuffer");
if (!abuffer) {
fprintf(stderr, "Could not find the abuffer filter.\n");
return AVERROR_FILTER_NOT_FOUND;
}
abuffer_ctx = avfilter_graph_alloc_filter(filter_graph, abuffer, "src");
if (!abuffer_ctx) {
fprintf(stderr, "Could not allocate the abuffer instance.\n");
return AVERROR(ENOMEM);
}
/* 设置abuffer filter的参数,也就是输入音频数据的参数 */
av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, INPUT_CHANNEL_LAYOUT);
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(INPUT_FORMAT), AV_OPT_SEARCH_CHILDREN);
av_opt_set_q (abuffer_ctx, "time_base", (AVRational){ 1, INPUT_SAMPLERATE }, AV_OPT_SEARCH_CHILDREN);
av_opt_set_int(abuffer_ctx, "sample_rate", INPUT_SAMPLERATE, AV_OPT_SEARCH_CHILDREN);
/* 用上面的参数初始化abuffer filter,因为上面设置了参数,所以第二个参数传NULL*/
err = avfilter_init_str(abuffer_ctx, NULL);
if (err < 0) {
fprintf(stderr, "Could not initialize the abuffer filter.\n");
return err;
}
/* 创建volume filter. */
volume = avfilter_get_by_name("volume");
if (!volume) {
fprintf(stderr, "Could not find the volume filter.\n");
return AVERROR_FILTER_NOT_FOUND;
}
volume_ctx = avfilter_graph_alloc_filter(filter_graph, volume, "volume");
if (!volume_ctx) {
fprintf(stderr, "Could not allocate the volume instance.\n");
return AVERROR(ENOMEM);
}
/* 通过avfilter_init_dict设置volume filter */
av_dict_set(&options_dict, "volume", AV_STRINGIFY(VOLUME_VAL), 0);
err = avfilter_init_dict(volume_ctx, &options_dict);
av_dict_free(&options_dict);
if (err < 0) {
fprintf(stderr, "Could not initialize the volume filter.\n");
return err;
}
/* 创建aformat filter,用于设置想要的最终输出格式 */
aformat = avfilter_get_by_name("aformat");
if (!aformat) {
fprintf(stderr, "Could not find the aformat filter.\n");
return AVERROR_FILTER_NOT_FOUND;
}
aformat_ctx = avfilter_graph_alloc_filter(filter_graph, aformat, "aformat");
if (!aformat_ctx) {
fprintf(stderr, "Could not allocate the aformat instance.\n");
return AVERROR(ENOMEM);
}
/* 另外一种方式设置aformat filter的参数(键值对且中间用冒号的形式)*/
snprintf(options_str, sizeof(options_str),
"sample_fmts=%s:sample_rates=%d:channel_layouts=0x%"PRIx64,
av_get_sample_fmt_name(OUTPUT_FORMAT), OUTPUT_SAMPLERATE,
(uint64_t)OUTPUT_CHANNEL_LAYOUT);
err = avfilter_init_str(aformat_ctx, options_str);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR, "Could not initialize the aformat filter.\n");
return err;
}
/* 最后创建abuffersink filter,用于获取graph输出的最终转换好的数据 */
abuffersink = avfilter_get_by_name("abuffersink");
if (!abuffersink) {
fprintf(stderr, "Could not find the abuffersink filter.\n");
return AVERROR_FILTER_NOT_FOUND;
}
abuffersink_ctx = avfilter_graph_alloc_filter(filter_graph, abuffersink, "sink");
if (!abuffersink_ctx) {
fprintf(stderr, "Could not allocate the abuffersink instance.\n");
return AVERROR(ENOMEM);
}
/* 不需要设置参数 */
err = avfilter_init_str(abuffersink_ctx, NULL);
if (err < 0) {
fprintf(stderr, "Could not initialize the abuffersink instance.\n");
return err;
}
/* 连接filters */
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) {
fprintf(stderr, "Error connecting filters\n");
return err;
}
/* 配置graph. */
err = avfilter_graph_config(filter_graph, NULL);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR, "Error configuring the filter graph\n");
return err;
}
*graph = filter_graph;
*src = abuffer_ctx;
*sink = abuffersink_ctx;
return 0;
}
static void filter_audio(void)
{
const char *in_filename = "/Users/zhw/Desktop/resource/sintel_f32le_2_48000.pcm";
const char *out_filename = "/Users/zhw/Desktop/sintel_s16le_3_44100.pcm";
FILE *in_file = NULL, *out_file = NULL;
AVFilterGraph *graph;
AVFilterContext *src, *sink;
AVFrame *frame;
int err;
in_file = fopen(in_filename, "rb");
if (!in_file) {
printf("open file %s fail\n", in_filename);
exit(1);
}
out_file = fopen(out_filename, "wb");
if (!out_file) {
printf("open file %s fail\n", out_filename);
exit(1);
}
/* 创建frame,存储输入数据 */
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Error allocating the frame\n");
exit(1);
}
/* 设置filtergraph. */
err = init_filter_graph(&graph, &src, &sink);
if (err < 0) {
fprintf(stderr, "Unable to init filter graph:");
exit(1);
}
while (!feof(in_file)) {
// static int frame_num = 0;
/* Set up the frame properties and allocate the buffer for the data. */
frame->sample_rate = INPUT_SAMPLERATE;
frame->format = INPUT_FORMAT;
frame->channel_layout = INPUT_CHANNEL_LAYOUT;
frame->nb_samples = FRAME_SIZE;
// frame->pts = frame_num * FRAME_SIZE;
err = av_frame_get_buffer(frame, 0);
if (err < 0)
exit(1);
int nb_channels = av_get_channel_layout_nb_channels(frame->channel_layout);
int bytes_per_sample = av_get_bytes_per_sample(frame->format);
//读取数据
unsigned long bytes = fread(frame->data[0], 1, frame->nb_samples * nb_channels * bytes_per_sample, in_file);
if (bytes < frame->nb_samples * nb_channels * bytes_per_sample) {
// printf("*************");
}
// frame_num++;
//添加frame到buffer,用于转换
err = av_buffersrc_add_frame(src, frame);
if (err < 0) {
av_frame_unref(frame);
fprintf(stderr, "Error submitting the frame to the filtergraph:");
exit(1);
}
//获取输出数据
while ((err = av_buffersink_get_frame(sink, frame)) >= 0) {
if (av_sample_fmt_is_planar(frame->format)) {
int data_size = av_get_bytes_per_sample(frame->format);
for (int i = 0; i < frame->nb_samples; i++)
for (int ch = 0; ch < frame->channels; ch++)
fwrite(frame->data[ch] + data_size*i, 1, data_size, out_file);
}else {
fwrite(frame->extended_data[0], 1, frame->linesize[0], out_file);
}
av_frame_unref(frame);
}
if (err == AVERROR(EAGAIN)) {
/* 需要更多的数据 */
continue;
} else if (err == AVERROR_EOF) {
/* 到文件尾*/
break;
} else if (err < 0) {
/* An error occurred. */
fprintf(stderr, "Error filtering the data:");
exit(1);
}
}
avfilter_graph_free(&graph);
av_frame_free(&frame);
printf("finish\n");
}