使用FFMPEG进行麦克风录音编码。
但是执行avcodec_send_frame报错AVERROR(EINVAL)
感觉是编码器参数和frame参数不匹配导致的。
有没有哪位熟悉ffmpeg的专家给看看,是哪的错误。
编码器设置为
c_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; //AV_SAMPLE_FMT_S16,AV_SAMPLE_FMT_FLTP;
c_ctx->channel_layout = AV_CH_LAYOUT_MONO; // 输入音频的CHANNEL LAYOUT
c_ctx->channels = 1; // 输入音频的声道数
c_ctx->sample_rate = 44100; // 输入音频的采样率
c_ctx->bit_rate = 16000; // AAC : 128K AAV_HE: 64K AAC_HE_V2: 32K. bit_rate为0时才会查找profile属性值
c_ctx->profile = FF_PROFILE_AAC_LOW;
frame设置为
frame = av_frame_alloc();
frame->nb_samples = 1024;
frame->format=AV_SAMPLE_FMT_FLTP;
frame->channel_layout=AV_CH_LAYOUT_MONO;
完整代码如下:
#include "audiodecode.h"
#include
#include
// init context
static AVFormatContext *fmt_ctx = nullptr;
static AVPacket ptk;
static int pktPos = 0;
static AVFrame* frame =nullptr;
static AVPacket *newpkt = nullptr;
static int swr_num = 0;
static uint8_t bufferData[2048];
static AVCodecContext* c_ctx = nullptr;
AudioDecode::AudioDecode( QObject* parent ):QObject(parent)
{
}
bool AudioDecode::init(){
// register device
avdevice_register_all();
// set logger level
av_log_set_level(AV_LOG_DEBUG);
// get input device format
AVInputFormat *iformat = av_find_input_format("dshow");
if( !iformat ){
emit error("get input format fail");
return false;
}
// 输入设备
QString deviceName = QString::fromLocal8Bit("audio=麦克风 (High Definition Audio Device)");
AVDictionary *options = nullptr;
// open audio device
int r = avformat_open_input(&fmt_ctx, deviceName.toStdString().c_str(), iformat, &options);
if (r < 0){
emit error(QString("Failed to open audio device! ret=%1").arg(QString::number(r)));
return false;
}
av_init_packet(&ptk);
const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if( codec==NULL ){
emit error("faild to open encoder");
return false;
}
c_ctx = avcodec_alloc_context3(codec);
c_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; //AV_SAMPLE_FMT_S16,AV_SAMPLE_FMT_FLTP;
c_ctx->channel_layout = AV_CH_LAYOUT_MONO; // 输入音频的CHANNEL LAYOUT
c_ctx->channels = 1; // 输入音频的声道数
c_ctx->sample_rate = 44100; // 输入音频的采样率
c_ctx->bit_rate = 16000; // AAC : 128K AAV_HE: 64K AAC_HE_V2: 32K. bit_rate为0时才会查找profile属性值
c_ctx->profile = FF_PROFILE_AAC_LOW;
// 打开编码器
if (avcodec_open2(c_ctx, codec, NULL) < 0){
emit error("failed to open aac");
return false;
}
frame = av_frame_alloc();
frame->nb_samples = 1024;
frame->format=AV_SAMPLE_FMT_FLTP;
frame->channel_layout=AV_CH_LAYOUT_MONO;
av_frame_get_buffer(frame,0);
newpkt = av_packet_alloc();//分配编码后的数据空间
return true;
}
AudioDecode::~AudioDecode(){
if( frame )
av_frame_free(&frame);
if( newpkt )
av_packet_free(&newpkt);
if( c_ctx )
av_free(c_ctx);
if( fmt_ctx )
avformat_close_input(&fmt_ctx);
}
AVPacket* AudioDecode::getInput(){
// 从编码器直接取包
int ret = avcodec_receive_packet(c_ctx,newpkt);
if( ret==0 ){
emit debug(QString("direct audio packet ").append(QString::number(newpkt->size)));
return newpkt;
}
while(true){
// 没有获取过输入包
if( ptk.size==0 ){
ret = av_read_frame(fmt_ctx,&ptk);
if( ret<0 ){
emit debug("No new frame");
return NULL;
}
pktPos = 0;
}
// 输入包的数据已经编码
if( pktPos==ptk.size ){
av_packet_unref(&ptk);
ret = av_read_frame(fmt_ctx,&ptk);
if( ret<0 ){
emit debug("No new frame");
return NULL;
}
pktPos = 0;
}
// 拷贝出帧数据到编码缓冲区
while( swr_num!=2048 && pktPos!=ptk.size ){
bufferData[swr_num] = ptk.data[pktPos];
++swr_num;
++pktPos;
}
// 编码缓冲区已满
if( swr_num==2048 ){
swr_num = 0;
memcpy((void *)frame->data[0], bufferData, 2048);
ret = avcodec_send_frame(c_ctx,frame);
if( ret<0 ){
switch( ret ){
case AVERROR(EAGAIN):
emit warn(QString("avcodec_send_frame [input is not accepted in the current state - user must read output with avcodec_receive_packet() (once all output is read, the packet should be resent, and the call will not fail with EAGAIN).]"));
break;
case AVERROR_EOF:
emit warn(QString("avcodec_send_frame [the encoder has been flushed, and no new frames can be sent to it]"));
break;
case AVERROR(EINVAL):
emit warn(QString("avcodec_send_frame [codec not opened, refcounted_frames not set, it is a decoder, or requires flush]"));
break;
case AVERROR(ENOMEM):
emit warn(QString("avcodec_send_frame [failed to add packet to internal queue, or similar]"));
break;
default:
emit warn(QString("avcodec_send_frame %1").arg(QString::number(ret)));
}
return NULL;
}
ret = avcodec_receive_packet(c_ctx,newpkt);
if( ret<0 ){
emit debug(QString("avcodec_receive_packet %1").arg(QString::number(ret)));
return NULL;
}
emit debug(QString("new audio packet ").append(QString::number(newpkt->size)));
return newpkt;
}
}
}
参考GPT和自己的思路,在使用FFmpeg进行麦克风录音编码时,当执行 avcodec_send_frame 函数时报错 AVERROR(EINVAL)。从您的代码中可以看出,您使用了 AAC 编码器进行编码,并且配置了音频参数和编码器属性。但是,代码中有几个问题可能导致这个错误。
首先,您没有给帧分配缓冲区。您应该在分配帧之后调用 av_frame_get_buffer 函数为帧分配内存。例如:
frame = av_frame_alloc();
frame->nb_samples = 1024;
frame->format=AV_SAMPLE_FMT_FLTP;
frame->channel_layout=AV_CH_LAYOUT_MONO;
av_frame_get_buffer(frame,0);
接下来,您的编码缓冲区大小为 2048 字节,这似乎是一个不太典型的大小。如果您使用了不正确的缓冲区大小,也可能导致 AVERROR(EINVAL) 错误。建议您将缓冲区大小更改为采样率(例如 44100)的倍数,以确保缓冲区的大小与音频数据相匹配。
最后,我注意到您使用的是 avcodec_receive_packet 函数直接从编码器中获取输出包,这可能会导致无法正常编码音频。在大多数情况下,您应该按照以下方式编码音频:
1 使用 av_read_frame 函数从输入设备中获取音频数据。
2 将音频数据放入帧中,使用 avcodec_send_frame 函数将帧发送到编码器。
4 通过多次调用 avcodec_receive_packet 函数来获取编码后的输出包。
您需要使用一个循环来重复执行第 1 步和第 3 步,直到结束录音。在这个过程中,您可以使用信号槽机制在主线程和录音线程之间传递数据。例如,当录音线程获得一个新的输出包时,它可以通过信号发射该包,主线程则通过槽函数接收该包。
这里是一个可能有用的代码片段,它展示了如何从输入设备中获取音频数据,将其放入帧中并发送到编码器:
AVPacket pkt;
av_init_packet(&pkt);
while (true) {
// read audio frame
int ret = av_read_frame(fmt_ctx, &pkt);
if (ret < 0) {
// end of stream
break;
}
// allocate new frame
AVFrame *frame = av_frame_alloc();
frame->format = c_ctx->sample_fmt;
frame->sample_rate = c_ctx->sample_rate;
frame->channel_layout = c_ctx->channel_layout;
frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);
frame->nb_samples = pkt.size / av_get_bytes_per_sample(c_ctx->sample_fmt) / frame->channels;
// allocate buffer for frame
av_frame_get_buffer(frame, 0);
// copy audio data to frame
int num_samples = av 继续
// copy audio data to frame
int num_samples = av_get_channel_layout_nb_channels(c_ctx->channel_layout) * frame->nb_samples;
int ret = av_frame_make_writable(frame);
if (ret < 0){
emit error(QString("failed to make frame writable! ret=%1").arg(QString::number(ret)));
return NULL;
}
if (swr_num >= num_samples * av_get_bytes_per_sample(c_ctx->sample_fmt)) {
swr_num = 0;
ret = av_samples_fill_arrays(frame->data, frame->linesize, bufferData, 1, frame->nb_samples, c_ctx->sample_fmt, 0);
if (ret < 0){
emit error(QString("failed to fill audio samples! ret=%1").arg(QString::number(ret)));
return NULL;
}
// set audio frame properties
frame->pts = av_rescale_q(av_gettime(), { 1, AV_TIME_BASE }, c_ctx->time_base);
frame->pict_type = AV_PICTURE_TYPE_NONE;
frame->sample_rate = c_ctx->sample_rate;
// send audio frame to encoder
ret = avcodec_send_frame(c_ctx, frame);
if (ret == AVERROR(EINVAL)) {
emit error(QString("Error sending audio frame to encoder! ret=%1").arg(QString::number(ret)));
return NULL;
} else if (ret == AVERROR_EOF) {
emit error(QString("Error sending audio frame to encoder! ret=%1").arg(QString::number(ret)));
return NULL;
}
}
// no packet output
return NULL;
}
如果对您有帮助,请给与采纳,谢谢。
根据代码和错误信息看,是avcodec_send_frame这一行出现了错误,错误码是EINVAL,表示传入了无效的参数。
在代码中可以看到,调用该函数时传入的是frame,即音频帧,而这个音频帧的格式是FLTP,采样率是44100Hz,声道布局是单声道,这些参数也与编码器设置中的参数匹配,应该是没有问题的。
因此,可以猜测出现错误的可能原因是编码器未正确打开。在代码中可以看到,虽然编码器打开了,但并没有检查返回值。如果打开失败,后续操作就会出错。
可以在打开编码器时添加一些检查,例如:
if (avcodec_open2(c_ctx, codec, NULL) < 0){
emit error("failed to open aac");
return false;
}
改为:
int ret = avcodec_open2(c_ctx, codec, NULL);
if (ret < 0){
emit error(QString("failed to open aac, ret=%1").arg(ret));
return false;
}
这样就可以检查编码器是否正确打开。如果出现错误,也可以通过错误码更好地定位问题。
根据你提供的代码,可以看出可能是因为参数设置不一致,导致avcodec_send_frame函数返回AVERROR(EINVAL)错误。你可以根据错误代码EINVAL的含义(Invalid argument)来排查,看看是哪一个参数设置不正确。检查编码器的输入参数,特别是AVCodecContext中的sample_fmt、channel_layout、channels、sample_rate等参数,是否和实际的音频数据一致。检查avcodec_send_frame函数的返回值,如果返回错误代码,可以通过av_strerror函数将错误代码转换为错误信息,更加方便地排查问题
您好,关于您提出的问题,为您推荐以下解决方法:
1、原因分析:
我们通过分析avcodec_send_frame方法的源码,可以看到: