FFMPEG录屏(4)---- 声音混流

声音混流基于FFmpeg中的AVFilter,参考资料如下:

最简单的基于FFmpeg的AVfilter的例子-纯净版

ffmpeg filter学习--混音实现

利用ffmpeg的filter混音

 

混流实现利用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);
	}

}

仅支持两路音频输入,一路音频输出。对于此还没有多多研究,所以仅仅贴上代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值