请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

avcodec_send_frame提示无效参数?

采集音频并编码教程中,编码用的fdk_aac,但是我从官网下载的ffmpeg库不支持,所以就改成opus,结果就采集的yuv数据保存到本地可以播放,但是编码就提示无效参数,不知道怎么办了?

创建编码器部分:

AVCodec *codec = avcodec_find_encoder_by_name("libopus");
//创建codec上下文
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);

codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16;//
codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
codec_ctx->channels = 2;
codec_ctx->sample_rate = 48000;
codec_ctx->bit_rate = 128000;//AAC_LC: 128K, AAC HE: 64K, AAC HE V2: 32K

创建AVFrame部分:

AVFrame *frame = NULL;

//分配AVFrame和其中的buf
frame = av_frame_alloc();

frame->nb_samples = 22050;
frame->format = AV_SAMPLE_FMT_S16;
frame->channel_layout = AV_CH_LAYOUT_STEREO;
frame->channels = 2;
frame->sample_rate = 48000;

循环写文件部分:

     while (rec_status) 
            {
	//读取
	ret = av_read_frame(fmt_ctx, &pkt);

	printf("packet size is %d(%p)\n", pkt.size, pkt.data);//pkt.size是88200

	//重采样
	memcpy((void*)src_data[0], (void*)pkt.data, pkt.size);
	swr_convert(swr_ctx, dst_data, 22050, (const uint8_t**)src_data, 22050);

	//这里如果写文件发现可以正常播放
	//fwrite(dst_data, 1, dst_linesize, outfile);//
	//fflush(outfile);

	//放到AVFrame里
	memcpy((void*)frame->data[0], (void*)dst_data[0], dst_linesize);

    //这里始终出错,提示无效参数
	ret = avcodec_send_frame(c_ctx, frame);
	if (ret < 0)
	{
		av_strerror(ret, errors, 1024);
		printf("avcodec_send_frame fail, [%d]%s\n", ret, errors);
	}
	//从编码器获取编码后数据
	while (ret >= 0) {
		//获取编码后的音频数据,如果成功,需要重复获取,直到失败为止
		ret = avcodec_receive_packet(c_ctx, newpkt);//newpkt.data即为编码后数据

		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		{
			printf("read one finish\n");
			break;
		}
		else if (ret < 0)//这个错误已经无法处理了,很严重,代码解决不了,就退出
		{
			printf("Error, encoding audio frame\n");
			exit(-1);
		}

		//打印
		char ss[300] = { 0 };
		sprintf(ss, "encode packet size is %d(%p)\n", newpkt->size, newpkt->data);
		printf(ss);

		//write file
		fwrite(newpkt->data, 1, newpkt->size, outfile);//
		fflush(outfile);
	}

	av_packet_unref(&pkt);
                 }

以下是我的main.cpp内容:

#include "stdafx.h"

extern "C"
{
#include "libavutil/avutil.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"

}


#include <thread>

#define MY_AUDIO_FORMAT_SRC       AV_SAMPLE_FMT_S16    //重采样为不带P的,只用到data[0]
#define MY_SAMPLE_RATE_SRC        44100    
#define MY_CHAANEL_SRC            AV_CH_LAYOUT_STEREO

#define MY_AUDIO_FORMAT_DST       AV_SAMPLE_FMT_S16   
#define MY_SAMPLE_RATE_DST        48000 
#define MY_CHAANEL_DST            AV_CH_LAYOUT_STEREO

int rec_status = 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;

}


AVFormatContext* open_dev() {
	int ret = 0;
	char errors[1024] = { 0 };

	AVFormatContext *fmt_ctx = NULL;
	AVDictionary *options = NULL;

	//[[video device}:[audio device]]  --中文乱码解决--要转成utf8格式,摄像头就是std::wstring(L"video=Integrated Camera"), 
	/*std::string ss = base::SysWideToUTF8(std::wstring(L"audio=麦克风阵列 (Realtek(R) Audio)"));
	const char *devicename = ss.c_str();*/

	char *devicename = dup_wchar_to_utf8(L"audio=麦克风阵列 (Realtek(R) Audio)");

	//char *devicename = "audio=麦克风阵列 (Realtek(R) Audio)";//中文乱码,错误

	//get format
	AVInputFormat *iformat = av_find_input_format("dshow");
	if ((ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options)) < 0) {
		av_strerror(ret, errors, 1024);
		printf("Failed to open audio device,[%d]%s\n", ret, errors);
		return NULL;
	}

	return fmt_ctx;
}

