ffmpeg4.x版本录音

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dancing_night/article/details/84253764

1、最近一网友找到我,说我博客上的录音程序用不起了,研究一番发现是他用我代码然后链接ffmpeg库。发现版本改动还挺大,故此从新上传一份依赖最新版本的ffmpeg的录音程序,以便大家参考。

2、直接上代码:


/*
*最简单的录音
*缪国凯 Mickel
*[email protected]
*本程序用目前最新ffmpeg录音
*2018-11-19
*/


#include <windows.h>
#include <DShow.h>
#include <comutil.h>
// #pragma comment(lib,"Strmiids")
// #pragma comment(lib,"comsuppw.lib")
#include <conio.h>
// #pragma comment(lib,"winmm")
#define	SAFE_RELEASE(x)	{if(x != NULL) x->Release();x=NULL;}

#ifdef __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/audio_fifo.h"

#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"

#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avfilter.lib")
	//#pragma comment(lib, "postproc.lib")
	//#pragma comment(lib, "swresample.lib")
	//#pragma comment(lib, "swscale.lib")
#ifdef __cplusplus
};
#endif

typedef struct FilteringContext
{
	AVFilterContext *buffersink_ctx;
	AVFilterContext *buffersrc_ctx;
	AVFilterGraph *filter_graph;
} FilteringContext;

_bstr_t	strDeviceName;
FilteringContext *filter_ctx = NULL;

static char *dup_wchar_to_utf8(wchar_t *w);

static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx,
	AVCodecContext *enc_ctx, const char *filter_spec)
{
	char args[512];
	int ret = 0;
	const AVFilter *buffersrc = NULL;
	const AVFilter *buffersink = NULL;
	AVFilterContext *buffersrc_ctx = NULL;
	AVFilterContext *buffersink_ctx = NULL;
	AVFilterInOut *outputs = avfilter_inout_alloc();
	AVFilterInOut *inputs = avfilter_inout_alloc();
	AVFilterGraph *filter_graph = avfilter_graph_alloc();

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

	if (dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO)
	{
		buffersrc = avfilter_get_by_name("abuffer");
		buffersink = avfilter_get_by_name("abuffersink");
		if (!buffersrc || !buffersink)
		{
			av_log(NULL, AV_LOG_ERROR, "filtering source or sink element not found\n");
			ret = AVERROR_UNKNOWN;
			goto end;
		}
		if (!dec_ctx->channel_layout)
			dec_ctx->channel_layout = av_get_default_channel_layout(dec_ctx->channels);

		//format args
		_snprintf(args, sizeof(args),
			"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
			dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate,
			av_get_sample_fmt_name(dec_ctx->sample_fmt),
			dec_ctx->channel_layout);

		ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source\n");
			goto end;
		}

		// 		char args1[512];
		// 		_snprintf(args1, sizeof(args1),
		// 			"sample_rates=%d:sample_fmts=%s:channel_layouts=0x%I64x",
		// 			(uint8_t)enc_ctx->sample_rate, (uint8_t)enc_ctx->sample_fmt,
		// 			(uint8_t)enc_ctx->channel_layout);

		ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "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_bin(buffersink_ctx, "sample_fmts", (uint8_t*)&enc_ctx->sample_fmt,
			sizeof(enc_ctx->sample_fmt), 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_bin(buffersink_ctx, "channel_layouts", (uint8_t*)&enc_ctx->channel_layout,
			sizeof(enc_ctx->channel_layout), 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_bin(buffersink_ctx, "sample_rates", (uint8_t*)&enc_ctx->sample_rate,
			sizeof(enc_ctx->sample_rate), AV_OPT_SEARCH_CHILDREN);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n");
			goto end;
		}
	}
	else
	{
		ret = AVERROR_UNKNOWN;
		goto end;
	}

	/* Endpoints for the filter graph. */
	outputs->name = av_strdup("in");
	outputs->filter_ctx = buffersrc_ctx;
	outputs->pad_idx = 0;
	outputs->next = NULL;
	inputs->name = av_strdup("out");
	inputs->filter_ctx = buffersink_ctx;
	inputs->pad_idx = 0;
	inputs->next = NULL;

	if (!outputs->name || !inputs->name)
	{
		ret = AVERROR(ENOMEM);
		goto end;
	}

	if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_spec, &inputs, &outputs, NULL)) < 0)
		goto end;

	if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
		goto end;

	/* Fill FilteringContext */
	fctx->buffersrc_ctx = buffersrc_ctx;
	fctx->buffersink_ctx = buffersink_ctx;
	fctx->filter_graph = filter_graph;

