前面有提到过,ffmpeg音频重采样有多种方法,一种是libswresample,另外一种是libavfilter,前一种方法已经介绍过,现在介绍使用libavfilter的方法,并把filter处理后的数据写入AVAudioFifo中,再从fifo中读取一帧音频数据(因为经过filter重采样的数据长度会发生变化,可能比一帧长,也可能比一帧短,具体看是上采样还是下采样了)并存储到AVFrame结构体中,为了验证filter后的数据是否正确,我特意把AVFrame中的数据存储为PCM格式的文件。
该例子还是用AAC格式的文件来做测试,resample后的采样率为64kHz,双声道,参数如下:
static const char *filter_descr = "aresample=64000:async=1024,aformat=sample_fmts=fltp:channel_layouts=stereo";
下面直接贴测试的代码
#include <unistd.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavutil/opt.h"
#include "libavutil/audio_fifo.h"
static AVFormatContext *fmt_ctx;
static AVCodecContext *dec_ctx;
static int audio_stream_index = -1;
AVFilterContext *buffersink_ctx;
AVFilterContext *buffersrc_ctx;
AVFilterGraph *filter_graph;
static const char *filter_descr = "aresample=64000:async=1024,aformat=sample_fmts=fltp:channel_layouts=stereo";
//static const char *filter_descr = "aresample=async=512:sample_rate=64000,aformat=sample_fmts=fltp:channel_layouts=stereo";
static FILE *fp = NULL;
AVFrame *alloc_audio_frame(const int channels, const int64_t layout, enum AVSampleFormat sample_fmt, int align)
{
AVFrame *frame;
int buffer_size;
uint16_t *samples;
frame = av_frame_alloc();
if(!frame)
{
av_log(NULL,AV_LOG_QUIET,"Could not allocate frame\n");
return NULL;
}
frame->nb_samples = 1024;
frame->format = sample_fmt;
frame->channel_layout = av_get_default_channel_layout(channels);
buffer_size = av_samples_get_buffer_size(NULL, channels, frame->nb_samples, frame->format ,0);
if(buffer_size < 0)
{
av_log(NULL,AV_LOG_QUIET,"Could not allocate buffer size\n");
return NULL;
}
samples = av_malloc(buffer_size);
if(!samples)
{
av_log(NULL,AV_LOG_QUIET,"Could not allocate samples\n");
return NULL;
}
if(avcodec_fill_audio_frame(frame, channels, frame->format,(const uint16_t*)samples, buffer_size,0) < 0)
{
av_log(NULL,AV_LOG_QUIET,"Could not setup audio frame\n");
return NULL;
}
return frame;
}
static void write_frame(const AVFrame *frame)
{
int i, ch;
int data_size = av_get_bytes_per_sample( AV_SAMPLE_FMT_FLTP);
int channels = av_get_channel_layout_nb_channels( AV_CH_LAYOUT_STEREO);
for(i=0;i < frame->nb_samples; i++)
{
for(ch = 0; ch < channels; ch++)
{
fwrite(frame->data[ch] + data_size*i, 1, data_size, fp);
}
}
}
static int open_input_file(const char *filename)
{
int ret;
AVCodec *dec = NULL;
ret = avformat_open_input(&fmt_ctx, filename ,NULL, NULL);
if(ret < 0)
{
av_log(NULL,AV_LOG_QUIET,"Cannot open input file\n");
return ret;
}
ret = avformat_find_stream_info(fmt_ctx, NULL);
if(ret < 0)
{
av_log(NULL,AV_LOG_QUIET,"Cannot find stream information\n");
return ret;
}
//select the audio stream
ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0);
if(ret < 0)
{
av_log(NULL,AV_LOG_QUIET,"Cannot find an audio stream in the input file\n");
return ret;
}
audio_stream_index = ret;
dec_ctx = fmt_ctx->streams[audio_stream_index]->codec;
av_opt_set_int(dec_ctx, "recounted_frames", 1, 0);
ret = avcodec_open2(dec_ctx, dec, NULL);
if(ret < 0)
{
av_log(NULL,AV_LOG_QUIET,"Cannot open audio decoder\n");
return ret;
}
return 0;
}
static int init_filters(const char *filters_descr)
{
char args[512];
int ret = 0;
AVFilter *abuffersrc = avfilter_get_by_name("abuffer");
AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
static const enum AVSampleFormat out_sample_fmts[] = { AV_SAMPLE_FMT_FLTP, -1 };
static const int64_t out_channel_layouts[] = { AV_CH_LAYOUT_STEREO, -1 };
static const int out_sample_rates[] = { 64000, -1 };
const AVFilterLink *outlink;
AVRational time_base = fmt_ctx->streams[audio_stream_index]->time_base;
filter_graph = avfilter_graph_alloc();
if (!outputs || !inputs || !filter_graph) {
ret = AVERROR(ENOMEM);
goto end;
}
/* buffer audio source: the decoded frames from the decoder will be inserted here. */
if (!dec_ctx->channel_layout)
dec_ctx->channel_layout = av_get_default_channel_layout(dec_ctx->channels);
snprintf(args, sizeof(args),
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64,
time_base.num, time_base.den, dec_ctx->sample_rate,
av_get_sample_fmt_name(dec_ctx->sample_fmt), dec_ctx->channel_layout);
printf("%s\n",args);
ret = avfilter_graph_create_filter(&buffersrc_ctx, abuffersrc, "in",
args, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
goto end;
}
/* buffer audio sink: to terminate the filter chain. */
ret = avfilter_graph_create_filter(&buffersink_ctx, abuffersink, "out",
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");
goto end;
}
ret = av_opt_set_int_list(buffersink_ctx, "sample_fmts", out_sample_fmts, -1,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n");
goto end;
}
ret = av_opt_set_int_list(buffersink_ctx, "channel_layouts", out_channel_layouts, -1,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n");
goto end;
}
ret = av_opt_set_int_list(buffersink_ctx, "sample_rates", out_sample_rates, -1,
AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n");
goto end;
}
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
&inputs, &outputs, NULL)) < 0)
goto end;
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
goto end;
/* Print summary of the sink buffer
* Note: args buffer is reused to store channel layout string */
outlink = buffersink_ctx->inputs[0];
av_get_channel_layout_string(args, sizeof(args), -1, outlink->channel_layout);
av_log(NULL, AV_LOG_INFO, "Output: srate:%dHz fmt:%s chlayout:%s\n",
(int)outlink->sample_rate,
(char *)av_x_if_null(av_get_sample_fmt_name(outlink->format), "?"),
args);
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
return ret;
}
static int init_fifo(AVAudioFifo **fifo)
{
*fifo = av_audio_fifo_alloc(AV_SAMPLE_FMT_FLTP, 2,1);
if(!(*fifo))
{
av_log(NULL,AV_LOG_QUIET,"Could not aoolcate fifo\n");
return AVERROR(ENOMEM);
}
return 0;
}
static int add_samples_to_fifo(AVAudioFifo *fifo, uint8_t **data, const int frame_size)
{
int error;
error = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + frame_size);
if(error < 0 )
{
return error;
}
if(av_audio_fifo_write(fifo, (void **)data, frame_size) < frame_size)
{
av_log(NULL,AV_LOG_QUIET,"Could not write data to fifo\n");
return AVERROR_EXIT;
}
return 0;
}
static int init_output_frame(AVFrame **frame, int frame_size)
{
*frame = av_frame_alloc();
(*frame)->nb_samples = frame_size;
(*frame)->channel_layout = AV_CH_LAYOUT_STEREO;
(*frame)->format = AV_SAMPLE_FMT_FLTP;
(*frame)->sample_rate = 64000;
av_frame_get_buffer(*frame, 0);
}
int main(int argc, char **argv)
{
if(argc != 3)
{
fprintf(stderr,"use it like this:");
fprintf(stderr,"%s input_file output.pcm\n");
return 0;
}
int ret;
AVAudioFifo *fifo = NULL;
AVPacket packet0, packet;
AVFrame *frame = av_frame_alloc();
AVFrame *filt_frame = av_frame_alloc();
int got_frame;
if(!frame || !filt_frame)
{
perror("Could not allocate frame");
exit(1);
}
av_register_all();
avfilter_register_all();
init_fifo(&fifo);
ret = open_input_file(argv[1]);
if(ret < 0)
{
av_log(NULL,AV_LOG_QUIET,"Open input file failed\n");
goto end;
}
ret = init_filters(filter_descr);
if(ret < 0)
{
av_log(NULL,AV_LOG_QUIET,"init filters failed\n");
goto end;
}
fp = fopen(argv[2], "wb");
if(fp == NULL)
{
av_log(NULL,AV_LOG_QUIET,"open file failed\n");
goto end;
}
packet0.data = NULL;
packet.data = NULL;
while (1) {
if (!packet0.data) {
if ((ret = av_read_frame(fmt_ctx, &packet)) < 0)
break;
packet0 = packet;
}
if (packet.stream_index == audio_stream_index) {
got_frame = 0;
ret = avcodec_decode_audio4(dec_ctx, frame, &got_frame, &packet);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error decoding audio\n");
continue;
}
packet.size -= ret;
packet.data += ret;
if (got_frame) {
/* push the audio data from decoded frame into the filtergraph */
if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, 0) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while feeding the audio filtergraph\n");
break;
}
/* pull filtered audio from the filtergraph */
while (1) {
ret = av_buffersink_get_frame_flags(buffersink_ctx, filt_frame, 0);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
if (ret < 0)
goto end;
add_samples_to_fifo(fifo, filt_frame->data, filt_frame->nb_samples);
while(av_audio_fifo_size(fifo) > 1024)
{
AVFrame *f;
init_output_frame(&f, 1024);
av_audio_fifo_read(fifo, (void **)f->data, 1024);
write_frame(f);
av_frame_free(&f);
}
av_frame_unref(filt_frame);
}
}
if (packet.size <= 0)
av_packet_unref(&packet0);
} else {
av_packet_unref(&packet0);
}
}
end:
if(fifo)
av_audio_fifo_free(fifo);
avfilter_graph_free(&filter_graph);
avcodec_close(dec_ctx);
avformat_close_input(&fmt_ctx);
av_frame_free(&frame);
av_frame_free(&filt_frame);
return 0;
}
编译方法
gcc -o filteraudio filter_audio.c -I/home/b1shen/workspace/itc_mcu_dep/install/include -L/home/b1shen/workspace/itc_mcu_dep/install/lib -lavdevice -lavformat -lavfilter -lpostproc -lavcodec -lswresample -lswscale -lavutil -lpthread -ldl -lxml2 -lz -lx264 -ldl -lm -g
有些库可能并不需要,可以自行删减。最后生成二进制文件filteraudio。找一段aac文件进行测试,命令为:
./filteraudio test.aac /tmp/test.pcm
最终resample后的文件为test.pcm,用goldWave工具验证数据是否正确:
test.aac的时长为10s,经过重采样后,保存到pcm文件也为10s,通过工具播放,pcm数据正确。