前言
接上一篇博客,上一篇中介绍了单路和双路混流,要是输入输出流多了像之前那样写就会很麻烦,于是考虑普适的情况,输入一个n初始化好对应的filter。
具体实现
定义要用到的结构体和类(里面一些参数的含义可以看我上一篇博客)
- 输入流编码信息结构体:
struct codec_info {
int sample_rate;
int channels;
int64_t channel_layout;
enum AVSampleFormat sample_fmt;
AVRational time_base;
codec_info() { //构造函数,根据输入可自己定义,我这里默认给了一些数据
sample_rate = 48000;
channels = 2;
channel_layout = av_get_default_channel_layout(channels);
sample_fmt = AV_SAMPLE_FMT_FLTP;
time_base = {1, sample_rate};
}
};
- 一个输入流的filter信息结构体:
struct src_filter {
char args[512];
std::string pad_name;
const AVFilter *abuffersrc;
AVFilterContext *buffersrc_ctx;
codec_info src_codec_info;
AVFilterInOut *outputs;
src_filter::src_filter() {}
void src_filter::init(int n) { //根据序号初始化pad_name、abuffersrc和outputs
abuffersrc = avfilter_get_by_name("abuffer");
outputs = avfilter_inout_alloc();
if (!outputs || !abuffersrc) {
std::cout << "init filter input output error!!!" << std::endl;
}
std::string p = "in" + std::to_string(n);
pad_name = p;
}
};
- 整个混流器filter对应的类:
class filter_info {
public:
std::string filter_desc = "[in0][in1]amix=inputs=2[out]";
//"aresample=48000,aformat=sample_fmts=fltp:channel_layouts=stereo";
std::map<int32_t, std::shared_ptr<src_filter>> src_filter_map; //保存所有输入流的filter信息
AVFilterContext *buffersink_ctx;
AVFilterGraph *filter_graph;
filter_info::filter_info() { }
void filter_info::init(int n) { //根据输入流个数初始化filter_desc和src_filter
filter_desc = gen_filter_desc(n);
std::cout << "init filter_desc: " << filter_desc << std::endl;
int i = 0;
for (i = 0; i < n; i++) {
std::shared_ptr<src_filter> filter_ptr(new src_filter);
filter_ptr->init(i);
src_filter_map.insert(std::make_pair((int32_t)i, std::move(filter_ptr)));
}
}
};
- 生成filter_descr的函数:
std::string Mixer::gen_filter_desc(int n) {
std::string prefix = "";
for (int i = 0; i < n; i++) {
prefix = prefix + "[in" + std::to_string(i) + "]";
}
prefix = prefix + "amix=inputs=" + std::to_string(n) + "[out]";
return prefix;
}
初始化混流器
这部分是核心代码,原理也和上一篇博客一样,实际实现有些许差别,应该看一下就懂了。
int Mixer::init_filters(int n) {
filter.init(n);
int ret = 0;
const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
AVFilterInOut *inputs = avfilter_inout_alloc();
static const enum AVSampleFormat out_sample_fmts[] = { out_codec_info.sample_fmt, (enum AVSampleFormat) - 1};
static const int64_t out_channel_layouts[] = {out_codec_info.channel_layout, -1};
static const int out_sample_rates[] = {out_codec_info.sample_rate, -1};
const AVFilterLink *outlink;
filter.filter_graph = avfilter_graph_alloc();
if (!inputs || !filter.filter_graph) {
ret = AVERROR(ENOMEM);
E_LOG("init filter input output error");
return -1;
}
int i = 0;
for (i = 0; i < n; i++) {
auto iter = filter.src_filter_map.find(i);
if (iter != filter.src_filter_map.end()) {
std::cout << "sample_fmt: "
<< av_get_sample_fmt_name(iter->second->src_codec_info.sample_fmt)
<< " channel_layout: " << iter->second->src_codec_info.channel_layout
<< " sample_rates: " << iter->second->src_codec_info.sample_rate << std::endl;
AVRational time_base = iter->second->src_codec_info.time_base;
snprintf(iter->second->args, sizeof(iter->second->args),
"time_base=%d/"
"%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
time_base.num, time_base.den,
iter->second->src_codec_info.sample_rate,
av_get_sample_fmt_name(iter->second->src_codec_info.sample_fmt),
iter->second->src_codec_info.channel_layout);
ret = avfilter_graph_create_filter(
&(iter->second->buffersrc_ctx), iter->second->abuffersrc,
iter->second->pad_name.c_str(), iter->second->args, NULL,
filter.filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source1\n");
return ret;
}
} else {
E_LOG("Can't find buffersrc, index: {}", i);
}
}
/* buffer audio sink: to terminate the filter chain. */
ret = avfilter_graph_create_filter(&filter.buffersink_ctx, abuffersink, "out",
NULL, NULL, filter.filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");
return ret;
}
ret = av_opt_set_int_list(filter.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");
return ret;
}
ret = av_opt_set_int_list(filter.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");
return ret;
}
ret = av_opt_set_int_list(filter.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");
return ret;
}
AVFilterInOut **filter_outputs;
filter_outputs = new AVFilterInOut*[n];
for (i = 0; i < n; i++) {
auto iter = filter.src_filter_map.find(i);
auto _iter = filter.src_filter_map.find(i + 1);
const char *p;
std::string pad = "in" + std::to_string(i);
p = pad.c_str();
if (iter != filter.src_filter_map.end() &&
_iter != filter.src_filter_map.end()) {
iter->second->outputs->name = av_strdup(iter->second->pad_name.c_str());
iter->second->outputs->filter_ctx = iter->second->buffersrc_ctx;
iter->second->outputs->pad_idx = 0;
iter->second->outputs->next = _iter->second->outputs;
filter_outputs[i] = iter->second->outputs;
} else if (iter != filter.src_filter_map.end() &&
_iter == filter.src_filter_map.end()) {
iter->second->outputs->name = av_strdup(iter->second->pad_name.c_str());
iter->second->outputs->filter_ctx = iter->second->buffersrc_ctx;
iter->second->outputs->pad_idx = 0;
iter->second->outputs->next = NULL;
filter_outputs[i] = iter->second->outputs;
} else {
E_LOG("Can't find buffersrc 222, index: {}", i);
}
}
inputs->name = av_strdup("out");
inputs->filter_ctx = filter.buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
if ((ret = avfilter_graph_parse_ptr(filter.filter_graph, filter.filter_desc.c_str(),
&inputs, filter_outputs, NULL)) < 0) // filter_outputs
{
av_log(NULL, AV_LOG_ERROR, "parse ptr fail, ret: %d\n", ret);
return ret;
}
if ((ret = avfilter_graph_config(filter.filter_graph, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "config graph fail, ret: %d\n", ret);
return ret;
}
/* Print summary of the sink buffer
* Note: args buffer is reused to store channel layout string */
//outlink = filter.buffersink_ctx->inputs[0];
//av_get_channel_layout_string(filter.src_filter_map[1]->args,
// sizeof(filter.src_filter_map[1]->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((enum AVSampleFormat)outlink->format), "?"),
// filter.src_filter_map[1]->args);
avfilter_inout_free(&inputs);
avfilter_inout_free(filter_outputs);
char *temp = avfilter_graph_dump(filter.filter_graph, NULL);
printf("%s\n", temp);
return ret;
}
向buffer里放入和取出数据
- 放数据:遍历一下src_filter_map就可以了,这里以第一个为例。
/* push the audio data from decoded frame into the filtergraph */
auto iter = filter.src_filter_map.find(0);
if (av_buffersrc_add_frame_flags(iter->second->buffersrc_ctx, frame, 0) <
0) {
av_log(NULL, AV_LOG_ERROR,
"Error while feeding the audio filtergraph1\n");
break;
}
- 取数据:几乎没有区别。
/* pull filtered audio from the filtergraph */
while (1) {
ret = av_buffersink_get_frame(filter.buffersink_ctx, filt_frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;
if (ret < 0) {
E_LOG("Failed to av_buffersink_get_frame_flags");
return -1;
}
if (filt_frame->data[0] != NULL) {
encode_write_frame(filt_frame, stream_index);
} else {
W_LOG("No data");
}
av_frame_unref(filt_frame);
}