声音混流基于FFmpeg中的AVFilter,参考资料如下:
混流实现利用FFMpeg的amix混音器实现了一个filter_audio类
filter_audio.h
#ifndef FILTER_AUDIO
#define FILTER_AUDIO
#include <thread>
#include <atomic>
#include <functional>
#include <string>
#include <mutex>
#include <condition_variable>
#include "headers_ffmpeg.h"
namespace am {
typedef struct {
AVFilterContext *ctx;
AVFilterInOut *inout;
AVRational time_base;
int sample_rate;
AVSampleFormat sample_fmt;
int nb_channel;
int64_t channel_layout;
}FILTER_CTX;
typedef std::function<void(AVFrame *)> on_filter_data;
typedef std::function<void(int)> on_filter_error;
class filter_audio
{
public:
filter_audio();
~filter_audio();
int init(const FILTER_CTX &ctx_in0, const FILTER_CTX &ctx_in1, const FILTER_CTX &ctx_out);
inline void registe_cb(on_filter_data cb_on_filter_data, on_filter_error cb_on_filter_error) {
_on_filter_data = cb_on_filter_data;
_on_filter_error = cb_on_filter_error;
}
int start();
int stop();
int add_frame(AVFrame *frame, int index);
const AVRational &get_time_base();
private:
void cleanup();
void filter_loop();
private:
FILTER_CTX _ctx_in_0;
FILTER_CTX _ctx_in_1;
FILTER_CTX _ctx_out;
AVFilterGraph *_filter_graph;
on_filter_data _on_filter_data;
on_filter_error _on_filter_error;
std::atomic_bool _inited;
std::atomic_bool _running;
std::thread _thread;
std::mutex _mutex;
std::condition_variable _cond_var;
bool _cond_notify;
};
}
#endif
filter_audio.cpp
#include "filter_audio.h"
#include <chrono>
#include "error_define.h"
#include "log_helper.h"
namespace am {
static void print_frame(const AVFrame *frame, int index)
{
al_debug("index:%d %lld %d", index, frame->pts, frame->nb_samples);
}
filter_audio::filter_audio()
{
av_register_all();
avfilter_register_all();
memset(&_ctx_in_0, 0, sizeof(FILTER_CTX));
memset(&_ctx_in_1, 0, sizeof(FILTER_CTX));
memset(&_ctx_out, 0, sizeof(FILTER_CTX));
_filter_graph = NULL;
_inited = false;
_running = false;
_cond_notify = false;
}
filter_audio::~filter_audio()
{
stop();
cleanup();
}
static void format_pad_arg(char *arg, int size, const FILTER_CTX &ctx)
{
sprintf_s(arg, size, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
ctx.time_base.num,
ctx.time_base.den,
ctx.sample_rate,
av_get_sample_fmt_name(ctx.sample_fmt),
av_get_default_channel_layout(ctx.nb_channel));
}
int filter_audio::init(const FILTER_CTX & ctx_in0, const FILTER_CTX & ctx_in1, const FILTER_CTX & ctx_out)
{
int error = AE_NO;
int ret = 0;
if (_inited) return AE_NO;
do {
_ctx_in_0 = ctx_in0;
_ctx_in_1 = ctx_in1;
_ctx_out = ctx_out;
_filter_graph = avfilter_graph_alloc();
if (!_filter_graph) {
error = AE_FILTER_ALLOC_GRAPH_FAILED;
break;
}
const std::string filter_desrc = "[in0][in1]amix=inputs=2:duration=first:dropout_transition=0[out]";
_ctx_in_0.inout = avfilter_inout_alloc();
_ctx_in_1.inout = avfilter_inout_alloc();
_ctx_out.inout = avfilter_inout_alloc();
char pad_args0[512] = { 0 }, pad_args1[512] = { 0 };
format_pad_arg(pad_args0, 512, _ctx_in_0);
format_pad_arg(pad_args1, 512, _ctx_in_1);
ret = avfilter_graph_create_filter(&_ctx_in_0.ctx, avfilter_get_by_name("abuffer"), "in0", pad_args0, NULL, _filter_graph);
if (ret < 0) {
error = AE_FILTER_CREATE_FILTER_FAILED;
break;
}
ret = avfilter_graph_create_filter(&_ctx_in_1.ctx, avfilter_get_by_name("abuffer"), "in1", pad_args1, NULL, _filter_graph);
if (ret < 0) {
error = AE_FILTER_CREATE_FILTER_FAILED;
break;
}
ret = avfilter_graph_create_filter(&_ctx_out.ctx, avfilter_get_by_name("abuffersink"), "out", NULL, NULL, _filter_graph);
if (ret < 0) {
error = AE_FILTER_CREATE_FILTER_FAILED;
break;
}
av_opt_set_bin(_ctx_out.ctx, "sample_fmts", (uint8_t*)&_ctx_out.sample_fmt, sizeof(_ctx_out.sample_fmt), AV_OPT_SEARCH_CHILDREN);
av_opt_set_bin(_ctx_out.ctx, "channel_layouts", (uint8_t*)&_ctx_out.channel_layout, sizeof(_ctx_out.channel_layout), AV_OPT_SEARCH_CHILDREN);
av_opt_set_bin(_ctx_out.ctx, "sample_rates", (uint8_t*)&_ctx_out.sample_rate, sizeof(_ctx_out.sample_rate), AV_OPT_SEARCH_CHILDREN);
_ctx_in_0.inout->name = av_strdup("in0");
_ctx_in_0.inout->filter_ctx = _ctx_in_0.ctx;
_ctx_in_0.inout->pad_idx = 0;
_ctx_in_0.inout->next = _ctx_in_1.inout;
_ctx_in_1.inout->name = av_strdup("in1");
_ctx_in_1.inout->filter_ctx = _ctx_in_1.ctx;
_ctx_in_1.inout->pad_idx = 0;
_ctx_in_1.inout->next = NULL;
_ctx_out.inout->name = av_strdup("out");
_ctx_out.inout->filter_ctx = _ctx_out.ctx;
_ctx_out.inout->pad_idx = 0;
_ctx_out.inout->next = NULL;
AVFilterInOut *inoutputs[2] = { _ctx_in_0.inout,_ctx_in_1.inout };
ret = avfilter_graph_parse_ptr(_filter_graph, filter_desrc.c_str(), &_ctx_out.inout, inoutputs, NULL);
if (ret < 0) {
error = AE_FILTER_PARSE_PTR_FAILED;
break;
}
ret = avfilter_graph_config(_filter_graph, NULL);
if (ret < 0) {
error = AE_FILTER_CONFIG_FAILED;
break;
}
//al_debug("dump graph:\r\n%s", avfilter_graph_dump(_filter_graph, NULL));
_inited = true;
} while (0);
if (error != AE_NO) {
al_debug("filter init failed:%s %d", err2str(error), ret);
cleanup();
}
//if (_ctx_in_0.inout)
// avfilter_inout_free(&_ctx_in_0.inout);
//if (_ctx_in_1.inout)
// avfilter_inout_free(&_ctx_in_1.inout);
//if (_ctx_out.inout)
// avfilter_inout_free(&_ctx_out.inout);
return error;
}
int filter_audio::start()
{
if (!_inited)
return AE_NEED_INIT;
if (_running)
return AE_NO;
_running = true;
_thread = std::thread(std::bind(&filter_audio::filter_loop, this));
return 0;
}
int filter_audio::stop()
{
if (!_inited || !_running)
return AE_NO;
_running = false;
_cond_notify = true;
_cond_var.notify_all();
if (_thread.joinable())
_thread.join();
return AE_NO;
}
int filter_audio::add_frame(AVFrame * frame, int index)
{
std::unique_lock<std::mutex> lock(_mutex);
int error = AE_NO;
int ret = 0;
do {
AVFilterContext *ctx = NULL;
switch (index) {
case 0:
ctx = _ctx_in_0.ctx;
break;
case 1:
ctx = _ctx_in_1.ctx;
break;
default:
ctx = NULL;
break;
}
if (!ctx) {
error = AE_FILTER_INVALID_CTX_INDEX;
break;
}
//print_frame(frame, index);
int ret = av_buffersrc_add_frame_flags(ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
if (ret < 0) {
error = AE_FILTER_ADD_FRAME_FAILED;
break;
}
} while (0);
if (error != AE_NO) {
al_debug("add frame failed:%s ,%d", err2str(error), ret);
}
_cond_notify = true;
_cond_var.notify_all();
return error;
}
const AVRational & filter_audio::get_time_base()
{
return av_buffersink_get_time_base(_ctx_out.ctx);
}
void filter_audio::cleanup()
{
if (_filter_graph)
avfilter_graph_free(&_filter_graph);
memset(&_ctx_in_0, 0, sizeof(FILTER_CTX));
memset(&_ctx_in_1, 0, sizeof(FILTER_CTX));
memset(&_ctx_out, 0, sizeof(FILTER_CTX));
_inited = false;
}
void filter_audio::filter_loop()
{
AVFrame *frame = av_frame_alloc();
int ret = 0;
while (_running) {
std::unique_lock<std::mutex> lock(_mutex);
while (!_cond_notify && _running)
_cond_var.wait_for(lock,std::chrono::milliseconds(300));
while (_running && _cond_notify) {
ret = av_buffersink_get_frame(_ctx_out.ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;;
}
if (ret < 0) {
al_fatal("avfilter get frame error:%d", ret);
if (_on_filter_error) _on_filter_error(ret);
break;
}
if (_on_filter_data)
_on_filter_data(frame);
av_frame_unref(frame);
}
_cond_notify = false;
}
av_frame_free(&frame);
}
}
仅支持两路音频输入,一路音频输出。对于此还没有多多研究,所以仅仅贴上代码。