FFmpeg版本4.2.5
流程图
关键函数
- avfilter_graph_alloc():分配过滤器图
- avfilter_graph_parse_ptr():将由字符串描述的图形添加到图形中。在图形过滤器描述中,如果第一个过滤器的输入标签没有指定,则假定为“in”;如果未指定最后一个过滤器的输出标签,则假定为“out”。
- avfilter_graph_config():检查有效性并配置图中的所有链接和格式。
- av_buffersrc_add_frame():将帧添加到缓冲区源。
- av_buffersink_get_frame():从接收器中获取一个包含过滤数据的框架并将其放入框架中。
代码
#include <exception>
#include <map>
extern "C"
{
#include <libavutil/avassert.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavutil/opt.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
}
#define PCM1_FRAME_SIZE 4096 //(2*1024*2)
#define SAMPLE_RATE 44100
#define SAMPLE_FORMAT AV_SAMPLE_FMT_S16
#define CHANNEL_LAYOUT AV_CH_LAYOUT_STEREO
#define CHANNELS 2
#define BIT_PER_SAMPLE 16
typedef struct {
char *filename;
FILE *file;
int sample_rate;
AVSampleFormat sample_format;
int channels;
int channel_layout;
char channel_layout_str[20];
int bitPerSample;
AVFilterContext *filterContext;
} AudioInfo;
std::map<int, AudioInfo> audioInfos;
std::shared_ptr<AudioInfo> outAudioInfo;
AVFilterGraph *graph;
int ret = 0;
/**
* 打开输入文件
* @param _audioInfo
* @return 0 succeed
*/
int openInput(AudioInfo *audioInfo) {
audioInfo->file = fopen(audioInfo->filename, "rb");
if (!audioInfo->file) {
fprintf(stderr, "%s open failed\n", audioInfo->filename);
return -1;
}
return 0;
}
/**
* 释放资源
*/
void exit() {
for (auto audioInfo: audioInfos) {
fclose(audioInfo.second.file);
avfilter_free(audioInfo.second.filterContext);
}
fclose(outAudioInfo->file);
avfilter_free(outAudioInfo->filterContext);
avfilter_graph_free(&graph);
exit(1);
}
int addFrame(AudioInfo *audioInfo, uint8_t *buffer, size_t size) {
if (buffer && size > 0) {
std::shared_ptr<AVFrame> avFrame(av_frame_alloc(), [](AVFrame *ptr) { av_frame_free(&ptr); });
avFrame->sample_rate = audioInfo->sample_rate;
avFrame->format = audioInfo->sample_format;
avFrame->channels = audioInfo->channels;
avFrame->nb_samples = (int) size * 8 / audioInfo->bitPerSample / audioInfo->channels;
if (avFrame->nb_samples) {
if (av_frame_get_buffer(avFrame.get(), 0) < 0) {
fprintf(stderr, "av_frame_get_buffer failed\n");
exit();
}
}
//todo 注意:这里要传extended_data[0],不然会把其他数据覆盖?
std::memcpy(avFrame->extended_data[0], buffer, size);
if ((ret = av_buffersrc_add_frame(audioInfo->filterContext, avFrame.get())) != 0) {
return -1;
}
} else {
if ((ret = av_buffersrc_add_frame(audioInfo->filterContext, nullptr)) != 0) {
return -1;
}
}
return 0;
}
/**
* 获取过滤器处理后的帧
* @param outBuffer
* @return
*/
int getFrame(uint8_t *outBuffer) {
//这里利用智能指针释放AVFrame
std::shared_ptr<AVFrame> avFrame(av_frame_alloc(), [](AVFrame *ptr) { av_frame_free(&ptr); });
ret = av_buffersink_get_frame(outAudioInfo->filterContext, avFrame.get());
if (ret < 0) {
return 0;
}
//写入前判断下frame buffer大小
int size = av_samples_get_buffer_size(nullptr, avFrame->channels, avFrame->nb_samples,
static_cast<AVSampleFormat>(avFrame->format), 1);
//todo? 这里10000随便给了,这里需要了解下这个判断的意义
if (size > 10000) {
return 0;
}
//注意:这里要传avFrame->extended_data[0]
std::memcpy(outBuffer, avFrame->extended_data[0], size);
return size;
}
/**
* 初始化过滤器图
* @param duration
* @return
*/
int initFilter(char *duration) {
graph = avfilter_graph_alloc();
if (!graph) {
fprintf(stderr, "avfilter_graph_alloc failed\n");
return -1;
}
AVFilterInOut *in = avfilter_inout_alloc();
AVFilterInOut *out = avfilter_inout_alloc();
char filters[512] = {0};
char sample_format[4];
/**
* 1、这里的Parsed_abuffer_0,Parsed_abuffer_1。。。是自动生成的,规则为[Parsed_过滤器名_索引],索引就是第几个过滤器
* 2、必须有abuffer和abuffersink(视频的话是buffer和buffersink)
*/
snprintf(filters, sizeof(filters),
"abuffer=sample_rate=%d:channels=%d:channel_layout=%s:sample_fmt=%s[input1];"//Parsed_abuffer_0
"abuffer=sample_rate=%d:channels=%d:channel_layout=%s:sample_fmt=%s[input2];"//Parsed_abuffer_1
"[input1][input2]amix=inputs=2:duration=%s:dropout_transition=3[amix];"//Parsed_amix_2
"[amix]aformat=sample_rates=%d:sample_fmts=%s:channel_layouts=%s[aformat];"//Parsed_aformat_3
"[aformat]abuffersink",//Parsed_abuffersink_4
audioInfos[0].sample_rate,
audioInfos[0].channels,
audioInfos[0].channel_layout_str,
av_get_sample_fmt_string(sample_format, 4, audioInfos[0].sample_format),
audioInfos[1].sample_rate,
audioInfos[1].channels,
audioInfos[1].channel_layout_str,
av_get_sample_fmt_string(sample_format, 4, audioInfos[1].sample_format),
duration,
outAudioInfo->sample_rate,
av_get_sample_fmt_string(sample_format, 4, outAudioInfo->sample_format),
outAudioInfo->channel_layout_str
);
fprintf(stdout, "filters:%s\n", filters);
//通过字符串的方式生成过滤器图
avfilter_graph_parse_ptr(graph, filters, &in, &out, nullptr);
if (avfilter_graph_config(graph, nullptr) < 0) {
fprintf(stderr, "avfilter_graph_alloc failed\n");
return -1;
}
audioInfos[0].filterContext = avfilter_graph_get_filter(graph, "Parsed_abuffer_0");
if (!audioInfos[0].filterContext) {
fprintf(stderr, "Parsed_abuffer_1 not find!\n");
return -1;
}
audioInfos[1].filterContext = avfilter_graph_get_filter(graph, "Parsed_abuffer_1");
if (!audioInfos[1].filterContext) {
fprintf(stderr, "Parsed_abuffer_2 not find!\n");
return -1;
}
outAudioInfo->filterContext = avfilter_graph_get_filter(graph, "Parsed_abuffersink_4");
if (!outAudioInfo->filterContext) {
fprintf(stderr, "Parsed_abuffersink_4 not find!\n");
return -1;
}
return 0;
}
int main(int argc, char **argv) {
if (argc < 4) {
fprintf(stderr, "args need pass 2 audio filename, and outfilename!\n");
return 1;
}
uint8_t buffer[PCM1_FRAME_SIZE];
uint8_t outBuffer[PCM1_FRAME_SIZE];
audioInfos[0].filename = argv[1];
audioInfos[1].filename = argv[2];
for (auto &audioInfo: audioInfos) {
ret = openInput(&audioInfo.second);
if (ret != 0) {
fprintf(stderr, "%s openInput failed!\n", audioInfo.second.filename);
exit();
}
audioInfo.second.sample_rate = SAMPLE_RATE;
audioInfo.second.channels = CHANNELS;
audioInfo.second.channel_layout = CHANNEL_LAYOUT;
av_get_channel_layout_string(audioInfo.second.channel_layout_str,
sizeof(audioInfo.second.channel_layout_str), 0,
audioInfo.second.channel_layout);
audioInfo.second.bitPerSample = BIT_PER_SAMPLE;
audioInfo.second.sample_format = SAMPLE_FORMAT;
}
outAudioInfo = std::make_shared<AudioInfo>();
outAudioInfo->filename = argv[3];
outAudioInfo->file = fopen(outAudioInfo->filename, "wb");
if (!outAudioInfo->file) {
fprintf(stderr, "%s open failed\n", outAudioInfo->filename);
return -1;
}
//todo 这里采样率可以改
outAudioInfo->sample_rate = 96000;
outAudioInfo->channels = CHANNELS;
outAudioInfo->channel_layout = CHANNEL_LAYOUT;
av_get_channel_layout_string(outAudioInfo->channel_layout_str,
sizeof(outAudioInfo->channel_layout_str), 0,
outAudioInfo->channel_layout);
outAudioInfo->bitPerSample = BIT_PER_SAMPLE;
outAudioInfo->sample_format = SAMPLE_FORMAT;
char *duration = "shortest";
if (initFilter(duration) != 0) {
fprintf(stderr, "initFilter failed!\n");
exit();
}
size_t len1 = 0, len2 = 0;
int size = 0;
while (true) {
len1 = fread(&buffer, 1, PCM1_FRAME_SIZE, audioInfos[0].file);
if (addFrame(&audioInfos[0], buffer, len1) != 0) {
fprintf(stderr, "%s addFrame failed!\n", audioInfos[0].filename);
exit();
}
std::memset(&buffer, 0, sizeof(buffer));
len2 = fread(&buffer, 1, PCM1_FRAME_SIZE, audioInfos[1].file);
if (addFrame(&audioInfos[1], buffer, len2) != 0) {
fprintf(stderr, "%s addFrame failed!\n", audioInfos[1].filename);
exit();
}
if ((size = getFrame(outBuffer)) > 0) {
fwrite(&outBuffer, 1, size, outAudioInfo->file);
}
if (len1 < 0 && len2 < 0) {
exit();
break;
}
}
}