FFMpeg-12、自带filter实现混音(系统音和麦克风音混合)

1、获取系统音设备名称

windows下获取系统需要安装软件:Setup Screen Capturer Recorder
安装之后使用ffmpeg命令查看设备可以发现

ffmpeg -list_devices true -f dshow -i dummy

FFMpeg-12、自带filter实现混音(系统音和麦克风音混合)_第1张图片
注意我们在写代码获取音频设备的时候,我暂时的方法找不到该设备名,因此暂时是写死的。

2、获取音频

2.1、初始化音频设备

bool CCameraRecord::InitAudioSysInput()
{
	dzlog_info("%s(%d) Start", __FUNCTION__, __LINE__);
	string strAudioDeviceName = "virtual-audio-capturer";
	if (strAudioDeviceName == "")
	{
		dzlog_info("%s(%d) m_strAudioDeviceName Fail", __FUNCTION__, __LINE__);
		return false;
	}
	//m_bAudioInit = false;
	AVDictionary *device_param = 0;
	char szDeviceName[1024] = { 0 };
	sprintf_s(szDeviceName, 5096, "audio=%s", strAudioDeviceName.c_str());

	string device_name_utf8 = AnsiToUTF8(szDeviceName, strlen(szDeviceName));
	dzlog_info("%s(%d) m_strAudioDeviceName=%s", __FUNCTION__, __LINE__, strAudioDeviceName.c_str());
	dzlog_info("%s(%d) szDeviceName=%s", __FUNCTION__, __LINE__, szDeviceName);
	dzlog_info("%s(%d) device_name_utf8=%s", __FUNCTION__, __LINE__, device_name_utf8.c_str());
	if (avformat_open_input(&m_pAudSysFmtCtx, device_name_utf8.c_str(), m_pInputFormat, &device_param) != 0)
	{
		dzlog_info("%s(%d) avformat_open_input Fail", __FUNCTION__, __LINE__);
		return false;
	}

	if (avformat_find_stream_info(m_pAudSysFmtCtx, NULL) < 0)
	{
		dzlog_info("%s(%d) avformat_find_stream_info Fail", __FUNCTION__, __LINE__);
		return false;
	}


	int m_audioindex = -1;
	for (int i = 0; i < m_pAudSysFmtCtx->nb_streams; i++)
	{
		if (m_pAudSysFmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			m_audioindex = i;
			break;
		}
	}

	if (m_audioindex == -1)
	{
		dzlog_info("%s(%d) m_audioindex == -1", __FUNCTION__, __LINE__);
		return false;
	}

	if (avcodec_open2(m_pAudSysFmtCtx->streams[m_audioindex]->codec, avcodec_find_decoder(m_pAudSysFmtCtx->streams[m_audioindex]->codec->codec_id), NULL) < 0)
	{
		dzlog_info("%s(%d) avcodec_open2 Fail", __FUNCTION__, __LINE__);
		return false;
	}

	m_nSample_rateSys = m_pAudSysFmtCtx->streams[m_audioindex]->codec->sample_rate;
	m_nChannelsSys = m_pAudSysFmtCtx->streams[m_audioindex]->codec->channels;
	dzlog_info("%s(%d) End", __FUNCTION__, __LINE__);
	m_bAudioSysInit = true;
	return m_bAudioSysInit;
}

2.2、解码获取音频帧