end:
	avfilter_inout_free(&inputs);
	avfilter_inout_free(&outputs);
	return ret;
}

int init_filters(AVCodecContext *inCodecContext, AVCodecContext *outCodecContext)
{
	const char *filter_spec = "anull";
	unsigned int i;
	int ret;

	filter_ctx = (FilteringContext *)av_malloc_array(1, sizeof(*filter_ctx));

	if (!filter_ctx)
		return AVERROR(ENOMEM);

	ret = init_filter(&filter_ctx[0], inCodecContext, outCodecContext, filter_spec);

	return ret;
}

int main()
{
	AVFormatContext		*	pFmtInputCtx = NULL, *ofmt_ctx_a = NULL;
	AVInputFormat		*	pAudioInputFmt = NULL;
	AVOutputFormat		*	pAudioOutputFmt = NULL;
	AVStream			*	pOutAudioStream = NULL;
	AVCodecContext		*	pOutputCodecCtx = NULL;
	AVCodecContext		*	pInputCodecCtx = NULL;
	AVCodec				*	pCodec = NULL;
	AVFrame				*	pAudioFrame = NULL;
	uint8_t				*	pFrameBuffer = NULL;
	int						iAudioIndex = -1;

	//注册FFMPEG库
	//av_register_all();
	avdevice_register_all();

	//查找输入方式
	pAudioInputFmt = av_find_input_format("dshow");
	assert(pAudioInputFmt != NULL);

	//以Direct Show的方式打开设备,并将 输入方式 关联到格式上下文
	char * psDevName = dup_wchar_to_utf8(L"audio=麦克风 (Realtek High Definition Au");
	assert(avformat_open_input(&pFmtInputCtx, psDevName, pAudioInputFmt, NULL) == 0);

	//	avformat_find_stream_info(pFmtCtx,NULL);

	if (avformat_find_stream_info(pFmtInputCtx, NULL) < 0)
		return -1;


	for (int i = 0; i < pFmtInputCtx->nb_streams; i++)
	{
		if (pFmtInputCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			pInputCodecCtx = avcodec_alloc_context3(NULL);

			if (avcodec_parameters_to_context(pInputCodecCtx, pFmtInputCtx->streams[i]->codecpar) < 0)
			{
				printf("Copy stream failed!\n");
				return -1;
			}

			iAudioIndex = i;
			AVCodec *tmpCodec = avcodec_find_decoder(pInputCodecCtx->codec_id);
			if (0 > avcodec_open2(pInputCodecCtx, tmpCodec, NULL))
			{
				printf("can not find or open decoder!\n");
			}

			break;
		}
	}

	av_dump_format(pFmtInputCtx, 0, NULL, 0);


// 	pAudioOutputFmt = av_find_output_format("dshow");
// 	assert(pAudioOutputFmt != NULL);
// 	av_register_output_format(pAudioOutputFmt);

	avformat_alloc_output_context2(&ofmt_ctx_a, NULL, NULL, "abc.aac");

	pOutAudioStream = avformat_new_stream(ofmt_ctx_a, NULL);
	AVCodec *audioEnCoder = avcodec_find_encoder(ofmt_ctx_a->oformat->audio_codec);
	
	pOutputCodecCtx = avcodec_alloc_context3(audioEnCoder);

	pOutputCodecCtx->sample_rate = pInputCodecCtx->sample_rate;
	pOutputCodecCtx->channel_layout = pOutputCodecCtx->channel_layout;
	pOutputCodecCtx->channels = av_get_channel_layout_nb_channels(pOutputCodecCtx->channel_layout);

	if (pOutputCodecCtx->channel_layout == 0)
	{
		pOutputCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
		pOutputCodecCtx->channels = av_get_channel_layout_nb_channels(pOutputCodecCtx->channel_layout);

	}

	pOutputCodecCtx->sample_fmt = pOutputCodecCtx->codec->sample_fmts[0];
	AVRational time_base = { 1, pOutputCodecCtx->sample_rate };
	pOutputCodecCtx->time_base = time_base;
	//audioCodecCtx->time_base = time_base;

	pOutputCodecCtx->codec_tag = 0;
	if (ofmt_ctx_a->oformat->flags & AVFMT_GLOBALHEADER)
		pOutputCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

	if (avcodec_open2(pOutputCodecCtx, pOutputCodecCtx->codec, 0) < 0)
	{
		//编码器打开失败,退出程序
		return -1;
	}

	int ret = avcodec_parameters_from_context(pOutAudioStream->codecpar, pOutputCodecCtx);
	if (ret < 0) 
	{
		printf("Failed to copy encoder parameters to output stream #%u\n");
		return ret;
	}

	if (!(ofmt_ctx_a->oformat->flags & AVFMT_NOFILE))
	{
		if (avio_open(&ofmt_ctx_a->pb, "abc.aac", AVIO_FLAG_WRITE) < 0)
		{
			printf("Could not open output file abc.aac");
			return -1;
		}
	}

	if (avformat_write_header(ofmt_ctx_a, NULL) < 0)
	{
		printf("Error occurred when opening audio output file\n");
		return -1;
	}

	if ((ret = init_filters(pInputCodecCtx, pOutputCodecCtx)) < 0)
	{
		printf("init_filters failed\n");
		return -1;
	}


	AVPacket pkt, pkt_out;

	// 	FILE * fp = fopen("C:\\Users\\jk\\Desktop\\abc","wb+");
	// 	assert(fp != NULL);
	AVFrame *frame = av_frame_alloc();
	AVAudioFifo *fifo = NULL;
	fifo = av_audio_fifo_alloc(pOutputCodecCtx->sample_fmt, pOutputCodecCtx->channels, 1);
	int frameIndex = 0;
	char errbuf[256] = { 0 };
	
	while (av_read_frame(pFmtInputCtx, &pkt) == 0 && _kbhit() == 0)
	{		

		pkt_out.data = NULL;
		pkt_out.size = 0;
		pkt_out.stream_index = pkt.stream_index;

		int iRes;
		if ((iRes = avcodec_send_packet(pInputCodecCtx, &pkt)) != 0)
		{
			printf("Send video stream packet failed!\n");
			av_strerror(iRes, errbuf, 256);
			continue;
		}
		if ((iRes = avcodec_receive_frame(pInputCodecCtx, frame)) != 0)
		{
			printf("Receive video frame failed!\n");
			av_strerror(iRes, errbuf, 256);
			continue;
		}
		
		av_packet_unref(&pkt);

		//test write pcm
		if (0)
		{
			FILE *p = NULL;
			p = fopen("test.pcm", "a+b");
			int tempLenght = 2 * frame->nb_samples;//由于实验中知道这是16位深,所以才这么写
			uint8_t *tmpPtr = frame->data[0];
			if (NULL != p)
			{
				while (tempLenght > 0)
				{
					size_t temp = fwrite(tmpPtr, 1, tempLenght, p);
					tmpPtr += temp;
					tempLenght = tempLenght - temp;
				}
				fclose(p);
			}
			continue;
		}

		AVFrame *filter_frame;
		if (pInputCodecCtx->sample_fmt != pOutputCodecCtx->sample_fmt
			|| pInputCodecCtx->channels != pOutputCodecCtx->channels
			|| pInputCodecCtx->sample_rate != pOutputCodecCtx->sample_rate)
		{
			filter_frame = av_frame_alloc();
			
			ret = av_buffersrc_add_frame_flags(filter_ctx[0].buffersrc_ctx, frame, 0);
			if (ret < 0)
			{
				av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
				continue;
			}

			ret = av_buffersink_get_frame(filter_ctx[0].buffersink_ctx, filter_frame);

			if (ret < 0)
			{
				/* if no more frames for output - returns AVERROR(EAGAIN)
				* if flushed and no more frames for output - returns AVERROR_EOF
				* rewrite retcode to 0 to show it as normal procedure completion
				*/
				if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
					ret = 0;
				av_frame_free(&filter_frame);
				continue;
			}
		}
		else
		{
			filter_frame = frame;
		}
		
		int tmpSize = av_audio_fifo_size(fifo);
		int tmpSpace = av_audio_fifo_space(fifo);
		
		//av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + filter_frame->nb_samples);

		av_audio_fifo_write(fifo, (void **)filter_frame->data, filter_frame->nb_samples);
		
		if (filter_frame != frame)
		{
			av_frame_free(&filter_frame);
		}
		tmpSize = av_audio_fifo_size(fifo);
		tmpSpace = av_audio_fifo_space(fifo);

		//循环读取数据,直到buf里数据采样数不够
		while (av_audio_fifo_size(fifo) >= (pOutputCodecCtx->frame_size > 0 ? pOutputCodecCtx->frame_size : 1024))
		{
			AVFrame *frameInput = av_frame_alloc();
			frameInput->nb_samples = pOutputCodecCtx->frame_size > 0 ? pOutputCodecCtx->frame_size : 1024;
			frameInput->channel_layout = pOutputCodecCtx->channel_layout;
			frameInput->format = pOutputCodecCtx->sample_fmt;
			frameInput->sample_rate = pOutputCodecCtx->sample_rate;
			av_frame_get_buffer(frameInput, 0);

			av_audio_fifo_read(fifo, (void **)frameInput->data, (pOutputCodecCtx->frame_size > 0 ? pOutputCodecCtx->frame_size : 1024));

			

			ret = avcodec_send_frame(pOutputCodecCtx, frameInput);
			if (ret < 0) 
			{
				fprintf(stderr, "Error sending a frame for encoding\n");
				continue;
			}
			
			av_frame_free(&frameInput);
			

			while (ret >= 0)
			{
				av_init_packet(&pkt_out);
				
				ret = avcodec_receive_packet(pOutputCodecCtx, &pkt_out);

				if (ret != 0)
				{
					continue;
				}
				
				pkt_out.pts = frameIndex * pOutputCodecCtx->frame_size;
				pkt_out.dts = frameIndex * pOutputCodecCtx->frame_size;
				pkt_out.duration = pOutputCodecCtx->frame_size;
				av_write_frame(ofmt_ctx_a, &pkt_out);
				av_packet_unref(&pkt_out);
				frameIndex++;
			}
		}
	}
	av_frame_free(&frame);

	av_write_trailer(ofmt_ctx_a);

	avio_close(ofmt_ctx_a->pb);
	avformat_free_context(ofmt_ctx_a);


	//解初始化
	if (pFmtInputCtx != NULL)
	{
		avformat_close_input(&pFmtInputCtx);
		pFmtInputCtx = NULL;
	}
	return 0;
}

static char *dup_wchar_to_utf8(wchar_t *w)
{
	char *s = NULL;
	int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
	s = (char *)av_malloc(l);
	if (s)
		WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
	return s;
}

3、工程地址:

https://download.csdn.net/download/dancing_night/10793816

4、老版本录音程序链接:

https://blog.csdn.net/dancing_night/article/details/46561909

猜你喜欢

转载自blog.csdn.net/dancing_night/article/details/84253764