FFMPEG麦克风编码错误

使用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;
}

如果对您有帮助,请给与采纳,谢谢。

以下答案基于ChatGPT与GISer Liu编写:

  • 根据代码和错误信息看,是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方法的源码,可以看到:

img


其中,在源码中的第6行,可以看出,该函数会判断编码器有没有打开,判断AVCodecContext是否为编码器,如果这两个的判断结果只要有1个不通过,就返回错误:AVERROR(EINVAL)。
2、因此,解决方法是:
1)检查代码中是否有调用avcodec_open2打开编码器或者是否打开成功。
2)检查avcodec_find_encoder是否调用正确;
3)j检查与编码相关的配置参数是否正确