void CCameraRecord::HandleReadAudioSysPacket()
{
	int ret;

	int encode_audio = 1;
	int dec_got_frame_a = 0;
	dzlog_info("%s(%d) Start", __FUNCTION__, __LINE__);
	while (encode_audio)
	{
		if (m_bAudioExit)
			break;

		AVFrame *input_frame = av_frame_alloc();
		AVFrame *output_frame;
		if (!input_frame)
		{
			ret = AVERROR(ENOMEM);
			dzlog_info("%s(%d) av_frame_alloc Fail", __FUNCTION__, __LINE__);
			return;
		}

		AVPacket input_packet;
		av_init_packet(&input_packet);
		input_packet.data = NULL;
		input_packet.size = 0;

		if ((ret = av_read_frame(m_pAudSysFmtCtx, &input_packet)) < 0)
		{
			if (ret == AVERROR_EOF)
				encode_audio = 0;
			else
			{
				dzlog_info("%s(%d) av_read_frame Fail", __FUNCTION__, __LINE__);
				return;
			}
		}

		if ((ret = avcodec_decode_audio4(m_pAudSysFmtCtx->streams[m_audioindex]->codec, input_frame, &dec_got_frame_a, &input_packet)) < 0)
		{
			dzlog_info("%s(%d) avcodec_decode_audio4 Fail", __FUNCTION__, __LINE__);
			return;
		}

		av_packet_unref(&input_packet);
		if (dec_got_frame_a)
		{
			//m_queAudioFrame.push(input_frame);
			//if (m_bIsRecord && m_bflagAudioSys)
			if (m_bIsRecord)
			{
				//AudioConvert(input_frame, AV_SAMPLE_FMT_S16, m_nChannels, m_nSample_rate, &output_frame);
				//这就是将音频帧送往混音filter的步骤
				if (av_buffersrc_add_frame_flags(buffersrc_ctx2, input_frame, 0) < 0) {
					av_log(NULL, AV_LOG_ERROR, "Error while feeding the audio filtergraph\n");
					continue;
				}
				//write_audio_frame(m_pAudSysFmtCtx->streams[m_audioindex], output_frame, av_gettime() - m_start_time);
				OutputDebugStringA("m_pAudSysFmtCtx  :\n ");
			}
		}

		if(input_frame != NULL)
			av_frame_free(&input_frame);
		//av_frame_free(&output_frame);
	}
	dzlog_info("%s(%d) End", __FUNCTION__, __LINE__);
	return;
}

3、初始化混音filter

//混音过滤器
char *filter_descr = "[in0][in1]amix=inputs=2[out]";

//混音过滤器初始化
int CCameraRecord::init_filters(const char *filters_descr)
{
	char args1[512];
	char args2[512];
	int ret = 0;
	const AVFilter *abuffersrc1 = avfilter_get_by_name("abuffer");
	const AVFilter *abuffersrc2 = avfilter_get_by_name("abuffer");
	const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");

	AVFilterInOut *outputs1 = avfilter_inout_alloc();
	AVFilterInOut *outputs2 = avfilter_inout_alloc();
	AVFilterInOut *inputs = avfilter_inout_alloc();

	//输出格式
	static const enum AVSampleFormat out_sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
	static const int64_t out_channel_layouts[] = { AV_CH_LAYOUT_STEREO, -1 };
	static const int out_sample_rates[] = { 44100, -1 };
	const AVFilterLink *outlink;

	AVRational time_base_1 = m_pAudFmtCtx->streams[m_audioindex]->time_base;//第一路音频时间戳
	AVRational time_base_2 = m_pAudSysFmtCtx->streams[m_audioindex]->time_base;//第二路音频时间戳

	filter_graph = avfilter_graph_alloc();
	if (!outputs1 || !inputs || !filter_graph) {
		ret = AVERROR(ENOMEM);
		goto end;
	}

	//根据第一路音频相关参数创建filter
	/* buffer audio source: the decoded frames from the decoder will be inserted here. */
	if (!m_pAudFmtCtx->streams[m_audioindex]->codec->channel_layout)
		m_pAudFmtCtx->streams[m_audioindex]->codec->channel_layout = av_get_default_channel_layout(m_pAudFmtCtx->streams[m_audioindex]->codec->channels);
	snprintf(args1, sizeof(args1),
		"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%d",
		time_base_1.num, time_base_1.den, m_pAudFmtCtx->streams[m_audioindex]->codec->sample_rate,
		av_get_sample_fmt_name(m_pAudFmtCtx->streams[m_audioindex]->codec->sample_fmt), m_pAudFmtCtx->streams[m_audioindex]->codec->channel_layout);
	ret = avfilter_graph_create_filter(&buffersrc_ctx1, abuffersrc1, "in1",
		args1, NULL, filter_graph);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
		goto end;
	}

	//根据第二路音频相关参数创建filter
//#if (ENABLE_FILTERS)
	/* buffer audio source: the decoded frames from the decoder will be inserted here. */
	if (!m_pAudSysFmtCtx->streams[m_audioindex]->codec->channel_layout)
		m_pAudSysFmtCtx->streams[m_audioindex]->codec->channel_layout = av_get_default_channel_layout(m_pAudSysFmtCtx->streams[m_audioindex]->codec->channels);
	snprintf(args2, sizeof(args2),
		"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%d",
		time_base_2.num, time_base_2.den, m_pAudSysFmtCtx->streams[m_audioindex]->codec->sample_rate,
		av_get_sample_fmt_name(m_pAudSysFmtCtx->streams[m_audioindex]->codec->sample_fmt), m_pAudSysFmtCtx->streams[m_audioindex]->codec->channel_layout);
	ret = avfilter_graph_create_filter(&buffersrc_ctx2, abuffersrc1, "in2",
		args2, NULL, filter_graph);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
		goto end;
	}