AVCodecContext* open_coder() {
	//打开编码器 --//这里调试发现sample_fmt是AV_SAMPLE_FMT_S16,先用这个吧,aac默认是AV_SAMPLE_FMT_FLTP,带p的,处理不一样,先不管了,过了教程再说
	//AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_OPUS);
	//AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");//官网下的库不支持这个
	AVCodec *codec = avcodec_find_encoder_by_name("libopus");//官网下的库不支持这个

	//创建codec上下文
	AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);

	codec_ctx->sample_fmt = MY_AUDIO_FORMAT_DST;//
	codec_ctx->channel_layout = MY_CHAANEL_DST;
	codec_ctx->channels = 2;
	codec_ctx->sample_rate = MY_SAMPLE_RATE_DST;//opus不能填44100了,https://blog.csdn.net/dj0379/article/details/53103154
	codec_ctx->bit_rate = 128000;//AAC_LC: 128K, AAC HE: 64K, AAC HE V2: 32K
	//codec_ctx->profile = FF_PROFILE_AAC_HE_V2;//阅读 ffmpeg 代码

	int ret = 0;
	char errors[1024] = { 0 };
	if ((ret = avcodec_open2(codec_ctx, codec, NULL)) < 0)
	{
		av_strerror(ret, errors, 1024);
		printf("Failed to open audio device,[%d]%s\n", ret, errors);
		return NULL;
	}

	return codec_ctx;
}

AVFrame* create_frame() {
	AVFrame *frame = NULL;

	//分配AVFrame和其中的buf
	frame = av_frame_alloc();
	if (!frame)
	{
		goto __ERROR;
	}
	frame->nb_samples = 22050;
	frame->format = MY_AUDIO_FORMAT_DST;
	frame->channel_layout = MY_CHAANEL_DST;
	frame->channels = 2;
	frame->sample_rate = MY_SAMPLE_RATE_DST;

	av_frame_get_buffer(frame, 0);//重要
	if (!frame->data[0])
	{
		printf("av_frame_get_buffer fail\n");
		goto __ERROR;
	}

	return frame;

__ERROR:
	if (frame) {
		av_frame_free(&frame);
	}

	return NULL;

}

void encode(AVCodecContext *ctx,
	AVFrame *frame,
	AVPacket *pkt,
	FILE *output) {

	int ret = 0;
	char errors[1024] = { 0 };

	//将数据送编码器
	ret = avcodec_send_frame(ctx, frame);
	if (ret < 0)
	{
		av_strerror(ret, errors, 1024);
		printf("avcodec_send_frame fail, [%d]%s\n", ret, errors);
	}

	//从编码器获取编码后数据
	while (ret >= 0) {
		//获取编码后的音频数据,如果成功,需要重复获取,直到失败为止
		ret = avcodec_receive_packet(ctx, pkt);

		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		{
			return;
		}
		else if (ret < 0)//这个错误已经无法处理了,很严重,代码解决不了,就退出
		{
			printf("Error, encoding audio frame\n");
			exit(-1);
		}

		//write file
		fwrite(pkt->data, 1, pkt->size, output);
		fflush(output);
	}

}

void read_data_and_encode(AVFormatContext *fmt_ctx,
	AVCodecContext *c_ctx,
	SwrContext* swr_ctx,
	FILE *outfile) {
	int ret = 0;
	int errcount = 0;
	char errors[1024] = { 0 };

	//pakcet
	AVPacket pkt;
	AVFrame *frame = NULL;
	AVPacket *newpkt = NULL;

	//重采样使用
	uint8_t **src_data = NULL;
	int src_linesize = 0;

	uint8_t **dst_data = NULL;
	int dst_linesize = 0;

	//本机采集的一帧数据大小是88200(就是av_read_frame那里的),88200/2/2=22050,一个通道的采样数
	av_samples_alloc_array_and_samples(&src_data, &src_linesize, 2, 22050, MY_AUDIO_FORMAT_SRC, 0);
	av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, 2, 22050, MY_AUDIO_FORMAT_DST, 0);

	frame = create_frame();
	if (!frame)
	{
		printf("create_frame fail");
		goto __ERROR;
	}

	newpkt = av_packet_alloc();
	if (!newpkt)
	{
		printf("av_packet_alloc fail");
		goto __ERROR;
	}

	//read data from device
	while (rec_status) {
		//读取
		ret = av_read_frame(fmt_ctx, &pkt);

		printf("packet size is %d(%p)\n", pkt.size, pkt.data);//pkt.size是88200

		//重采样
		memcpy((void*)src_data[0], (void*)pkt.data, pkt.size);
		swr_convert(swr_ctx, dst_data, 22050, (const uint8_t**)src_data, 22050);

		//写到文件测试源流是否正常用
		//fwrite(dst_data, 1, dst_linesize, outfile);//
		//fflush(outfile);

		//放到AVFrame里
		memcpy((void*)frame->data[0], (void*)dst_data[0], dst_linesize);

#if 0   //这两个一样的
		//encode(c_ctx, frame, newpkt, outfile);//编码 --为了看的方便,直接把函数里内容拿出来在这里写在下面算了
#else
		//编码流程
		//将数据送编码器
		ret = avcodec_send_frame(c_ctx, frame);
		if (ret < 0)
		{
			av_strerror(ret, errors, 1024);
			printf("avcodec_send_frame fail, [%d]%s\n", ret, errors);
		}


		//从编码器获取编码后数据
		while (ret >= 0) {
			//获取编码后的音频数据,如果成功,需要重复获取,直到失败为止
			ret = avcodec_receive_packet(c_ctx, newpkt);//newpkt.data即为编码后数据

			if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			{
				printf("read one finish\n");
				break;
			}
			else if (ret < 0)//这个错误已经无法处理了,很严重,代码解决不了,就退出
			{
				printf("Error, encoding audio frame\n");
				exit(-1);
			}

			//打印
			char ss[300] = { 0 };
			sprintf(ss, "encode packet size is %d(%p)\n", newpkt->size, newpkt->data);
			printf(ss);

			//write file
			fwrite(newpkt->data, 1, newpkt->size, outfile);//
			fflush(outfile);
		}
#endif

		av_packet_unref(&pkt);
	}

	//强制把编码器缓冲区中的数据编码输出,方法是avcodec_send_frame发送一个NULL
