采集音频并编码教程中,编码用的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;
}