//#endif

	//创建输出filter 及filter的参数设置
	/* buffer audio sink: to terminate the filter chain. */
	ret = avfilter_graph_create_filter(&buffersink_ctxAudio, 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_ctxAudio, "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_ctxAudio, "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_ctxAudio, "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;
	}

	/*
	* Set the endpoints for the filter graph. The filter_graph will
	* be linked to the graph described by filters_descr.
	*/

	/*
	* The buffer source output must be connected to the input pad of
	* the first filter described by filters_descr; since the first
	* filter input label is not specified, it is set to "in" by
	* default.
	*/
	outputs1->name = av_strdup("in0");
	outputs1->filter_ctx = buffersrc_ctx1;
	outputs1->pad_idx = 0;
//#if (ENABLE_FILTERS)
	outputs1->next = outputs2;

	outputs2->name = av_strdup("in1");
	outputs2->filter_ctx = buffersrc_ctx2;
	outputs2->pad_idx = 0;
	outputs2->next = NULL;
//#else
	//outputs1->next = NULL;
//#endif
	/*
	* The buffer sink input must be connected to the output pad of
	* the last filter described by filters_descr; since the last
	* filter output label is not specified, it is set to "out" by
	* default.
	*/
	inputs->name = av_strdup("out");
	inputs->filter_ctx = buffersink_ctxAudio;
	inputs->pad_idx = 0;
	inputs->next = NULL;


	AVFilterInOut* filter_outputs[2];
	filter_outputs[0] = outputs1;
//#if (ENABLE_FILTERS)
	filter_outputs[1] = outputs2;
//#endif

	if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
		&inputs, &outputs1, NULL)) < 0)//filter_outputs
	{
		av_log(NULL, AV_LOG_ERROR, "parse ptr fail, ret: %d\n", ret);
		goto end;
	}

	if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "config graph fail, ret: %d\n", ret);
		goto end;
	}

	/* Print summary of the sink buffer
	* Note: args buffer is reused to store channel layout string */
	outlink = buffersink_ctxAudio->inputs[0];
	av_get_channel_layout_string(args1, sizeof(args1), -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), "?"),
		args1);*/

end:
	avfilter_inout_free(&inputs);
	avfilter_inout_free(&outputs1);

	return ret;
}


3.1、往filter添加音频帧及取出混音后的帧

往混音filter中添加音频帧
av_buffersrc_add_frame_flags(buffersrc_ctx1, input_frame, 0)
av_buffersrc_add_frame_flags(buffersrc_ctx2, input_frame, 0) 
从filter中取出混音后的音频帧  然后再进行编码
void CCameraRecord::HandleHunyin()
{
	int encode_audio = 1;
	int ret;
	while (encode_audio)
	{
		if (m_bAudioExit)
			break;
		ret = av_buffersink_get_frame(buffersink_ctxAudio, m_filt_frame);//接收混音后的音频帧
		if (ret >= 0)
		{
			write_audio_frame(m_pAudFmtCtx->streams[m_audioindex], m_filt_frame, av_gettime() - m_start_time);//将音频帧进行重采样编码写入文件
			av_frame_unref(m_filt_frame);
		}
		else
		{
			Sleep(10);
			continue;
		}
	}
}

3.2、实现一路静音

混音的具体操作是
PCM的叠加原理:假设混合PCM1和PCM2,则MIX_PCM=PCM1/2 + PCM2/2。
因此,如果需要在混音的时候只要一路的时候需要将另外一路进行静音,也就是传0进去就ok了

if (m_bflagAudioSys)//控制声音
{
	memset(input_frame->data[0], 0, input_frame->nb_samples*2*2);
}
if (av_buffersrc_add_frame_flags(buffersrc_ctx1, input_frame, 0) < 0) {
	av_log(NULL, AV_LOG_ERROR, "Error while feeding the audio filtergraph\n");
	continue;
}

参考链接:

https://www.cnblogs.com/ranson7zop/p/7725324.html

你可能感兴趣的:(音视频,音视频,filter实现混音,ffmpeg实现混音,系统音获取)