#if 0   //这两个一样的
	encode(c_ctx, NULL, newpkt, outfile);//编码 --为了看的方便,直接把函数里内容拿出来在这里写在下面算了
#else
	////编码流程
	////将数据送编码器
	//ret = avcodec_send_frame(c_ctx, NULL);

	////从编码器获取编码后数据
	//while (ret >= 0) {
	//	//获取编码后的音频数据,如果成功,需要重复获取,直到失败为止
	//	ret = avcodec_receive_packet(c_ctx, newpkt);//newpkt.data即为编码后数据

	//	if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
	//	{
	//		break;
	//	}
	//	else if (ret < 0)//这个错误已经无法处理了,很严重,代码解决不了,就退出
	//	{
	//		printf("Error, encoding audio frame\n");
	//		exit(-1);
	//	}

	//	//write file
	//	fwrite(newpkt->data, 1, newpkt->size, outfile);//
	//	fflush(outfile);
	//}
#endif

__ERROR:
	if (frame)
	{
		av_frame_free(&frame);
	}

	if (newpkt)
	{
		av_packet_free(&newpkt);
	}

	//释放
	if (src_data)
	{
		av_free(src_data[0]);
		av_free(src_data);
	}


	if (dst_data)
	{
		av_free(dst_data[0]);
		av_free(dst_data);
	}
}

//音频采集、编码、保存到文件
void rec_audio_resample_encode() {
	int ret = 0;
	char errors[1024] = { 0 };

	//ctx
	AVFormatContext *fmt_ctx = NULL;
	AVCodecContext *c_ctx = NULL;
	SwrContext* swr_ctx = NULL;
	FILE *outfile = NULL;


	//set log level
	av_log_set_level(AV_LOG_DEBUG);

	//start record
	rec_status = 1;

	fmt_ctx = open_dev();
	if (!fmt_ctx)
	{
		printf("open_dev fail\n");
		goto __ERROR;
	}

	//音频重采样
	swr_ctx = init_swr();
	if (!swr_ctx)
	{
		printf("init_swr fail\n");
		goto __ERROR;
	}


	c_ctx = open_coder();
	if (!c_ctx)
	{
		printf("open_coder fail\n");
		goto __ERROR;
	}


	//create file
	//char *out = "test.pcm";
	//char *out = "test.aac";
	char *out = "test3.opus";
	outfile = fopen(out, "wb+");
	if (!outfile)
	{
		printf("open file fail\n");
		goto __ERROR;
	}

	read_data_and_encode(fmt_ctx, c_ctx, swr_ctx, outfile);

	printf("finish");

__ERROR:

	//close file
	if (outfile)
	{
		fclose(outfile);
	}

	if (fmt_ctx)
	{
		avformat_close_input(&fmt_ctx);
	}

	if (swr_ctx)
	{
		swr_free(&swr_ctx);
	}

	if (c_ctx)
	{
		avcodec_free_context(&c_ctx);
	}

}


int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);


	::AllocConsole();
	FILE *file = NULL;
	freopen_s(&file, "conout$", "w", stdout);
	freopen_s(&file, "conout$", "w", stderr);

#if 0
#if CONFIG_AVDEVICE
	avdevice_register_all();
#endif
	avformat_network_init();

	std::string ss = avcodec_configuration();
#endif

#if 0
	std::thread([]() {
		Sleep(10000);
		rec_status = 0;
	}).detach();
#endif

	//register audio device
	avdevice_register_all();

	//采集/重采样/编码
	rec_audio_resample_encode();


	::FreeConsole();

	system("pause");
	return 0;
}

正在回答 回答被采纳积分+3

插入代码

1回答

李超 2020-04-27 17:27:35

opus 用ID 的方式查找编码器试试

0 回复 有任何疑惑可以回复我~
  • 提问者 慕神8200934 #1
    编码器用名称或者ID都能找到,没问题,出错的是调用avcodec_send_frame这个函数,提示无效参数
    回复 有任何疑惑可以回复我~ 2020-04-27 18:00:00
  • 李超 #2
    到QQ群里问吧,有些参数我要了解一下
    回复 有任何疑惑可以回复我~ 2020-04-27 18:24:30
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信